import type { DocumentRecord } from "@/lib/documents";
import type {
  TreeProjectionCapability,
  TreeProjectionResourceMeta,
} from "@/lib/tree-protocol";

export type KernelSidebarProjectionEdge = {
  id: string;
  edgeType: "parent_of";
  workspaceId: string | null;
  fromNodeId: string;
  toNodeId: string;
};

export type KernelSidebarProjectionItem = {
  nodeId: string;
  parentNodeId: string | null;
  nodeType: "page";
  projectionKind: "sidebar_tree";
  title: string | null;
  depth: number;
  position: number | null;
  childCount: number;
  expandable: boolean;
  expandedByDefault: boolean;
  capabilities: TreeProjectionCapability[];
  resourceMeta: TreeProjectionResourceMeta;
  iconHint: string | null;
};

export type KernelSidebarProjection = {
  projectionId: string;
  projection: "sidebar_tree";
  rootNodeId: string | null;
  items: KernelSidebarProjectionItem[];
  edges: KernelSidebarProjectionEdge[];
};

export type SidebarTreeNode = DocumentRecord & {
  children: SidebarTreeNode[];
  kernel?: {
    nodeType: "page";
    depth: number;
    position: number | null;
    childCount: number;
    expandedByDefault: boolean;
  };
};

function sortRecords(records: DocumentRecord[]) {
  return [...records].sort((a, b) => {
    const orderA = a.sort_order ?? Number.MAX_SAFE_INTEGER;
    const orderB = b.sort_order ?? Number.MAX_SAFE_INTEGER;
    if (orderA !== orderB) {
      return orderA - orderB;
    }
    return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
  });
}

function dedupeRecords(records: DocumentRecord[]) {
  const seen = new Set<string>();
  const unique: DocumentRecord[] = [];
  for (let index = records.length - 1; index >= 0; index -= 1) {
    const record = records[index]!;
    if (seen.has(record.id)) {
      continue;
    }
    seen.add(record.id);
    unique.push(record);
  }
  unique.reverse();
  return unique;
}

function buildSidebarCapabilities(childCount: number): TreeProjectionCapability[] {
  const capabilities: TreeProjectionCapability[] = [
    "open",
    "drag",
    "drop",
    "select",
    "create-child",
    "rename",
    "archive",
    "restore",
    "context-menu",
    "reorder",
  ];
  if (childCount > 0) {
    capabilities.unshift("expand");
  }
  return capabilities;
}

function buildChildrenByParent(records: DocumentRecord[]) {
  const childrenByParentId = new Map<string | null, DocumentRecord[]>();
  const recordIds = new Set(records.map((record) => record.id));
  for (const record of records) {
    const parentId =
      record.parent_id && recordIds.has(record.parent_id) ? record.parent_id : null;
    const bucket = childrenByParentId.get(parentId) ?? [];
    bucket.push(record);
    childrenByParentId.set(parentId, bucket);
  }
  for (const [parentId, bucket] of childrenByParentId.entries()) {
    childrenByParentId.set(parentId, sortRecords(bucket));
  }
  return childrenByParentId;
}

export function buildKernelSidebarProjection(
  records: DocumentRecord[],
): KernelSidebarProjection {
  const uniqueRecords = dedupeRecords(records);
  const recordById = new Map(uniqueRecords.map((record) => [record.id, record]));
  const childrenByParentId = buildChildrenByParent(uniqueRecords);
  const items: KernelSidebarProjectionItem[] = [];
  const edges: KernelSidebarProjectionEdge[] = [];
  const visited = new Set<string>();

  const walk = (parentId: string | null, depth: number) => {
    const children = childrenByParentId.get(parentId) ?? [];
    for (const child of children) {
      if (visited.has(child.id)) {
        continue;
      }
      visited.add(child.id);
      const childNodes = childrenByParentId.get(child.id) ?? [];
      items.push({
        nodeId: child.id,
        parentNodeId: parentId,
        nodeType: "page",
        projectionKind: "sidebar_tree",
        title: child.title ?? "无标题",
        depth,
        position: child.sort_order ?? null,
        childCount: childNodes.length,
        expandable: childNodes.length > 0,
        expandedByDefault: true,
        capabilities: buildSidebarCapabilities(childNodes.length),
        resourceMeta: {
          resourceKind: "document",
          documentId: child.id,
          workspaceId: child.workspace_id ?? null,
          iconHint: "page",
        },
        iconHint: "page",
      });
      if (parentId && recordById.has(parentId)) {
        edges.push({
          id: `edge:${parentId}:${child.id}:parent_of`,
          edgeType: "parent_of",
          workspaceId: child.workspace_id ?? null,
          fromNodeId: parentId,
          toNodeId: child.id,
        });
      }
      walk(child.id, depth + 1);
    }
  };

  walk(null, 0);

  return {
    projectionId: "kernel_projection:sidebar_tree:workspace_root",
    projection: "sidebar_tree",
    rootNodeId: null,
    items,
    edges,
  };
}

export function buildSidebarTreeFromKernelProjection(input: {
  records: DocumentRecord[];
  projection: KernelSidebarProjection;
}): SidebarTreeNode[] {
  const projectionItems = Array.isArray(input.projection?.items)
    ? input.projection.items
    : [];
  const recordById = new Map(input.records.map((record) => [record.id, record]));
  const nodeMap = new Map<string, SidebarTreeNode>();
  const itemById = new Map(projectionItems.map((item) => [item.nodeId, item]));

  for (const item of projectionItems) {
    const record = recordById.get(item.nodeId);
    nodeMap.set(item.nodeId, {
      access_scope: record?.access_scope ?? "private",
      id: item.nodeId,
      workspace_id: record?.workspace_id ?? "",
      title: record?.title ?? item.title ?? "无标题",
      parent_id: record?.parent_id ?? item.parentNodeId,
      sort_order: record?.sort_order ?? item.position,
      is_starred: record?.is_starred ?? false,
      is_template: record?.is_template ?? false,
      created_at: record?.created_at ?? "",
      updated_at: record?.updated_at ?? null,
      children: [],
      kernel: {
        nodeType: item.nodeType,
        depth: item.depth,
        position: item.position,
        childCount: item.childCount,
        expandedByDefault: item.expandedByDefault,
      },
    });
  }

  const roots: SidebarTreeNode[] = [];
  for (const item of projectionItems) {
    const node = nodeMap.get(item.nodeId);
    if (!node) continue;
    const parentId = item.parentNodeId;
    if (parentId && nodeMap.has(parentId)) {
      nodeMap.get(parentId)!.children.push(node);
      continue;
    }
    roots.push(node);
  }

  const sortTree = (nodes: SidebarTreeNode[]) => {
    nodes.sort((a, b) => {
      const orderA = a.kernel?.position ?? a.sort_order ?? Number.MAX_SAFE_INTEGER;
      const orderB = b.kernel?.position ?? b.sort_order ?? Number.MAX_SAFE_INTEGER;
      if (orderA !== orderB) {
        return orderA - orderB;
      }
      return new Date(a.created_at).getTime() - new Date(b.created_at).getTime();
    });
    nodes.forEach((node) => sortTree(node.children));
  };

  sortTree(roots);

  // 防御性处理：如果 projection 丢了节点，但 records 里还在，补到根节点，避免页面从主导航消失。
  const missingRoots = input.records
    .filter((record) => !itemById.has(record.id))
    .map((record) => ({
      ...record,
      children: [],
      kernel: {
        nodeType: "page" as const,
        depth: 0,
        position: record.sort_order ?? null,
        childCount: 0,
        expandedByDefault: true,
      },
    }));
  if (missingRoots.length > 0) {
    roots.push(...missingRoots);
    sortTree(roots);
  }

  return roots;
}
