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

const ITEM_NODE_PREFIX = "item:";
const FOLDER_NODE_PREFIX = "folder:";

function normalizeId(value) {
  return String(value || "").trim();
}

function uniquePush(target, value, seen) {
  if (!value || seen.has(value)) {
    return;
  }
  seen.add(value);
  target.push(value);
}

export function toItemNodeId(itemId) {
  const normalizedId = normalizeId(itemId);
  return normalizedId ? ITEM_NODE_PREFIX + normalizedId : "";
}

export function toFolderNodeId(folderId) {
  const normalizedId = normalizeId(folderId);
  return normalizedId ? FOLDER_NODE_PREFIX + normalizedId : "";
}

export function getItemIdFromNodeId(nodeId) {
  const normalizedId = normalizeId(nodeId);
  if (!normalizedId) {
    return "";
  }
  if (normalizedId.startsWith(ITEM_NODE_PREFIX)) {
    return normalizeId(normalizedId.slice(ITEM_NODE_PREFIX.length));
  }
  if (normalizedId.startsWith(FOLDER_NODE_PREFIX)) {
    return "";
  }
  return normalizedId;
}

export function getFolderIdFromNodeId(nodeId) {
  const normalizedId = normalizeId(nodeId);
  if (!normalizedId) {
    return "";
  }
  if (normalizedId.startsWith(FOLDER_NODE_PREFIX)) {
    return normalizeId(normalizedId.slice(FOLDER_NODE_PREFIX.length));
  }
  return "";
}

function cloneFolder(folderId, sourceFolder) {
  const rawFolder = sourceFolder && typeof sourceFolder === "object" && !Array.isArray(sourceFolder)
    ? sourceFolder
    : {};
  return {
    id: folderId,
    name: String(rawFolder.name || "New Folder").trim() || "New Folder",
    collapsed: rawFolder.collapsed === true,
    itemOrder: Array.isArray(rawFolder.itemOrder)
      ? rawFolder.itemOrder.map((entry) => normalizeId(entry)).filter(Boolean)
      : (Array.isArray(rawFolder.items) ? rawFolder.items.map((entry) => normalizeId(entry)).filter(Boolean) : []),
  };
}

export function normalizePanelFolderLayout(rawLayout, itemIds) {
  const validItemIds = Array.isArray(itemIds)
    ? itemIds.map((entry) => normalizeId(entry)).filter(Boolean)
    : [];
  const validItemIdSet = new Set(validItemIds);
  const rawState = rawLayout && typeof rawLayout === "object" && !Array.isArray(rawLayout)
    ? rawLayout
    : {};
  const rawFolders = rawState.folders && typeof rawState.folders === "object" && !Array.isArray(rawState.folders)
    ? rawState.folders
    : {};
  const folders = {};
  const folderIds = [];

  Object.entries(rawFolders).forEach(([rawFolderId, rawFolderValue]) => {
    const folderId = normalizeId(rawFolderValue?.id || rawFolderId);
    if (!folderId || folders[folderId]) {
      return;
    }
    folders[folderId] = cloneFolder(folderId, rawFolderValue);
    folderIds.push(folderId);
  });

  const assignedItemIds = new Set();
  folderIds.forEach((folderId) => {
    const nextItemOrder = [];
    folders[folderId].itemOrder.forEach((itemId) => {
      const normalizedId = normalizeId(itemId);
      if (!validItemIdSet.has(normalizedId) || assignedItemIds.has(normalizedId)) {
        return;
      }
      assignedItemIds.add(normalizedId);
      nextItemOrder.push(normalizedId);
    });
    folders[folderId].itemOrder = nextItemOrder;
  });

  const rootOrder = [];
  const seenRootNodes = new Set();
  const rawRootOrder = Array.isArray(rawState.rootOrder)
    ? rawState.rootOrder
    : (Array.isArray(rawState.order) ? rawState.order : []);
  rawRootOrder.forEach((rawNodeId) => {
    const folderId = getFolderIdFromNodeId(rawNodeId);
    if (folderId) {
      if (!folders[folderId]) {
        return;
      }
      uniquePush(rootOrder, toFolderNodeId(folderId), seenRootNodes);
      return;
    }
    const itemId = getItemIdFromNodeId(rawNodeId);
    if (!itemId || !validItemIdSet.has(itemId) || assignedItemIds.has(itemId)) {
      return;
    }
    assignedItemIds.add(itemId);
    uniquePush(rootOrder, toItemNodeId(itemId), seenRootNodes);
  });

  folderIds.forEach((folderId) => {
    uniquePush(rootOrder, toFolderNodeId(folderId), seenRootNodes);
  });
  validItemIds.forEach((itemId) => {
    if (assignedItemIds.has(itemId)) {
      return;
    }
    assignedItemIds.add(itemId);
    uniquePush(rootOrder, toItemNodeId(itemId), seenRootNodes);
  });

  return {
    rootOrder,
    folders,
  };
}

export function clonePanelFolderLayout(layout, itemIds) {
  return normalizePanelFolderLayout(
    JSON.parse(JSON.stringify(layout || {})),
    Array.isArray(itemIds) ? itemIds.slice() : [],
  );
}

export function createPanelFolderLayoutFolder(layout, itemIds, folderId, folderName) {
  const nextLayout = clonePanelFolderLayout(layout, itemIds);
  const normalizedFolderId = normalizeId(folderId);
  if (!normalizedFolderId || nextLayout.folders[normalizedFolderId]) {
    return nextLayout;
  }
  nextLayout.folders[normalizedFolderId] = {
    id: normalizedFolderId,
    name: String(folderName || "New Folder").trim() || "New Folder",
    collapsed: false,
    itemOrder: [],
  };
  nextLayout.rootOrder = [toFolderNodeId(normalizedFolderId)].concat(nextLayout.rootOrder.filter((nodeId) => nodeId !== toFolderNodeId(normalizedFolderId)));
  return normalizePanelFolderLayout(nextLayout, itemIds);
}

export function renamePanelFolderLayoutFolder(layout, itemIds, folderId, nextName) {
  const nextLayout = clonePanelFolderLayout(layout, itemIds);
  const normalizedFolderId = normalizeId(folderId);
  if (!normalizedFolderId || !nextLayout.folders[normalizedFolderId]) {
    return nextLayout;
  }
  nextLayout.folders[normalizedFolderId].name = String(nextName || "").trim() || nextLayout.folders[normalizedFolderId].name || "New Folder";
  return normalizePanelFolderLayout(nextLayout, itemIds);
}

export function togglePanelFolderLayoutFolder(layout, itemIds, folderId) {
  const nextLayout = clonePanelFolderLayout(layout, itemIds);
  const normalizedFolderId = normalizeId(folderId);
  if (!normalizedFolderId || !nextLayout.folders[normalizedFolderId]) {
    return nextLayout;
  }
  nextLayout.folders[normalizedFolderId].collapsed = !nextLayout.folders[normalizedFolderId].collapsed;
  return normalizePanelFolderLayout(nextLayout, itemIds);
}

export function deletePanelFolderLayoutFolder(layout, itemIds, folderId) {
  const nextLayout = clonePanelFolderLayout(layout, itemIds);
  const normalizedFolderId = normalizeId(folderId);
  const folder = nextLayout.folders[normalizedFolderId];
  if (!normalizedFolderId || !folder) {
    return nextLayout;
  }

  const folderNodeId = toFolderNodeId(normalizedFolderId);
  const folderIndex = nextLayout.rootOrder.findIndex((nodeId) => nodeId === folderNodeId);
  const nextRootOrder = nextLayout.rootOrder.filter((nodeId) => nodeId !== folderNodeId);
  const movedItemNodeIds = folder.itemOrder.map((itemId) => toItemNodeId(itemId));
  if (folderIndex >= 0) {
    nextRootOrder.splice(folderIndex, 0, ...movedItemNodeIds);
  } else {
    nextRootOrder.push(...movedItemNodeIds);
  }
  nextLayout.rootOrder = nextRootOrder;
  delete nextLayout.folders[normalizedFolderId];
  return normalizePanelFolderLayout(nextLayout, itemIds);
}

function removeItemFromLayout(layout, itemId) {
  const normalizedItemId = normalizeId(itemId);
  layout.rootOrder = layout.rootOrder.filter((nodeId) => getItemIdFromNodeId(nodeId) !== normalizedItemId);
  Object.values(layout.folders).forEach((folder) => {
    folder.itemOrder = folder.itemOrder.filter((entry) => normalizeId(entry) !== normalizedItemId);
  });
}

function removeFolderFromLayout(layout, folderId) {
  const normalizedFolderId = normalizeId(folderId);
  layout.rootOrder = layout.rootOrder.filter((nodeId) => getFolderIdFromNodeId(nodeId) !== normalizedFolderId);
}

function insertRelative(list, value, targetValue, position) {
  const nextList = Array.isArray(list) ? list.slice() : [];
  const targetIndex = nextList.findIndex((entry) => entry === targetValue);
  if (targetIndex < 0) {
    nextList.push(value);
    return nextList;
  }
  const insertionIndex = position === "after" ? targetIndex + 1 : targetIndex;
  nextList.splice(insertionIndex, 0, value);
  return nextList;
}

export function movePanelFolderLayoutNode(layout, itemIds, dragging, dropTarget) {
  const nextLayout = clonePanelFolderLayout(layout, itemIds);
  const dragKind = normalizeId(dragging?.kind);
  const dragId = normalizeId(dragging?.id);
  const dropKind = normalizeId(dropTarget?.kind);
  const dropId = normalizeId(dropTarget?.id);
  const dropPosition = dropTarget?.position === "after" ? "after" : "before";
  if (!dragKind || !dragId || !dropKind) {
    return nextLayout;
  }

  if (dragKind === "folder") {
    removeFolderFromLayout(nextLayout, dragId);
    const folderNodeId = toFolderNodeId(dragId);
    if (dropKind === "root") {
      nextLayout.rootOrder.push(folderNodeId);
      return normalizePanelFolderLayout(nextLayout, itemIds);
    }
    if (dropKind === "item" && !normalizeId(dropTarget?.parentFolderId)) {
      nextLayout.rootOrder = insertRelative(nextLayout.rootOrder, folderNodeId, toItemNodeId(dropId), dropPosition);
      return normalizePanelFolderLayout(nextLayout, itemIds);
    }
    if (dropKind === "folder") {
      nextLayout.rootOrder = insertRelative(nextLayout.rootOrder, folderNodeId, toFolderNodeId(dropId), dropPosition);
      return normalizePanelFolderLayout(nextLayout, itemIds);
    }
    nextLayout.rootOrder.push(folderNodeId);
    return normalizePanelFolderLayout(nextLayout, itemIds);
  }

  if (dragKind === "item") {
    removeItemFromLayout(nextLayout, dragId);
    if (dropKind === "folder" && nextLayout.folders[dropId]) {
      nextLayout.folders[dropId].itemOrder.push(dragId);
      return normalizePanelFolderLayout(nextLayout, itemIds);
    }
    if (dropKind === "item") {
      const parentFolderId = normalizeId(dropTarget?.parentFolderId);
      if (parentFolderId && nextLayout.folders[parentFolderId]) {
        nextLayout.folders[parentFolderId].itemOrder = insertRelative(
          nextLayout.folders[parentFolderId].itemOrder,
          dragId,
          dropId,
          dropPosition,
        );
        return normalizePanelFolderLayout(nextLayout, itemIds);
      }
      nextLayout.rootOrder = insertRelative(nextLayout.rootOrder, toItemNodeId(dragId), toItemNodeId(dropId), dropPosition);
      return normalizePanelFolderLayout(nextLayout, itemIds);
    }
    nextLayout.rootOrder.push(toItemNodeId(dragId));
    return normalizePanelFolderLayout(nextLayout, itemIds);
  }

  return nextLayout;
}
