/**
 * M11 版本 diff 预览 - 纯函数模块
 *
 * 基于字符串差异对比算法，输出可用于 React 渲染的 diff 段落。
 * 实现思路：
 * - 将 LessonPlanDocument 序列化为可读文本（节点列表 + 标题）
 * - 使用简化的 LCS 算法对比两段文本
 * - 输出 added/removed/unchanged 三种段落
 *
 * 此模块仅做纯计算，UI 渲染由 version-diff-viewer.tsx 负责。
 */

import type { LessonPlanDocument } from "../types";

/** diff 段落类型 */
export type DiffSegmentType = "added" | "removed" | "unchanged";

/** diff 单个段落 */
export interface DiffSegment {
  type: DiffSegmentType;
  content: string;
  lineNumber?: number;
}

/**
 * 将 LessonPlanDocument 序列化为可读文本（用于 diff 比对）
 * 每行一个节点，包含节点类型、标题、关键字段摘要
 */
export function serializeDocumentToText(doc: LessonPlanDocument): string {
  const lines: string[] = [];
  lines.push(`Version: ${doc.version}`);
  lines.push(`TextbookNodeId: ${doc.textbookContentNodeId}`);
  lines.push(`Nodes (${doc.nodes.length}):`);

  for (const node of doc.nodes) {
    const summary = summarizeNode(node);
    lines.push(`  [${node.type}] ${node.title ?? "(no title)"} - ${summary}`);
  }

  lines.push(`Edges (${doc.edges.length}):`);
  for (const edge of doc.edges) {
    lines.push(`  ${edge.source} → ${edge.target} (${"type" in edge ? edge.type : "unknown"})`);
  }

  lines.push(`Anchors (${doc.anchors.length}):`);
  for (const anchor of doc.anchors) {
    lines.push(
      `  ${anchor.id}: node=${anchor.nodeId} type=${anchor.type} start=${anchor.start}${anchor.end !== undefined ? ` end=${anchor.end}` : ""}`,
    );
  }

  return lines.join("\n");
}

/**
 * 节点摘要（提取关键字段以便 diff 可读）
 */
function summarizeNode(node: { data?: unknown; type: string }): string {
  if (!node.data || typeof node.data !== "object") return "";
  const data = node.data as Record<string, unknown>;
  const fields: string[] = [];
  for (const key of Object.keys(data).slice(0, 5)) {
    const value = data[key];
    if (typeof value === "string") {
      fields.push(`${key}="${value.slice(0, 60)}"`);
    } else if (Array.isArray(value)) {
      fields.push(`${key}=[${value.length} items]`);
    } else if (typeof value === "number" || typeof value === "boolean") {
      fields.push(`${key}=${value}`);
    }
  }
  return fields.join(", ");
}

/**
 * 简化 LCS diff 算法
 * 输入两段文本，输出 diff 段落数组
 *
 * 时间复杂度 O(n*m)，对于教案文档（通常 < 500 行）可接受。
 * 对于更长文本可换用 diff-match-patch 库。
 */
export function computeTextDiff(
  oldText: string,
  newText: string,
): DiffSegment[] {
  const oldLines = oldText.split("\n");
  const newLines = newText.split("\n");

  // 构建 LCS 矩阵
  const m = oldLines.length;
  const n = newLines.length;
  const lcs: number[][] = Array.from({ length: m + 1 }, () =>
    new Array<number>(n + 1).fill(0),
  );

  for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
      if (oldLines[i - 1] === newLines[j - 1]) {
        lcs[i][j] = lcs[i - 1][j - 1] + 1;
      } else {
        lcs[i][j] = Math.max(lcs[i - 1][j], lcs[i][j - 1]);
      }
    }
  }

  // 回溯生成 diff 段落
  const segments: DiffSegment[] = [];
  let i = m;
  let j = n;
  while (i > 0 || j > 0) {
    if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
      segments.unshift({
        type: "unchanged",
        content: oldLines[i - 1]!,
        lineNumber: i,
      });
      i--;
      j--;
    } else if (j > 0 && (i === 0 || lcs[i][j - 1] >= lcs[i - 1][j])) {
      segments.unshift({
        type: "added",
        content: newLines[j - 1]!,
        lineNumber: j,
      });
      j--;
    } else if (i > 0) {
      segments.unshift({
        type: "removed",
        content: oldLines[i - 1]!,
        lineNumber: i,
      });
      i--;
    }
  }

  return segments;
}

/**
 * 计算两个课案文档的 diff
 */
export function computeDocumentDiff(
  oldDoc: LessonPlanDocument,
  newDoc: LessonPlanDocument,
): DiffSegment[] {
  return computeTextDiff(
    serializeDocumentToText(oldDoc),
    serializeDocumentToText(newDoc),
  );
}

/**
 * 统计 diff 摘要
 */
export function summarizeDiff(
  segments: DiffSegment[],
): { added: number; removed: number; unchanged: number; total: number } {
  let added = 0;
  let removed = 0;
  let unchanged = 0;
  for (const seg of segments) {
    if (seg.type === "added") added++;
    else if (seg.type === "removed") removed++;
    else unchanged++;
  }
  return { added, removed, unchanged, total: segments.length };
}
