"use client";

import Link from "next/link";
import type { CSSProperties, ReactNode } from "react";
import { cn } from "@/lib/utils";
import type { TocEntry } from "@/components/editor/document-toc";
import {
  clampHeadingLevel,
  extractPageBlocks,
  getInlineText,
  getPageBlockChildren,
  type PageOutlineEntry,
  type PageSubtreeBlock,
  type PageSubtreeInlineNode,
  type PageSubtreeProjection,
} from "@/lib/documents/page-subtree";
import type { PageOptionsState } from "@/types/page-options";

interface DocumentReadViewProps {
  content: unknown;
  documentId: string;
  options: PageOptionsState;
  pageSubtree?: PageSubtreeProjection | null;
  className?: string;
}

const TODO_STATUS_LABELS: Record<string, string> = {
  todo: "未开始",
  doing: "进行中",
  done: "已完成",
  cancelled: "已取消",
};

const LIST_INDENT_CLASS = [
  "",
  "ml-5",
  "ml-9",
  "ml-12",
  "ml-16",
];

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

const buildTextStyle = (styles: Record<string, unknown> | undefined): CSSProperties => {
  const style: CSSProperties = {};
  const textColor = styles?.textColor;
  const backgroundColor = styles?.backgroundColor;
  if (typeof textColor === "string" && textColor.trim()) {
    style.color = textColor;
  }
  if (typeof backgroundColor === "string" && backgroundColor.trim()) {
    style.backgroundColor = backgroundColor;
  }
  return style;
};

const applyInlineMarks = (
  content: ReactNode,
  styles: Record<string, unknown> | undefined,
  key: string,
): ReactNode => {
  let node = content;
  if (styles?.bold) {
    node = <strong key={`${key}-bold`}>{node}</strong>;
  }
  if (styles?.italic) {
    node = <em key={`${key}-italic`}>{node}</em>;
  }
  if (styles?.underline) {
    node = <u key={`${key}-underline`}>{node}</u>;
  }
  if (styles?.strike) {
    node = <s key={`${key}-strike`}>{node}</s>;
  }
  if (styles?.code) {
    node = (
      <code
        key={`${key}-code`}
        className="rounded bg-[#f4f4f5] px-1 py-0.5 font-mono text-[0.92em] text-[#d97706]"
      >
        {node}
      </code>
    );
  }

  const style = buildTextStyle(styles);
  if (Object.keys(style).length > 0) {
    node = (
      <span key={`${key}-style`} style={style}>
        {node}
      </span>
    );
  }
  return node;
};

const renderInlineNode = (node: unknown, key: string): ReactNode => {
  if (typeof node === "string") {
    return node;
  }
  if (!node || typeof node !== "object") {
    return null;
  }

  const typedNode = node as PageSubtreeInlineNode;
  if (typedNode.type === "link") {
    const href = typeof typedNode.href === "string" && typedNode.href.trim() ? typedNode.href : "#";
    const textContent = renderInlineNodes(typedNode.content, `${key}-content`);
    return (
      <a
        key={key}
        href={href}
        target={href.startsWith("/") ? undefined : "_blank"}
        rel={href.startsWith("/") ? undefined : "noreferrer"}
        className="text-[#2563eb] underline underline-offset-2"
      >
        {textContent.length > 0 ? textContent : href}
      </a>
    );
  }

  const text = typeof typedNode.text === "string" ? typedNode.text : "";
  return (
    <span key={key}>
      {applyInlineMarks(text, typedNode.styles, key)}
    </span>
  );
};

const renderInlineNodes = (nodes: unknown, keyPrefix: string): ReactNode[] => {
  return toInlineNodes(nodes).map((node, index) => renderInlineNode(node, `${keyPrefix}-${index}`));
};

const buildHeadingNumberingMap = (blocks: PageSubtreeBlock[]): Map<string, string> => {
  const counters = [0, 0, 0, 0, 0];
  const numberingById = new Map<string, string>();

  const walk = (targetBlocks: PageSubtreeBlock[]) => {
    targetBlocks.forEach((block) => {
      if (block.type === "heading") {
        const level = clampHeadingLevel(block.props?.level);
        counters[level - 1] += 1;
        for (let index = level; index < counters.length; index += 1) {
          counters[index] = 0;
        }
        if (block.id) {
          numberingById.set(
            block.id,
            counters
              .slice(0, level)
              .filter((value) => value > 0)
              .join("."),
          );
        }
      }
      const children = getPageBlockChildren(block.children);
      if (children.length > 0) {
        walk(children);
      }
    });
  };

  walk(blocks);
  return numberingById;
};

const buildHeadingNumberingMapFromOutline = (outline: PageOutlineEntry[]): Map<string, string> => {
  return new Map(
    outline
      .filter((entry) => entry.anchorBlockId)
      .map((entry) => [entry.anchorBlockId as string, entry.numbering]),
  );
};

export const extractReadViewBlocks = (content: unknown): PageSubtreeBlock[] => extractPageBlocks(content);

export const buildReadViewTocEntries = (blocks: PageSubtreeBlock[]): TocEntry[] => {
  const counters = [0, 0, 0, 0, 0];
  return blocks.flatMap((block) => {
    if (block.type !== "heading") {
      return [];
    }
    const level = clampHeadingLevel(block.props?.level);
    counters[level - 1] += 1;
    for (let index = level; index < counters.length; index += 1) {
      counters[index] = 0;
    }
    return [{
      id: String(block.id ?? `preview-heading-${counters.join("-")}`),
      level,
      numbering: counters
        .slice(0, level)
        .filter((value) => value > 0)
        .join("."),
      title: getInlineText(block.content) || "未命名标题",
    }];
  }).map(({ id, level, numbering, title }) => ({
    id,
    level,
    numbering,
    title,
  }));
};

const renderChildren = (
  block: PageSubtreeBlock,
  options: PageOptionsState,
  documentId: string,
  headingNumberingById: Map<string, string>,
  depth: number,
) => {
  const children = getPageBlockChildren(block.children);
  if (children.length === 0) {
    return null;
  }
  return (
    <div className={cn("mt-2 space-y-1", LIST_INDENT_CLASS[Math.min(depth + 1, LIST_INDENT_CLASS.length - 1)])}>
      {renderBlocks(children, options, documentId, headingNumberingById, depth + 1)}
    </div>
  );
};

const renderMediaBlock = (block: PageSubtreeBlock) => {
  const props = block.props ?? {};
  const assetType = String(props.assetType ?? "image");
  const fileUrl = typeof props.fileUrl === "string" ? props.fileUrl : "";
  const thumbnailUrl =
    typeof props.thumbnailUrl === "string" && props.thumbnailUrl.trim() ? props.thumbnailUrl : fileUrl;
  const fileName =
    typeof props.fileName === "string" && props.fileName.trim() ? props.fileName : "未命名资源";
  const caption = typeof props.caption === "string" ? props.caption.trim() : "";

  if (!fileUrl) {
    return (
      <div className="rounded-2xl border border-dashed border-[#d4d4d8] bg-[#fafafa] px-4 py-6 text-sm text-[#71717a]">
        资源链接不可用
      </div>
    );
  }

  if (assetType === "image") {
    return (
      <figure className="space-y-3">
        {/* eslint-disable-next-line @next/next/no-img-element */}
        <img
          src={thumbnailUrl}
          alt={caption || fileName}
          className="max-h-[560px] w-auto max-w-full rounded-2xl border border-[#f1f5f9] object-contain shadow-sm"
        />
        {(caption || fileName) && (
          <figcaption className="text-sm text-[#71717a]">{caption || fileName}</figcaption>
        )}
      </figure>
    );
  }

  if (assetType === "video") {
    return (
      <figure className="space-y-3">
        <video controls className="max-h-[560px] w-full rounded-2xl border border-[#f1f5f9] bg-black">
          <source src={fileUrl} />
        </video>
        {(caption || fileName) && (
          <figcaption className="text-sm text-[#71717a]">{caption || fileName}</figcaption>
        )}
      </figure>
    );
  }

  if (assetType === "audio") {
    return (
      <div className="space-y-3 rounded-2xl border border-[#e4e4e7] bg-[#fafafa] p-4">
        <div className="text-sm font-medium text-[#27272a]">{caption || fileName}</div>
        <audio controls className="w-full">
          <source src={fileUrl} />
        </audio>
      </div>
    );
  }

  return (
    <a
      href={fileUrl}
      target="_blank"
      rel="noreferrer"
      className="flex items-center justify-between rounded-2xl border border-[#e4e4e7] bg-[#fafafa] px-4 py-3 text-sm text-[#27272a] transition hover:border-[#cbd5e1] hover:bg-white"
    >
      <span className="truncate">{caption || fileName}</span>
      <span className="ml-4 shrink-0 text-xs text-[#71717a]">打开附件</span>
    </a>
  );
};

const renderBlock = (
  block: PageSubtreeBlock,
  options: PageOptionsState,
  documentId: string,
  headingNumberingById: Map<string, string>,
  depth: number,
  orderedIndex: number,
): ReactNode => {
  const key = block.id ?? `${block.type ?? "block"}-${depth}-${orderedIndex}`;
  const props = block.props ?? {};
  const inlineContent = renderInlineNodes(block.content, key);
  const plainText = getInlineText(block.content).trim();
  const children = renderChildren(block, options, documentId, headingNumberingById, depth);

  if (block.type === "pageReference" && Boolean(props.asChildPage) && options.hideChildPages) {
    return null;
  }

  switch (block.type) {
    case "heading": {
      const level = clampHeadingLevel(props.level);
      const numbering = block.id ? headingNumberingById.get(block.id) ?? "" : "";
      const HeadingTag = (`h${level}` as "h1" | "h2" | "h3" | "h4" | "h5");
      return (
        <div key={key} className="space-y-2">
          <HeadingTag
            data-id={block.id}
            id={block.id}
            className={cn(
              "scroll-mt-24 font-semibold tracking-tight text-[#18181b]",
              level === 1 && "text-[2rem]",
              level === 2 && "text-[1.6rem]",
              level === 3 && "text-[1.3rem]",
              level >= 4 && "text-[1.08rem]",
            )}
          >
            {options.showHeadingNumbers && numbering ? (
              <span className="mr-2 text-[#94a3b8]">{numbering}</span>
            ) : null}
            {inlineContent.length > 0 ? inlineContent : "未命名标题"}
          </HeadingTag>
          {children}
        </div>
      );
    }
    case "bulletListItem":
      return (
        <div key={key} className={cn("flex gap-3", LIST_INDENT_CLASS[Math.min(depth, LIST_INDENT_CLASS.length - 1)])}>
          <span className="mt-2 text-sm text-[#64748b]">•</span>
          <div className="min-w-0 flex-1 space-y-2">
            <div className="leading-7 text-[#27272a]">{inlineContent}</div>
            {children}
          </div>
        </div>
      );
    case "numberedListItem":
      return (
        <div key={key} className={cn("flex gap-3", LIST_INDENT_CLASS[Math.min(depth, LIST_INDENT_CLASS.length - 1)])}>
          <span className="mt-1.5 min-w-5 text-right text-sm font-medium text-[#64748b]">{orderedIndex}.</span>
          <div className="min-w-0 flex-1 space-y-2">
            <div className="leading-7 text-[#27272a]">{inlineContent}</div>
            {children}
          </div>
        </div>
      );
    case "checkListItem": {
      const checked = Boolean(props.checked);
      return (
        <div key={key} className={cn("flex gap-3", LIST_INDENT_CLASS[Math.min(depth, LIST_INDENT_CLASS.length - 1)])}>
          <span className="mt-1.5 text-lg leading-none text-[#2563eb]">{checked ? "☑" : "☐"}</span>
          <div className={cn("min-w-0 flex-1 space-y-2 leading-7 text-[#27272a]", checked && "text-[#71717a] line-through")}>
            <div>{inlineContent}</div>
            {children}
          </div>
        </div>
      );
    }
    case "quote":
      return (
        <blockquote
          key={key}
          className="border-l-4 border-[#dbeafe] bg-[#f8fbff] px-4 py-3 text-[#334155]"
        >
          <div className="leading-7">{inlineContent}</div>
          {children}
        </blockquote>
      );
    case "codeBlock":
      return (
        <div key={key} className="space-y-2">
          <pre className="overflow-x-auto rounded-2xl bg-[#0f172a] p-4 text-sm text-[#e2e8f0]">
            <code>{plainText}</code>
          </pre>
          {children}
        </div>
      );
    case "pageReference": {
      const pageId = typeof props.pageId === "string" ? props.pageId : "";
      const title = typeof props.title === "string" && props.title.trim() ? props.title : "未命名页面";
      return (
        <div key={key} className="space-y-2">
          <Link
            href={pageId ? `/documents/${pageId}` : "#"}
            className="inline-flex items-center gap-2 rounded-xl border border-[#e4e4e7] bg-[#fafafa] px-3 py-2 text-sm font-medium text-[#27272a] transition hover:border-[#cbd5e1] hover:bg-white"
          >
            <span className="text-[#94a3b8]">页面</span>
            <span>{title}</span>
          </Link>
          {children}
        </div>
      );
    }
    case "blockReference": {
      const sourceDocumentId = typeof props.sourceDocumentId === "string" ? props.sourceDocumentId : "";
      const targetBlockId = typeof props.targetBlockId === "string" ? props.targetBlockId : "";
      const href = sourceDocumentId ? `/documents/${sourceDocumentId}${targetBlockId ? `#${targetBlockId}` : ""}` : "#";
      return (
        <div key={key} className="space-y-2 rounded-2xl border border-dashed border-[#cbd5e1] bg-[#fafafa] px-4 py-3">
          <Link href={href} className="text-sm font-medium text-[#2563eb] underline underline-offset-2">
            打开引用块
          </Link>
          {children}
        </div>
      );
    }
    case "advancedTodo": {
      const status = String(props.status ?? "todo");
      const faded = status === "done" || status === "cancelled";
      return (
        <div key={key} className="space-y-2 rounded-2xl border border-[#e4e4e7] bg-white px-4 py-3">
          <div className="flex items-start gap-3">
            <span
              className={cn(
                "rounded-full px-2.5 py-1 text-xs font-medium",
                status === "done" && "bg-[#dcfce7] text-[#166534]",
                status === "doing" && "bg-[#dbeafe] text-[#1d4ed8]",
                status === "cancelled" && "bg-[#f3f4f6] text-[#6b7280]",
                status === "todo" && "bg-[#fef3c7] text-[#92400e]",
              )}
            >
              {TODO_STATUS_LABELS[status] ?? "未开始"}
            </span>
            <div className={cn("min-w-0 flex-1 leading-7 text-[#27272a]", faded && "text-[#71717a] line-through")}>
              {inlineContent}
            </div>
          </div>
          {children}
        </div>
      );
    }
    case "progressMeter": {
      const percent = Math.min(100, Math.max(0, Number(props.percent ?? 0) || 0));
      const summary = typeof props.summary === "string" && props.summary.trim() ? props.summary : "暂无条目";
      return (
        <div key={key} className="space-y-3 rounded-2xl border border-[#dbeafe] bg-[#f8fbff] px-4 py-4">
          <div className="flex items-center justify-between gap-3 text-sm">
            <span className="font-medium text-[#1e3a8a]">{summary}</span>
            <span className="text-[#64748b]">{percent}%</span>
          </div>
          <div className="h-2 overflow-hidden rounded-full bg-[#dbeafe]">
            <div className="h-full rounded-full bg-[#2563eb]" style={{ width: `${percent}%` }} />
          </div>
          {children}
        </div>
      );
    }
    case "media":
      return (
        <div key={key} className="space-y-2">
          {renderMediaBlock(block)}
          {children}
        </div>
      );
    case "onlineTable": {
      const tableId = typeof props.tableId === "string" ? props.tableId : "";
      return (
        <div key={key} className="space-y-2 rounded-2xl border border-[#e4e4e7] bg-[#fafafa] px-4 py-4">
          <div className="text-sm font-medium text-[#27272a]">
            {typeof props.title === "string" && props.title.trim() ? props.title : "在线表格"}
          </div>
          <Link
            href={tableId ? `/tables/${tableId}/view` : "#"}
            className="inline-flex items-center text-sm text-[#2563eb] underline underline-offset-2"
          >
            打开表格
          </Link>
          {children}
        </div>
      );
    }
    case "mindmap": {
      const mindmapId = typeof block.id === "string" ? block.id : "";
      const docId = typeof props.docId === "string" && props.docId.trim() ? props.docId : documentId;
      return (
        <div key={key} className="space-y-2 rounded-2xl border border-[#e4e4e7] bg-[#fafafa] px-4 py-4">
          <div className="text-sm font-medium text-[#27272a]">思维导图</div>
          <Link
            href={docId && mindmapId ? `/mindmap/${docId}/${mindmapId}` : "#"}
            className="inline-flex items-center text-sm text-[#2563eb] underline underline-offset-2"
          >
            打开思维导图
          </Link>
          {children}
        </div>
      );
    }
    case "paragraph":
      return (
        <div key={key} className="space-y-2">
          <p className="min-h-7 whitespace-pre-wrap break-words leading-7 text-[#27272a]">
            {inlineContent.length > 0 ? inlineContent : <span className="text-[#d4d4d8]"> </span>}
          </p>
          {children}
        </div>
      );
    default:
      if (inlineContent.length === 0 && !children) {
        return null;
      }
      return (
        <div key={key} className="space-y-2">
          {inlineContent.length > 0 ? (
            <div className="whitespace-pre-wrap break-words leading-7 text-[#27272a]">{inlineContent}</div>
          ) : null}
          {children}
        </div>
      );
  }
};

const renderBlocks = (
  blocks: PageSubtreeBlock[],
  options: PageOptionsState,
  documentId: string,
  headingNumberingById: Map<string, string>,
  depth: number,
) => {
  let orderedIndex = 0;

  return blocks.map((block, index) => {
    orderedIndex = block.type === "numberedListItem" ? orderedIndex + 1 : 0;
    return renderBlock(block, options, documentId, headingNumberingById, depth, orderedIndex || index + 1);
  });
};

function DocumentReadStructurePanel({ pageSubtree }: { pageSubtree: PageSubtreeProjection }) {
  const outlineEntries = pageSubtree.outline.slice(0, 10);
  const evidenceEntries = pageSubtree.evidence.slice(0, 5);

  return (
    <section className="rounded-2xl border border-[#dbe4f0] bg-[#f8fbff] p-4">
      <div className="flex flex-wrap items-center gap-2">
        <div className="text-sm font-semibold text-[#1e293b]">页面子树 / Kernel Outline</div>
        <span className="rounded-full bg-white px-2 py-1 text-[11px] text-[#64748b]">
          节点 {pageSubtree.subtree.nodes.length}
        </span>
        <span className="rounded-full bg-white px-2 py-1 text-[11px] text-[#64748b]">
          标题 {pageSubtree.stats.headingCount}
        </span>
        <span className="rounded-full bg-white px-2 py-1 text-[11px] text-[#64748b]">
          证据 {pageSubtree.stats.evidenceCount}
        </span>
      </div>
      <div className="mt-3 grid gap-4 lg:grid-cols-[minmax(0,1.2fr)_minmax(0,1fr)]">
        <div className="space-y-2">
          <div className="text-xs font-medium uppercase tracking-[0.18em] text-[#64748b]">大纲</div>
          {outlineEntries.length > 0 ? (
            <ul className="space-y-1.5 text-sm text-[#334155]">
              {outlineEntries.map((entry) => (
                <li key={entry.nodeId} className={cn(entry.level > 1 && "pl-4", entry.level > 2 && "pl-7", entry.level > 3 && "pl-10")}>
                  <span className="mr-2 font-mono text-[11px] text-[#94a3b8]">{entry.numbering}</span>
                  <span>{entry.title || "未命名标题"}</span>
                </li>
              ))}
            </ul>
          ) : (
            <div className="text-sm text-[#64748b]">当前页面还没有标题型 subtree。</div>
          )}
        </div>
        <div className="space-y-2">
          <div className="text-xs font-medium uppercase tracking-[0.18em] text-[#64748b]">证据</div>
          {evidenceEntries.length > 0 ? (
            <ul className="space-y-2">
              {evidenceEntries.map((entry) => (
                <li key={entry.id} className="rounded-xl border border-white/70 bg-white/80 px-3 py-2 text-sm text-[#334155]">
                  <div className="text-[11px] uppercase tracking-[0.12em] text-[#94a3b8]">{entry.kind}</div>
                  <div className="mt-1 line-clamp-2">{entry.snippet}</div>
                </li>
              ))}
            </ul>
          ) : (
            <div className="text-sm text-[#64748b]">还没有可用证据片段。</div>
          )}
        </div>
      </div>
    </section>
  );
}

export function DocumentReadView({ content, documentId, options, pageSubtree, className }: DocumentReadViewProps) {
  const blocks = extractReadViewBlocks(content);
  const headingNumberingById =
    (pageSubtree?.outline.length ?? 0) > 0
      ? buildHeadingNumberingMapFromOutline(pageSubtree?.outline ?? [])
      : buildHeadingNumberingMap(blocks);

  if (blocks.length === 0) {
    return (
      <div className={cn("space-y-4", className)}>
        {options.showStructure && pageSubtree ? <DocumentReadStructurePanel pageSubtree={pageSubtree} /> : null}
        <div className="flex min-h-[40vh] items-center justify-center rounded-2xl border border-dashed border-[#e4e4e7] bg-[#fafafa] px-6 py-10 text-sm text-[#71717a]">
          页面暂无正文内容
        </div>
      </div>
    );
  }

  return (
    <div className={cn("space-y-4", className)}>
      {options.showStructure && pageSubtree ? <DocumentReadStructurePanel pageSubtree={pageSubtree} /> : null}
      {renderBlocks(blocks, options, documentId, headingNumberingById, 0)}
    </div>
  );
}
