// src/client/features/tasks/model/useTaskDrag.ts
// Composable for drag-and-drop logic in TaskCard component
//
// KEY FIXES vs original:
// 1. Uses global useDragState() — no more relying on broken dataTransfer.getData() in dragover
// 2. Zoning: 30/40/30 instead of 25/50/25 — easier to hit before/after
// 3. "after" a level-1 task at level 1 skips past its children (no more inserting in the middle)
// 4. dragleave does NOT clear placeholder (prevents flicker)
// 5. Explicit parentId stored in placeholder — no ambiguous recalculation on drop
// 6. Throttled dragover to reduce jitter

import { ref, type Ref } from 'vue';
import { useTaskStore } from '@client/entities/task';
import type { Task } from '@shared/contracts/tasks';
import { useDragState } from './useDragState';

export const INDENT_SIZE = 32;
const INDENT_THRESHOLD = 40;
const OUTDENT_THRESHOLD = 32;
const DRAG_OVER_THROTTLE_MS = 30;

type DropPosition = 'before' | 'after' | 'child';

export interface DragPlaceholder {
  index: number;       // position in allTasksFlat where the blue line appears
  level: number;       // 1 or 2
  visible: boolean;
  draggedTaskId: string;
  parentId: string;    // the parentId the dragged task will get on drop
}

export interface ReorderData {
  id: string;
  order: number;
  parentId: string | null;
}

export function useTaskDrag(
  rootTask: Ref<Task>,
  allTasksFlat: Ref<Task[]>,
) {
  const taskStore = useTaskStore();
  const { draggedTaskId: globalDraggedId } = useDragState();

  const dragPlaceholder = ref<DragPlaceholder | null>(null);
  let lastDragOverTs = 0;

  // ── helpers ──────────────────────────────────────────────

  const isDescendantOf = (possibleDescendant: string, possibleAncestor: string): boolean => {
    const visited = new Set<string>();
    let cur = possibleDescendant;
    while (cur) {
      if (visited.has(cur)) return false;
      visited.add(cur);
      const t = allTasksFlat.value.find(x => x.id === cur);
      if (!t?.parentId) return false;
      if (t.parentId === possibleAncestor) return true;
      cur = t.parentId;
    }
    return false;
  };

  const taskHasChildren = (taskId: string) =>
    allTasksFlat.value.some(t => t.parentId === taskId);

  const taskLevel = (task: Task) =>
    task.parentId === rootTask.value.id ? 1 : 2;

  // Find the closest level-1 task above `idx` in `list`
  const findLevel1Above = (list: Task[], idx: number): string | null => {
    for (let i = idx - 1; i >= 0; i--) {
      if (list[i].parentId === rootTask.value.id) return list[i].id;
    }
    return null;
  };

  // ── dragover handler ────────────────────────────────────

  const handleDragOver = (
    draggedTaskId: string,
    targetTaskId: string,
    clientX: number,
    clientY: number,
  ) => {
    // Throttle
    const now = Date.now();
    if (now - lastDragOverTs < DRAG_OVER_THROTTLE_MS) return;
    lastDragOverTs = now;

    if (!draggedTaskId || draggedTaskId === targetTaskId) return;

    const targetEl = document.querySelector(`[data-id="${targetTaskId}"]`) as HTMLElement | null;
    if (!targetEl) return;

    const rect = targetEl.getBoundingClientRect();
    const dy = clientY - rect.top;
    const h = rect.height;

    // ── 1. Vertical zone: 30 / 40 / 30 ──
    let pos: DropPosition;
    if (dy < h * 0.30) pos = 'before';
    else if (dy > h * 0.70) pos = 'after';
    else pos = 'child';

    // ── 2. Resolve tasks ──
    const target = allTasksFlat.value.find(t => t.id === targetTaskId);
    const dragged = allTasksFlat.value.find(t => t.id === draggedTaskId);
    if (!target || !dragged) return;

    const tgtLvl = taskLevel(target);
    const dragHasKids = taskHasChildren(draggedTaskId);

    // Horizontal analysis — use the CARD BODY's left edge, not the target item's!
    // For nested items, rect.left is shifted right by ~32px, which makes it nearly
    // impossible to "outdent" by moving the cursor left — the threshold is already
    // inside the indented area. Using the card body as reference makes the gesture
    // consistent: left side of card = level 1, right side = level 2.
    const bodyEl = targetEl.closest('.task-card__body') as HTMLElement | null;
    const refLeft = bodyEl ? bodyEl.getBoundingClientRect().left : rect.left;
    const isLeft = clientX < refLeft + OUTDENT_THRESHOLD;
    const isRight = clientX > refLeft + INDENT_THRESHOLD;

    // ── 3. Determine level + parentId ──
    let level: number;
    let parentId: string;

    if (pos === 'child') {
      if (tgtLvl === 2 || dragHasKids) {
        // Can't nest: fallback to "after" at same level
        pos = 'after';
        if (dragHasKids) {
          level = 1;
          parentId = rootTask.value.id;
        } else {
          level = tgtLvl;
          parentId = tgtLvl === 2 ? target.parentId! : rootTask.value.id;
        }
      } else {
        // Nest under level-1 target
        level = 2;
        parentId = target.id;
      }
    } else {
      // before / after
      if (tgtLvl === 2) {
        if (isLeft) {
          // Outdent to level 1
          level = 1;
          parentId = rootTask.value.id;
        } else {
          // Stay at level 2, same parent as target
          level = 2;
          parentId = target.parentId!;
        }
      } else {
        // Target is level 1
        if (isRight && !dragHasKids) {
          // Indent to level 2
          if (pos === 'after') {
            level = 2;
            parentId = target.id;
          } else {
            // 'before' + indent: attach to the level-1 task above
            const idx = allTasksFlat.value.indexOf(target);
            const above = findLevel1Above(allTasksFlat.value, idx);
            if (above) {
              level = 2;
              parentId = above;
            } else {
              level = 1;
              parentId = rootTask.value.id;
            }
          }
        } else {
          level = 1;
          parentId = rootTask.value.id;
        }
      }

      // Safety: task with children must stay level 1
      if (dragHasKids && parentId !== rootTask.value.id) {
        level = 1;
        parentId = rootTask.value.id;
      }
    }

    // ── 4. Prevent circular nesting ──
    if (isDescendantOf(parentId, draggedTaskId) || parentId === draggedTaskId) {
      return; // keep current placeholder
    }

    // ── 5. Calculate placeholder index ──
    const tgtIdx = allTasksFlat.value.findIndex(t => t.id === targetTaskId);
    let phIdx: number;

    if (pos === 'before') {
      phIdx = tgtIdx;
    } else {
      // after / child → place after target
      phIdx = tgtIdx + 1;

      // If "after" a level-1 target and we stay at level 1,
      // skip past all children of that target
      if (pos === 'after' && tgtLvl === 1 && level === 1) {
        while (
          phIdx < allTasksFlat.value.length &&
          allTasksFlat.value[phIdx].parentId === target.id
        ) {
          phIdx++;
        }
      }
    }

    // ── 6. Update placeholder ──
    dragPlaceholder.value = {
      index: phIdx,
      level,
      visible: true,
      draggedTaskId,
      parentId,
    };
  };

  // ── dragleave: intentionally a no-op ─────────────────────
  const handleDragLeave = (_draggedTaskId: string) => {
    // no-op — prevents flicker from child DOM node enter/leave
  };

  // ── dragend: cleanup ─────────────────────────────────────
  const handleDragEnd = () => {
    dragPlaceholder.value = null;
  };

  // ── drop / reorder ──────────────────────────────────────

  const handleReorder = async (
    draggedTaskId: string,
    _targetTaskId: string,
    _clientX: number,
    _clientY: number,
  ) => {
    try {
      const ph = dragPlaceholder.value;
      if (!ph || ph.draggedTaskId !== draggedTaskId) return;

      const dragged = allTasksFlat.value.find(t => t.id === draggedTaskId);
      if (!dragged) return;

      // Build new list without the dragged task
      const newList = allTasksFlat.value.filter(t => t.id !== draggedTaskId);

      // Adjust insert index for the removal of the dragged task
      const origIdx = allTasksFlat.value.findIndex(t => t.id === draggedTaskId);
      let insertIdx = ph.index;
      if (origIdx !== -1 && origIdx < ph.index) {
        insertIdx--;
      }
      insertIdx = Math.max(0, Math.min(insertIdx, newList.length));

      // parentId comes straight from the placeholder (already validated)
      let newParentId: string | null = ph.parentId;

      // Double-check: if task has children, force level 1
      if (taskHasChildren(draggedTaskId) && newParentId !== rootTask.value.id) {
        newParentId = rootTask.value.id;
      }

      // Insert
      newList.splice(insertIdx, 0, { ...dragged, parentId: newParentId });

      // Linear order for the entire flat list
      const reorderData: ReorderData[] = newList.map((t, i) => ({
        id: t.id,
        order: i,
        parentId: t.id === draggedTaskId ? newParentId : t.parentId,
      }));

      // Optimistic update
      dragged.parentId = newParentId;

      await taskStore.reorderTasks({ rootId: rootTask.value.id, tasks: reorderData });
      await taskStore.fetchTasks();
    } finally {
      dragPlaceholder.value = null;
    }
  };

  const handleReorderFromPlaceholder = () => {
    const ph = dragPlaceholder.value;
    if (!ph) return;
    handleReorder(ph.draggedTaskId, '', 0, 0);
  };

  return {
    dragPlaceholder,
    handleDragOver,
    handleDragLeave,
    handleDragEnd,
    handleReorder,
    handleReorderFromPlaceholder,
  };
}