/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck

export function moveItemRelative(items, sourceId, targetId, position) {
  const sourceKey = String(sourceId || "").trim();
  const targetKey = String(targetId || "").trim();
  if (!sourceKey || !targetKey || sourceKey === targetKey) {
    return Array.isArray(items) ? items.slice() : [];
  }

  const list = Array.isArray(items) ? items.slice() : [];
  const sourceIndex = list.findIndex((entry) => String(entry) === sourceKey);
  const targetIndex = list.findIndex((entry) => String(entry) === targetKey);
  if (sourceIndex < 0 || targetIndex < 0) {
    return list;
  }

  const [moved] = list.splice(sourceIndex, 1);
  let insertionIndex = targetIndex;
  if (sourceIndex < targetIndex) {
    insertionIndex -= 1;
  }
  if (position === "after") {
    insertionIndex += 1;
  }
  insertionIndex = Math.max(0, Math.min(list.length, insertionIndex));
  list.splice(insertionIndex, 0, moved);
  return list;
}

export function createReorderableListController(config) {
  let draggingId = "";
  let dropTargetId = "";
  let dropPosition = "before";

  function getItems() {
    if (!config.container) {
      return [];
    }
    return Array.from(config.container.querySelectorAll(config.itemSelector || "[data-reorder-item-id]"));
  }

  function resolveItemId(item) {
    if (!item) {
      return "";
    }
    if (typeof config.getItemId === "function") {
      return String(config.getItemId(item) || "").trim();
    }
    return String(item.getAttribute("data-reorder-item-id") || "").trim();
  }

  function canDrag(itemId) {
    return typeof config.canDragItem === "function" ? config.canDragItem(itemId) !== false : true;
  }

  function canDrop(itemId) {
    return typeof config.canDropOnItem === "function" ? config.canDropOnItem(itemId) !== false : true;
  }

  function syncClasses() {
    getItems().forEach((item) => {
      const itemId = resolveItemId(item);
      item.classList.toggle("reorder-dragging", !!draggingId && itemId === draggingId);
      item.classList.toggle("reorder-drop-before", !!dropTargetId && itemId === dropTargetId && dropPosition === "before" && itemId !== draggingId);
      item.classList.toggle("reorder-drop-after", !!dropTargetId && itemId === dropTargetId && dropPosition === "after" && itemId !== draggingId);
    });
    if (typeof config.onStateChange === "function") {
      config.onStateChange({
        draggingId,
        dropTargetId,
        dropPosition,
      });
    }
  }

  function clearState() {
    draggingId = "";
    dropTargetId = "";
    dropPosition = "before";
    syncClasses();
  }

  function handleDragStart(itemId, event) {
    if (!canDrag(itemId)) {
      event.preventDefault();
      return;
    }
    draggingId = itemId;
    dropTargetId = "";
    dropPosition = "before";
    if (event.dataTransfer) {
      event.dataTransfer.effectAllowed = "move";
      event.dataTransfer.setData("text/plain", itemId);
    }
    syncClasses();
  }

  function handleDragOver(itemId, item, event) {
    if (!draggingId || draggingId === itemId || !canDrop(itemId)) {
      return;
    }
    event.preventDefault();
    if (event.dataTransfer) {
      event.dataTransfer.dropEffect = "move";
    }
    const rect = item.getBoundingClientRect();
    dropTargetId = itemId;
    dropPosition = event.clientY < rect.top + (rect.height / 2) ? "before" : "after";
    syncClasses();
  }

  function handleDrop(itemId, event) {
    if (!draggingId || draggingId === itemId || !canDrop(itemId)) {
      clearState();
      return;
    }
    event.preventDefault();
    const nextDraggingId = draggingId;
    const nextDropPosition = dropPosition;
    clearState();
    if (typeof config.onMove === "function") {
      config.onMove(nextDraggingId, itemId, nextDropPosition);
    }
  }

  function bindItem(item) {
    if (!item || item.dataset.reorderBound === "true") {
      return;
    }
    const itemId = resolveItemId(item);
    if (!itemId) {
      return;
    }
    item.dataset.reorderBound = "true";

    item.addEventListener("dragover", (event) => {
      handleDragOver(itemId, item, event);
    });
    item.addEventListener("drop", (event) => {
      handleDrop(itemId, event);
    });

    const handles = config.handleSelector ? Array.from(item.querySelectorAll(config.handleSelector)) : [item];
    handles.forEach((handle) => {
      handle.setAttribute("draggable", canDrag(itemId) ? "true" : "false");
      handle.addEventListener("dragstart", (event) => {
        handleDragStart(itemId, event);
      });
      handle.addEventListener("dragend", () => {
        clearState();
      });
    });
  }

  function refresh() {
    getItems().forEach((item) => bindItem(item));
    syncClasses();
  }

  return {
    clearState,
    refresh,
  };
}
