export type PageSubtreeInlineNode = {
  type?: string;
  text?: string;
  href?: string;
  content?: unknown;
  styles?: Record<string, unknown>;
};

export type PageSubtreeBlock = {
  id?: string;
  type?: string;
  props?: Record<string, unknown>;
  content?: unknown;
  children?: unknown;
};

export type PageSubtreeNodeType =
  | "page"
  | "section"
  | "content_node"
  | "reference_anchor"
  | "mindmap";

export type PageSubtreeNode = {
  id: string;
  parentNodeId: string | null;
  nodeType: PageSubtreeNodeType;
  blockId: string | null;
  anchorBlockId: string | null;
  depth: number;
  metadata: {
    title: string | null;
    textSnippet: string | null;
    blockType: string | null;
    headingLevel: number | null;
    numbering: string | null;
    childCount: number;
    order: number;
    path: string[];
  };
};

export type PageOutlineEntry = {
  id: string;
  nodeId: string;
  anchorBlockId: string | null;
  title: string;
  level: number;
  numbering: string;
};

export type PageEvidenceItem = {
  id: string;
  nodeId: string;
  blockId: string | null;
  kind:
    | "page"
    | "heading"
    | "paragraph"
    | "list"
    | "todo"
    | "quote"
    | "code"
    | "media"
    | "reference"
    | "table"
    | "mindmap"
    | "text";
  snippet: string;
};

export type PageSubtreeProjection = {
  projectionId: string;
  projection: "page_tree";
  rootNodeId: string;
  rootNode: PageSubtreeNode;
  subtree: {
    rootNodeId: string;
    nodes: PageSubtreeNode[];
  };
  outline: PageOutlineEntry[];
  evidence: PageEvidenceItem[];
  stats: {
    blockCount: number;
    headingCount: number;
    evidenceCount: number;
    maxDepth: number;
  };
};

const SNIPPET_MAX_LENGTH = 220;
const PAGE_SUBTREE_PROJECTION_ID = "kernel_projection:page_tree:document_subtree";

const pickFirstText = (...values: unknown[]) => {
  for (const value of values) {
    if (typeof value === "string" && value.trim()) {
      return value.trim();
    }
  }
  return "";
};

const normalizeSnippet = (value: string) => value.replace(/\s+/g, " ").trim().slice(0, SNIPPET_MAX_LENGTH);

export const clampHeadingLevel = (value: unknown) => {
  const level = Number(value);
  if (Number.isNaN(level) || !Number.isFinite(level)) {
    return 1;
  }
  return Math.min(5, Math.max(1, Math.trunc(level)));
};

export const extractPageBlocks = (content: unknown): PageSubtreeBlock[] => {
  if (Array.isArray(content)) {
    return content as PageSubtreeBlock[];
  }
  if (content && typeof content === "object") {
    const blocks = (content as { blocks?: unknown }).blocks;
    if (Array.isArray(blocks)) {
      return blocks as PageSubtreeBlock[];
    }
  }
  return [];
};

export const getPageBlockChildren = (value: unknown): PageSubtreeBlock[] => {
  if (!Array.isArray(value)) {
    return [];
  }
  return value as PageSubtreeBlock[];
};

export const getInlineText = (value: unknown): string => {
  if (typeof value === "string") {
    return value;
  }
  if (!Array.isArray(value)) {
    return "";
  }
  return value
    .map((node) => {
      if (typeof node === "string") {
        return node;
      }
      if (!node || typeof node !== "object") {
        return "";
      }
      const typedNode = node as PageSubtreeInlineNode;
      if (typedNode.type === "link") {
        return getInlineText(typedNode.content);
      }
      return typeof typedNode.text === "string" ? typedNode.text : "";
    })
    .join("");
};

const getBlockSnippet = (block: PageSubtreeBlock): string => {
  const props = block.props ?? {};
  const inlineText = normalizeSnippet(getInlineText(block.content));
  if (inlineText) {
    return inlineText;
  }
  return normalizeSnippet(
    pickFirstText(
      props.title,
      props.caption,
      props.summary,
      props.fileName,
      props.name,
      props.alt,
      props.status,
    ),
  );
};

const getBlockDisplayTitle = (block: PageSubtreeBlock, snippet: string) => {
  const props = block.props ?? {};
  switch (block.type) {
    case "heading":
      return snippet || "未命名标题";
    case "pageReference":
      return pickFirstText(props.title, snippet, "页面引用");
    case "blockReference":
      return pickFirstText(props.title, snippet, "块引用");
    case "onlineTable":
      return pickFirstText(props.title, snippet, "在线表格");
    case "mindmap":
      return pickFirstText(props.title, snippet, "思维导图");
    case "media":
      return pickFirstText(props.caption, props.fileName, snippet, "附件");
    case "codeBlock":
      return snippet || "代码块";
    case "advancedTodo":
      return snippet || "任务";
    case "quote":
      return snippet || "引用";
    default:
      return snippet || null;
  }
};

const getNodeType = (block: PageSubtreeBlock): PageSubtreeNodeType => {
  switch (block.type) {
    case "heading":
      return "section";
    case "blockReference":
    case "pageReference":
      return "reference_anchor";
    case "mindmap":
      return "mindmap";
    default:
      return "content_node";
  }
};

const getEvidenceKind = (block: PageSubtreeBlock): PageEvidenceItem["kind"] => {
  switch (block.type) {
    case "heading":
      return "heading";
    case "paragraph":
      return "paragraph";
    case "bulletListItem":
    case "numberedListItem":
    case "checkListItem":
      return "list";
    case "advancedTodo":
      return "todo";
    case "quote":
      return "quote";
    case "codeBlock":
      return "code";
    case "media":
      return "media";
    case "pageReference":
    case "blockReference":
      return "reference";
    case "onlineTable":
      return "table";
    case "mindmap":
      return "mindmap";
    default:
      return "text";
  }
};

export function buildPageSubtreeProjection(input: {
  documentId: string;
  title: string | null;
  content: unknown;
}): PageSubtreeProjection {
  const documentId = String(input.documentId ?? "").trim();
  const rootNodeId = documentId || "page:unknown";
  const blocks = extractPageBlocks(input.content);
  const rootTitle = pickFirstText(input.title, "无标题");
  const rootNode: PageSubtreeNode = {
    id: rootNodeId,
    parentNodeId: null,
    nodeType: "page",
    blockId: null,
    anchorBlockId: null,
    depth: 0,
    metadata: {
      title: rootTitle,
      textSnippet: null,
      blockType: "page",
      headingLevel: null,
      numbering: null,
      childCount: blocks.length,
      order: 0,
      path: [rootNodeId],
    },
  };

  const nodes: PageSubtreeNode[] = [rootNode];
  const outline: PageOutlineEntry[] = [];
  const evidence: PageEvidenceItem[] = [];
  const headingCounters = [0, 0, 0, 0, 0];
  const headingStack: Array<{ level: number; nodeId: string }> = [];
  let order = 0;
  let maxDepth = 0;

  const walk = (items: PageSubtreeBlock[], parentBlockNodeId: string | null, depth: number, path: number[]) => {
    items.forEach((block, index) => {
      const blockId = typeof block.id === "string" && block.id.trim() ? block.id.trim() : null;
      const nodeId = blockId ? `block:${blockId}` : `block:auto:${[...path, index].join(".")}`;
      const snippet = getBlockSnippet(block);
      const headingLevel = block.type === "heading" ? clampHeadingLevel(block.props?.level) : null;
      let parentNodeId = parentBlockNodeId ?? headingStack.at(-1)?.nodeId ?? rootNodeId;
      let numbering: string | null = null;

      if (headingLevel != null) {
        while (headingStack.length > 0 && headingStack[headingStack.length - 1]!.level >= headingLevel) {
          headingStack.pop();
        }
        parentNodeId = headingStack.at(-1)?.nodeId ?? parentBlockNodeId ?? rootNodeId;
        headingCounters[headingLevel - 1] += 1;
        for (let counterIndex = headingLevel; counterIndex < headingCounters.length; counterIndex += 1) {
          headingCounters[counterIndex] = 0;
        }
        numbering = headingCounters
          .slice(0, headingLevel)
          .filter((value) => value > 0)
          .join(".");
      }

      order += 1;
      const children = getPageBlockChildren(block.children);
      const node: PageSubtreeNode = {
        id: nodeId,
        parentNodeId,
        nodeType: getNodeType(block),
        blockId,
        anchorBlockId: blockId,
        depth: depth + 1,
        metadata: {
          title: getBlockDisplayTitle(block, snippet),
          textSnippet: snippet || null,
          blockType: typeof block.type === "string" ? block.type : null,
          headingLevel,
          numbering,
          childCount: children.length,
          order,
          path: [rootNodeId, ...path.map(String), String(index)],
        },
      };

      nodes.push(node);
      maxDepth = Math.max(maxDepth, node.depth);

      if (headingLevel != null) {
        outline.push({
          id: blockId ?? node.id,
          nodeId: node.id,
          anchorBlockId: blockId,
          title: node.metadata.title ?? "未命名标题",
          level: headingLevel,
          numbering: numbering ?? "",
        });
        headingStack.push({ level: headingLevel, nodeId: node.id });
      }

      if (snippet) {
        evidence.push({
          id: `evidence:${node.id}`,
          nodeId: node.id,
          blockId,
          kind: getEvidenceKind(block),
          snippet,
        });
      }

      if (children.length > 0) {
        walk(children, node.id, depth + 1, [...path, index]);
      }
    });
  };

  walk(blocks, null, 0, []);

  if (rootTitle) {
    evidence.unshift({
      id: `evidence:${rootNodeId}`,
      nodeId: rootNodeId,
      blockId: null,
      kind: "page",
      snippet: rootTitle,
    });
  }

  return {
    projectionId: PAGE_SUBTREE_PROJECTION_ID,
    projection: "page_tree",
    rootNodeId,
    rootNode,
    subtree: {
      rootNodeId,
      nodes,
    },
    outline,
    evidence,
    stats: {
      blockCount: Math.max(0, nodes.length - 1),
      headingCount: outline.length,
      evidenceCount: evidence.length,
      maxDepth,
    },
  };
}
