"use client";

import type { KernelFileTreeProjectionItem } from "@/lib/kernel-file-tree";
import {
  getDocIdFromFileTreeItem,
  resolveFileTreeRowAsset,
  resolveFileTreeRowNode,
} from "@/lib/kernel-file-tree";
import type { SidebarTreeNode } from "@/lib/kernel-sidebar";
import type { MediaAsset } from "@/types/media";

export type FileTreeShellRowKind = "doc" | "index" | "asset" | "asset-folder";

export type FileTreeShellRow = {
  rowId: string;
  rowKind: FileTreeShellRowKind;
  documentId: string;
  assetId: string | null;
  node: SidebarTreeNode | null;
  asset: MediaAsset | null;
};

export type FileTreeShellInternalDropPreflightRow = {
  rowId: string;
  rowKind: FileTreeShellRowKind;
  documentId: string | null;
  assetId: string | null;
  assetDocumentId: string | null;
  assetType: string | null;
  storagePath: string | null;
};

export type FileTreeShellInternalDropPreflightPayload = {
  workspaceId: string | null;
  copy: boolean;
  targetDocumentId: string | null;
  targetRowId: string | null;
  focusedRowId: string | null;
  activeDocumentId: string | null;
  rowIds: string[];
  rows: FileTreeShellInternalDropPreflightRow[];
  documentParents: Array<{ documentId: string; parentId: string | null }>;
};

export type FileTreeShellDeletePreflightPayload = {
  workspaceId: string | null;
  rowIds: string[];
  rows: FileTreeShellInternalDropPreflightRow[];
  documentParents: Array<{ documentId: string; parentId: string | null }>;
};

export type FileTreeShellPastePreflightPayload = {
  workspaceId: string | null;
  targetDocumentId: string | null;
  focusedRowId: string | null;
  activeDocumentId: string | null;
  rowIds: string[];
  rows: FileTreeShellInternalDropPreflightRow[];
};

export type FileTreeShellUploadTargetPreflightPayload = {
  workspaceId: string | null;
  targetDocumentId: string | null;
  targetRowId: string | null;
  focusedRowId: string | null;
  activeDocumentId: string | null;
  rows: FileTreeShellInternalDropPreflightRow[];
  documentWorkspaces: Array<{ documentId: string; workspaceId: string | null }>;
};

function toShellRowKind(item: KernelFileTreeProjectionItem): FileTreeShellRowKind {
  switch (item.rowKind) {
    case "document":
      return "doc";
    case "asset_folder":
      return "asset-folder";
    default:
      return item.rowKind;
  }
}

export function buildFileTreeShellVisibleRowIds(
  fileTreeItems: readonly KernelFileTreeProjectionItem[],
): string[] {
  return fileTreeItems
    .map((item) => item.rowId)
    .filter((rowId): rowId is string => typeof rowId === "string" && rowId.trim().length > 0);
}

export function buildFileTreeShellRowById(input: {
  fileTreeItems: readonly KernelFileTreeProjectionItem[];
  nodeById?: Map<string, SidebarTreeNode>;
  assetById?: Map<string, MediaAsset>;
}): Map<string, FileTreeShellRow> {
  const rowById = new Map<string, FileTreeShellRow>();

  input.fileTreeItems.forEach((item) => {
    const rowId = typeof item.rowId === "string" ? item.rowId.trim() : "";
    if (!rowId || rowById.has(rowId)) {
      return;
    }

    const rowKind = toShellRowKind(item);
    const documentId = getDocIdFromFileTreeItem(item);
    const isDocumentRow = rowKind === "doc" || rowKind === "index";

    rowById.set(rowId, {
      rowId,
      rowKind,
      documentId,
      assetId: isDocumentRow ? null : item.resourceMeta.assetId ?? null,
      node: isDocumentRow ? resolveFileTreeRowNode(item, input.nodeById) : null,
      asset: isDocumentRow ? null : resolveFileTreeRowAsset(item, input.assetById),
    });
  });

  return rowById;
}

export function getOrderedFileTreeShellRows(input: {
  rowIds: Iterable<string>;
  visibleRowIds: readonly string[];
  rowById: Map<string, FileTreeShellRow>;
}): FileTreeShellRow[] {
  const selectedRowIds = new Set<string>();
  for (const rowId of input.rowIds) {
    if (typeof rowId !== "string" || rowId.trim().length === 0) {
      continue;
    }
    if (!input.rowById.has(rowId)) {
      continue;
    }
    selectedRowIds.add(rowId);
  }

  return input.visibleRowIds
    .map((rowId) => (selectedRowIds.has(rowId) ? input.rowById.get(rowId) ?? null : null))
    .filter((row): row is FileTreeShellRow => Boolean(row));
}

export function extractMindmapAssetIdFromStoragePath(
  storagePath: string | null | undefined,
): string | null {
  if (!storagePath) return null;
  const normalized = storagePath.replaceAll("\\", "/");

  const prefix = "mindmaps/";
  if (normalized.startsWith(prefix)) {
    const rest = normalized.slice(prefix.length);
    const id = rest.split("/")[0];
    return id ? id : null;
  }

  const marker = "/mindmaps/";
  const idx = normalized.indexOf(marker);
  if (idx === -1) return null;
  const rest = normalized.slice(idx + marker.length);
  const id = rest.split("/")[0];
  return id ? id : null;
}

export function resolveFileTreeShellMindmapTargetId(
  row: FileTreeShellRow | null,
): string | null {
  if (!row?.asset) {
    return null;
  }
  if (row.rowKind === "asset-folder" && row.asset.asset_type === "mindmap") {
    return row.asset.id;
  }
  if (row.rowKind === "asset") {
    return extractMindmapAssetIdFromStoragePath(row.asset.storage_path);
  }
  return null;
}

export function inferFileTreeShellTargetDocumentId(input: {
  focusedRowId: string | null;
  rowById: Map<string, FileTreeShellRow>;
  activeDocId: string | null;
}): string | null {
  if (input.focusedRowId) {
    const row = input.rowById.get(input.focusedRowId) ?? null;
    if (row?.documentId) {
      return row.documentId;
    }
  }
  return input.activeDocId || null;
}

function normalizeShellText(value: string | null | undefined): string | null {
  if (typeof value !== "string") return null;
  const trimmed = value.trim();
  return trimmed.length > 0 ? trimmed : null;
}

function buildFileTreeShellDropPreflightRow(
  row: FileTreeShellRow,
): FileTreeShellInternalDropPreflightRow {
  return {
    rowId: row.rowId,
    rowKind: row.rowKind,
    documentId: normalizeShellText(row.documentId),
    assetId: normalizeShellText(row.assetId),
    assetDocumentId: normalizeShellText(row.asset?.document_id ?? null),
    assetType: normalizeShellText(row.asset?.asset_type ?? null),
    storagePath: normalizeShellText(row.asset?.storage_path ?? null),
  };
}

export function buildFileTreeShellInternalDropPreflightPayload(input: {
  workspaceId: string | null;
  copy: boolean;
  targetDocumentId: string | null;
  targetRowId: string | null;
  focusedRowId: string | null;
  activeDocId: string | null;
  rowIds: readonly string[];
  rowById: Map<string, FileTreeShellRow>;
  parentById: Map<string, string | null>;
}): FileTreeShellInternalDropPreflightPayload {
  const rowIdSet = new Set<string>();
  const appendRowId = (value: string | null | undefined) => {
    const rowId = normalizeShellText(value);
    if (rowId) {
      rowIdSet.add(rowId);
    }
  };
  const rowIds = input.rowIds.map((rowId) => (typeof rowId === "string" ? rowId : ""));
  const targetRowId = normalizeShellText(input.targetRowId);
  const focusedRowId = normalizeShellText(input.focusedRowId);
  appendRowId(targetRowId);
  appendRowId(focusedRowId);
  rowIds.forEach(appendRowId);

  const rows = Array.from(rowIdSet)
    .map((rowId) => input.rowById.get(rowId) ?? null)
    .filter((row): row is FileTreeShellRow => Boolean(row))
    .map(buildFileTreeShellDropPreflightRow);

  return {
    workspaceId: normalizeShellText(input.workspaceId),
    copy: input.copy,
    targetDocumentId: normalizeShellText(input.targetDocumentId),
    targetRowId,
    focusedRowId,
    activeDocumentId: normalizeShellText(input.activeDocId),
    rowIds,
    rows,
    documentParents: Array.from(input.parentById.entries()).map(([documentId, parentId]) => ({
      documentId,
      parentId: normalizeShellText(parentId),
    })),
  };
}

export function buildFileTreeShellDeletePreflightPayload(input: {
  workspaceId: string | null;
  rowIds: readonly string[];
  rowById: Map<string, FileTreeShellRow>;
  parentById: Map<string, string | null>;
}): FileTreeShellDeletePreflightPayload {
  const rowIds = input.rowIds.map((rowId) => (typeof rowId === "string" ? rowId : ""));
  const rowIdSet = new Set<string>();
  rowIds.forEach((rowId) => {
    const normalized = normalizeShellText(rowId);
    if (normalized) {
      rowIdSet.add(normalized);
    }
  });

  const rows = Array.from(rowIdSet)
    .map((rowId) => input.rowById.get(rowId) ?? null)
    .filter((row): row is FileTreeShellRow => Boolean(row))
    .map(buildFileTreeShellDropPreflightRow);

  return {
    workspaceId: normalizeShellText(input.workspaceId),
    rowIds,
    rows,
    documentParents: Array.from(input.parentById.entries()).map(([documentId, parentId]) => ({
      documentId,
      parentId: normalizeShellText(parentId),
    })),
  };
}

export function buildFileTreeShellPastePreflightPayload(input: {
  workspaceId: string | null;
  targetDocumentId: string | null;
  focusedRowId: string | null;
  activeDocId: string | null;
  rowIds: readonly string[];
  rowById: Map<string, FileTreeShellRow>;
}): FileTreeShellPastePreflightPayload {
  const rowIds = input.rowIds.map((rowId) => (typeof rowId === "string" ? rowId : ""));
  const rowIdSet = new Set<string>();
  const focusedRowId = normalizeShellText(input.focusedRowId);
  if (focusedRowId) {
    rowIdSet.add(focusedRowId);
  }
  rowIds.forEach((rowId) => {
    const normalized = normalizeShellText(rowId);
    if (normalized) {
      rowIdSet.add(normalized);
    }
  });

  const rows = Array.from(rowIdSet)
    .map((rowId) => input.rowById.get(rowId) ?? null)
    .filter((row): row is FileTreeShellRow => Boolean(row))
    .map(buildFileTreeShellDropPreflightRow);

  return {
    workspaceId: normalizeShellText(input.workspaceId),
    targetDocumentId: normalizeShellText(input.targetDocumentId),
    focusedRowId,
    activeDocumentId: normalizeShellText(input.activeDocId),
    rowIds,
    rows,
  };
}

export function buildFileTreeShellUploadTargetPreflightPayload(input: {
  workspaceId: string | null;
  targetDocumentId: string | null;
  targetRowId: string | null;
  focusedRowId: string | null;
  activeDocId: string | null;
  rowById: Map<string, FileTreeShellRow>;
  documentWorkspaceById: Map<string, string | null>;
}): FileTreeShellUploadTargetPreflightPayload {
  const rowIdSet = new Set<string>();
  const appendRowId = (value: string | null | undefined) => {
    const rowId = normalizeShellText(value);
    if (rowId) {
      rowIdSet.add(rowId);
    }
  };
  const targetRowId = normalizeShellText(input.targetRowId);
  const focusedRowId = normalizeShellText(input.focusedRowId);
  appendRowId(targetRowId);
  appendRowId(focusedRowId);

  const rows = Array.from(rowIdSet)
    .map((rowId) => input.rowById.get(rowId) ?? null)
    .filter((row): row is FileTreeShellRow => Boolean(row))
    .map(buildFileTreeShellDropPreflightRow);

  return {
    workspaceId: normalizeShellText(input.workspaceId),
    targetDocumentId: normalizeShellText(input.targetDocumentId),
    targetRowId,
    focusedRowId,
    activeDocumentId: normalizeShellText(input.activeDocId),
    rows,
    documentWorkspaces: Array.from(input.documentWorkspaceById.entries()).map(
      ([documentId, workspaceId]) => ({
        documentId,
        workspaceId: normalizeShellText(workspaceId),
      }),
    ),
  };
}

export function collectFileTreeShellAssetHints(input: {
  rowById: Map<string, FileTreeShellRow>;
  assetIds: readonly string[];
}): MediaAsset[] {
  const hints: MediaAsset[] = [];
  const seen = new Set<string>();
  input.assetIds.forEach((assetId) => {
    const normalized = normalizeShellText(assetId);
    if (!normalized || seen.has(normalized)) {
      return;
    }
    const asset =
      input.rowById.get(`asset:${normalized}`)?.asset ??
      input.rowById.get(`asset-folder:${normalized}`)?.asset ??
      null;
    if (!asset) {
      return;
    }
    seen.add(normalized);
    hints.push(asset);
  });
  return hints;
}
