import { onBeforeUnmount, ref } from "vue";
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
import {
  draggable,
  dropTargetForElements
} from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview";
import {
  attachInstruction,
  extractInstruction
} from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item";
export function applyReorder(rows, sourceIndex, targetIndex, position) {
  if (sourceIndex === targetIndex) return rows;
  if (sourceIndex < 0 || sourceIndex >= rows.length) return rows;
  if (targetIndex < 0 || targetIndex >= rows.length) return rows;
  let insertIdx = targetIndex + (position === "reorder-below" ? 1 : 0);
  if (sourceIndex < targetIndex) insertIdx -= 1;
  if (insertIdx === sourceIndex) return rows;
  const next = rows.slice();
  const [moved] = next.splice(sourceIndex, 1);
  next.splice(insertIdx, 0, moved);
  return next;
}
const SOURCE_KIND = "shwfed-table-row";
export function useRowReorder(options) {
  const instruction = ref(null);
  const regs = /* @__PURE__ */ new Map();
  function rewire(rowId, reg) {
    reg.cleanup?.();
    if (!reg.rowEl || !reg.handleEl) {
      reg.cleanup = null;
      return;
    }
    const rowEl = reg.rowEl;
    const handleEl = reg.handleEl;
    reg.cleanup = combine(
      draggable({
        element: rowEl,
        dragHandle: handleEl,
        getInitialData: () => ({ kind: SOURCE_KIND, id: rowId }),
        canDrag: () => options.isEnabled(),
        // The row is full-table-width and most of its content trails off-
        // screen on wide tables, so the default native preview (a snapshot
        // of `element`) is a giant useless rectangle. We render a clone
        // capped at ~360px and fade it out to the right via a CSS mask so
        // the user sees just enough of the row's leading cells to identify
        // it, with the trailing content gracefully dissolving.
        onGenerateDragPreview: ({ nativeSetDragImage, location, source }) => {
          setCustomNativeDragPreview({
            nativeSetDragImage,
            // Anchor the preview to the grip's rect, not the row's. The row is
            // virtualized (`position: absolute` + `translate3d`) so its
            // bounding box shifts with vertical scroll and `preserveOffsetOnSource`
            // pulls the preview off the pointer once the table has been
            // scrolled. The grip is always rendered exactly where the user
            // clicked AND sits at the row's leading edge, so offsetting from
            // it keeps the start of the line pinned under the cursor in
            // every scroll state.
            getOffset: ({ container }) => {
              const containerRect = container.getBoundingClientRect();
              const handleRect = handleEl.getBoundingClientRect();
              return {
                x: Math.max(0, Math.min(location.current.input.clientX - handleRect.left, containerRect.width)),
                y: Math.max(0, Math.min(location.current.input.clientY - handleRect.top, containerRect.height))
              };
            },
            render: ({ container }) => {
              const src = source.element;
              const rect = src.getBoundingClientRect();
              const clone = src.cloneNode(true);
              clone.style.transform = "none";
              clone.style.position = "relative";
              clone.style.top = "0";
              clone.style.left = "0";
              clone.style.height = `${rect.height}px`;
              clone.style.width = `${rect.width}px`;
              clone.style.flexShrink = "0";
              const wrapper = document.createElement("div");
              wrapper.style.width = `${Math.min(rect.width, 360)}px`;
              wrapper.style.maxWidth = "60vw";
              wrapper.style.height = `${rect.height}px`;
              wrapper.style.overflow = "hidden";
              wrapper.style.background = "white";
              wrapper.style.boxShadow = "0 6px 18px rgba(0,0,0,0.12)";
              wrapper.style.borderRadius = "4px";
              const fade = "linear-gradient(to right, rgba(0,0,0,1) 0%, rgba(0,0,0,1) 55%, rgba(0,0,0,0) 100%)";
              wrapper.style.maskImage = fade;
              wrapper.style.webkitMaskImage = fade;
              wrapper.appendChild(clone);
              container.appendChild(wrapper);
            }
          });
        }
      }),
      dropTargetForElements({
        element: reg.rowEl,
        canDrop: ({ source }) => {
          if (!options.isEnabled()) return false;
          return source.data.kind === SOURCE_KIND && source.data.id !== rowId;
        },
        getIsSticky: () => true,
        getData: ({ input, element }) => attachInstruction(
          { kind: `${SOURCE_KIND}-target`, id: rowId },
          {
            input,
            element,
            currentLevel: 0,
            indentPerLevel: 0,
            mode: "standard",
            block: ["make-child", "reparent"]
          }
        ),
        onDrag: ({ self }) => {
          const inst = extractInstruction(self.data);
          if (!inst || inst.type === "instruction-blocked") {
            if (instruction.value?.id === rowId) instruction.value = null;
            return;
          }
          if (inst.type === "reorder-above" || inst.type === "reorder-below") {
            instruction.value = { id: rowId, type: inst.type };
          }
        },
        onDragLeave: () => {
          if (instruction.value?.id === rowId) instruction.value = null;
        },
        onDrop: ({ source, self }) => {
          instruction.value = null;
          const inst = extractInstruction(self.data);
          if (!inst) return;
          if (inst.type !== "reorder-above" && inst.type !== "reorder-below") return;
          const sourceId = typeof source.data.id === "string" ? source.data.id : null;
          if (!sourceId || sourceId === rowId) return;
          options.onReorder(sourceId, rowId, inst.type);
        }
      })
    );
  }
  function getReg(rowId) {
    let reg = regs.get(rowId);
    if (!reg) {
      reg = { rowEl: null, handleEl: null, cleanup: null };
      regs.set(rowId, reg);
    }
    return reg;
  }
  function purgeIfEmpty(rowId, reg) {
    if (!reg.rowEl && !reg.handleEl && !reg.cleanup) regs.delete(rowId);
    if (instruction.value?.id === rowId && !reg.rowEl) instruction.value = null;
  }
  function setRow(rowId, el) {
    const reg = getReg(rowId);
    if (reg.rowEl === el) return;
    reg.rowEl = el;
    rewire(rowId, reg);
    purgeIfEmpty(rowId, reg);
  }
  function setHandle(rowId, el) {
    const reg = getReg(rowId);
    if (reg.handleEl === el) return;
    reg.handleEl = el;
    rewire(rowId, reg);
    purgeIfEmpty(rowId, reg);
  }
  function instructionFor(rowId) {
    return instruction.value?.id === rowId ? instruction.value.type : null;
  }
  onBeforeUnmount(() => {
    for (const reg of regs.values()) reg.cleanup?.();
    regs.clear();
    instruction.value = null;
  });
  return { setRow, setHandle, instructionFor };
}
