<script setup>
import { watch, onMounted, onUnmounted, computed, inject } from "vue";
import { SELECTION_CONTEXT_KEY } from "../types";
import { createFreeformContext } from "../composables/useFreeform";
import { useExternalDrag } from "../composables/useExternalDrag";
import { useExternalDrop } from "../composables/useExternalDrop";
const props = defineProps({
  modelValue: { type: Array, required: true },
  disabled: { type: Boolean, required: false, default: false },
  manualReorder: { type: Boolean, required: false, default: false },
  dropZoneId: { type: String, required: false }
});
const emit = defineEmits(["update:modelValue", "select", "drag-start", "drag-move", "drag-end", "reorder", "drop", "drop-into", "drop-to-zone"]);
const {
  items,
  dragState,
  selectionState,
  disabled: contextDisabled,
  dropIndex,
  dragSourceIndex,
  currentDropTarget,
  containerElement,
  itemElements,
  handlePointerMove,
  handlePointerUp,
  handleExternalDrop,
  updateDropTarget
} = createFreeformContext();
function onScrollDuringDrag() {
  if (dragState.value.thresholdPassed && dragState.value.currentPosition) {
    updateDropTarget(dragState.value.currentPosition, true);
  }
}
watch(() => dragState.value.thresholdPassed, (dragging) => {
  if (dragging) {
    document.addEventListener("scroll", onScrollDuringDrag, true);
  } else {
    document.removeEventListener("scroll", onScrollDuringDrag, true);
  }
});
const { detectExternalZone, finishExternalDrag } = useExternalDrag(props.dropZoneId);
useExternalDrop({ dropZoneId: props.dropZoneId, dragState, handleExternalDrop });
const selectionContext = inject(SELECTION_CONTEXT_KEY, null);
watch(() => props.modelValue, (newItems) => {
  items.value = [...newItems];
}, { immediate: true, deep: true });
watch(() => props.disabled, (val) => {
  contextDisabled.value = val;
}, { immediate: true });
watch(() => selectionState.value.items, (selected) => {
  emit("select", [...selected]);
}, { deep: true });
watch(() => dragState.value.thresholdPassed, (passed) => {
  if (passed) {
    emit("drag-start", [...dragState.value.items]);
  }
});
watch(() => dragState.value.currentPosition, (pos) => {
  if (dragState.value.thresholdPassed && pos) {
    emit("drag-move", [...dragState.value.items], { ...pos });
  }
});
const ghostStyle = computed(() => {
  if (!dragState.value.thresholdPassed || !dragState.value.currentPosition) {
    return { display: "none" };
  }
  return {
    position: "fixed",
    left: `${dragState.value.currentPosition.x}px`,
    top: `${dragState.value.currentPosition.y}px`,
    pointerEvents: "none",
    zIndex: 9999,
    transform: "translate(-50%, -50%)"
  };
});
const isDragging = computed(() => dragState.value.thresholdPassed);
const dragItems = computed(() => dragState.value.items);
const dragPosition = computed(() => dragState.value.currentPosition);
const dragStartPosition = computed(() => dragState.value.startPosition);
const isLassoActive = computed(() => selectionState.value.lassoActive);
function onPointerMove(event) {
  handlePointerMove(event);
  if (dragState.value.thresholdPassed) {
    detectExternalZone({ x: event.clientX, y: event.clientY }, dragState.value.items);
  }
}
function reorderItems(draggedItems, targetIndex) {
  const draggedIds = new Set(draggedItems.map((i) => i.id));
  const itemsToMove = items.value.filter((i) => draggedIds.has(i.id));
  const remaining = items.value.filter((i) => !draggedIds.has(i.id));
  let insertAt = 0;
  for (let i = 0; i < targetIndex && i < items.value.length; i++) {
    if (!draggedIds.has(items.value[i].id)) {
      insertAt++;
    }
  }
  return [
    ...remaining.slice(0, insertAt),
    ...itemsToMove,
    ...remaining.slice(insertAt)
  ];
}
function onPointerUp(event) {
  if (dragState.value.thresholdPassed) {
    const draggedItems = [...dragState.value.items];
    const dropTarget = currentDropTarget.value;
    emit("drag-end", draggedItems);
    const externalDrop = finishExternalDrag(draggedItems);
    if (externalDrop) {
      emit("drop-to-zone", draggedItems, externalDrop.zoneId, externalDrop.dropIndex, externalDrop.containerId);
      emit("drop", {
        items: draggedItems,
        target: null,
        position: dragState.value.currentPosition,
        dropType: externalDrop.containerId ? "container" : "zone",
        targetZoneId: externalDrop.zoneId,
        targetContainerId: externalDrop.containerId
      });
      selectionState.value.items = [];
    } else if (dropTarget?.type === "container") {
      emit("drop-into", draggedItems, dropTarget.item, dropTarget.accepted);
      if (dropTarget.accepted) {
        emit("drop", {
          items: draggedItems,
          target: dropTarget,
          position: dragState.value.currentPosition,
          dropType: "container",
          targetContainer: dropTarget.item
        });
        selectionState.value.items = [];
      }
    } else {
      const to = dropIndex.value;
      if (to !== null && draggedItems.length > 0) {
        if (!props.manualReorder) {
          const newItems = reorderItems(draggedItems, to);
          items.value = newItems;
          emit("update:modelValue", newItems);
        }
        emit("reorder", dragSourceIndex.value ?? 0, to);
        emit("drop", {
          items: draggedItems,
          target: null,
          position: dragState.value.currentPosition,
          dropType: "reorder",
          fromIndex: dragSourceIndex.value ?? 0,
          toIndex: to
        });
      }
    }
  }
  handlePointerUp(event);
}
onMounted(() => {
  document.addEventListener("pointermove", onPointerMove);
  document.addEventListener("pointerup", onPointerUp);
  document.addEventListener("pointercancel", onPointerUp);
  if (selectionContext) {
    selectionContext.registerFreeform({
      items,
      itemElements,
      disabled: contextDisabled,
      selectionState
    });
  }
});
onUnmounted(() => {
  document.removeEventListener("scroll", onScrollDuringDrag, true);
  document.removeEventListener("pointermove", onPointerMove);
  document.removeEventListener("pointerup", onPointerUp);
  document.removeEventListener("pointercancel", onPointerUp);
  if (selectionContext) {
    selectionContext.unregisterFreeform();
  }
});
</script>

<template>
  <div
    ref="containerElement"
    class="freeform-container"
  >
    <slot
      :items="items"
      :selected="selectionState.items"
      :is-dragging="isDragging"
      :drag-items="dragItems"
      :is-lasso-active="isLassoActive"
      :drop-index="dropIndex"
      :drag-source-index="dragSourceIndex"
    />

    <!-- Drag Ghost (user provides via slot) -->
    <Teleport to="body">
      <div
        v-if="isDragging"
        :style="ghostStyle"
      >
        <slot
          name="drag-ghost"
          :items="dragItems"
          :count="dragItems.length"
          :position="dragPosition"
          :start-position="dragStartPosition"
        >
          <!-- Default ghost: looks like item but transparent -->
          <div class="freeform-ghost-default">
            <span class="freeform-ghost-default__label">{{ dragItems[0]?.id }}</span>
            <span
              v-if="dragItems.length > 1"
              class="freeform-ghost-default__badge"
            >
              +{{ dragItems.length - 1 }}
            </span>
          </div>
        </slot>
      </div>
    </Teleport>
  </div>
</template>

<style>
.freeform-container{display:flex;position:relative;--freeform-color-primary:#3b82f6;--freeform-color-primary-light:#dbeafe;--freeform-color-success:#22c55e;--freeform-color-success-light:#dcfce7;--freeform-color-danger:#ef4444;--freeform-color-danger-light:#fee2e2;--freeform-color-neutral:#f3f4f6;--freeform-color-text:#374151}.freeform-ghost-default{align-items:center;backdrop-filter:blur(4px);background:var(--freeform-color-neutral);border-radius:8px;box-shadow:0 10px 40px rgba(0,0,0,.2);display:flex;gap:8px;padding:16px 24px}.freeform-ghost-default__label{color:var(--freeform-color-text);font-weight:500}.freeform-ghost-default__badge{align-items:center;background:var(--freeform-color-primary);border-radius:12px;color:#fff;display:flex;font-size:12px;font-weight:600;height:24px;justify-content:center;min-width:24px;padding:0 8px}
</style>
