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

/**
 * 锚点注入算法：将锚点信息注入到 Markdown 文本中，生成带标记的纯文本。
 *
 * 策略：
 * - 由于 Markdown 渲染后是 HTML，纯文本偏移量无法直接对应 DOM 节点
 * - 简化方案：将 Markdown 视为纯文本（去除 markdown 语法符号），在纯文本上做偏移注入
 * - 注入特殊标记符号（如 ①②③ 或 [anchor:id]），由 ReactMarkdown 的 components 自定义渲染
 *
 * 对于 range 锚定：在 [start, end] 范围包裹 [[anchor:id]]...[[/anchor]] 标记
 * 对于 point 锚定：在 start 位置插入 [[point:id]] 标记
 *
 * 渲染时由 textbook-content-node.tsx 的 components 自定义解析这些标记。
 */

// 标记格式：[[anchor:id]]range text[[/anchor]] 或 [[point:id]]
const ANCHOR_RANGE_START = (id: string) => `[[anchor:${id}]]`;
const ANCHOR_RANGE_END = `[[/anchor]]`;
const ANCHOR_POINT = (id: string) => `[[point:${id}]]`;

/**
 * 将 Markdown 文本简化为纯文本（去除常见 markdown 语法符号）。
 * 仅用于锚点偏移计算，不影响实际渲染。
 */
export function markdownToPlainText(markdown: string): string {
  return markdown
    // 去除标题标记
    .replace(/^#{1,6}\s+/gm, "")
    // 去除强调符号
    .replace(/\*\*(.+?)\*\*/g, "$1")
    .replace(/\*(.+?)\*/g, "$1")
    .replace(/__(.+?)__/g, "$1")
    .replace(/_(.+?)_/g, "$1")
    // 去除链接，保留文本
    .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
    // 去除图片
    .replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1")
    // 去除代码块
    .replace(/```[\s\S]*?```/g, "")
    .replace(/`([^`]+)`/g, "$1")
    // 去除引用标记
    .replace(/^\s*>\s+/gm, "")
    // 去除列表标记
    .replace(/^[\s]*[-*+]\s+/gm, "")
    .replace(/^[\s]*\d+\.\s+/gm, "")
    // 去除水平分割线
    .replace(/^---+$/gm, "")
    // 去除 HTML 标签
    .replace(/<[^>]+>/g, "");
}

/**
 * 在 Markdown 文本中注入锚点标记。
 *
 * 注意：偏移量基于纯文本（markdownToPlainText 的输出）。
 * 由于 Markdown 语法符号的存在，纯文本偏移与 Markdown 原文偏移不一致。
 * 此函数通过构建偏移映射，将纯文本偏移转换为 Markdown 原文偏移。
 *
 * @param markdown 原始 Markdown 文本
 * @param anchors 锚点列表
 * @returns 注入标记后的 Markdown 文本
 */
export function injectPlaceholders(
  markdown: string,
  anchors: NodeAnchor[],
): string {
  if (anchors.length === 0) return markdown;

  // 构建偏移映射：plainText[i] → markdown 原文位置
  const { plainToMd } = buildOffsetMap(markdown);

  // 过滤失效锚点，按 markdown 偏移排序（倒序注入，避免偏移变化）
  const validAnchors = anchors
    .filter((a) => !a.invalid && a.start >= 0)
    .map((a) => {
      const mdStart = plainToMd.get(a.start) ?? a.start;
      const mdEnd = a.end !== undefined
        ? plainToMd.get(a.end) ?? a.end
        : undefined;
      return { ...a, mdStart, mdEnd };
    })
    .sort((a, b) => b.mdStart - a.mdStart);

  let result = markdown;
  for (const anchor of validAnchors) {
    if (anchor.type === "range" && anchor.mdEnd !== undefined && anchor.mdEnd > anchor.mdStart) {
      // 范围锚定：包裹标记
      const before = result.slice(0, anchor.mdStart);
      const middle = result.slice(anchor.mdStart, anchor.mdEnd);
      const after = result.slice(anchor.mdEnd);
      result = before + ANCHOR_RANGE_START(anchor.id) + middle + ANCHOR_RANGE_END + after;
    } else if (anchor.type === "point") {
      // 点锚定：插入标记
      const before = result.slice(0, anchor.mdStart);
      const after = result.slice(anchor.mdStart);
      result = before + ANCHOR_POINT(anchor.id) + after;
    }
  }

  return result;
}

/**
 * 构建 Markdown → 纯文本的偏移映射。
 * 返回 plainToMd（纯文本位置 → Markdown 原文位置）。
 */
function buildOffsetMap(markdown: string): {
  plainToMd: Map<number, number>;
} {
  const plainToMd = new Map<number, number>();
  let mdIdx = 0;
  let plainIdx = 0;

  // 简化映射：逐字符遍历 Markdown，跳过被去除的字符
  // 这里采用与 markdownToPlainText 一致的简化逻辑
  // V4 P2-5 修复：统一 regex 标志为 gm，与 markdownToPlainText 保持一致
  const skipPatterns: RegExp[] = [
    /^#{1,6}\s+/gm,
    /^\s*[-*+]\s+/gm,
    /^\s*\d+\.\s+/gm,
    /^\s*>\s+/gm,
    /^---+$/gm,
  ];

  while (mdIdx < markdown.length) {
    // 检查是否处于需要跳过的模式
    let skipped = false;
    for (const pattern of skipPatterns) {
      const rest = markdown.slice(mdIdx);
      const match = rest.match(pattern);
      if (match && match.index === 0) {
        mdIdx += match[0].length;
        skipped = true;
        break;
      }
    }
    if (skipped) continue;

    // 处理行内标记（**、*、`、_）
    const ch = markdown[mdIdx];
    if (ch === "*" || ch === "_" || ch === "`") {
      // 跳过成对的标记符号
      if (markdown[mdIdx + 1] === ch) {
        mdIdx += 2; // 跳过 ** 或 __ 或 ``
        continue;
      }
      mdIdx += 1; // 跳过单个 * 或 _ 或 `
      continue;
    }

    // 处理 HTML 标签
    if (ch === "<") {
      const closeIdx = markdown.indexOf(">", mdIdx);
      if (closeIdx !== -1) {
        mdIdx = closeIdx + 1;
        continue;
      }
    }

    // 处理链接 [text](url)
    if (ch === "[") {
      const closeBracket = markdown.indexOf("]", mdIdx);
      if (closeBracket !== -1 && markdown[closeBracket + 1] === "(") {
        const closeParen = markdown.indexOf(")", closeBracket + 2);
        if (closeParen !== -1) {
          // 链接文本部分映射到纯文本
          const linkText = markdown.slice(mdIdx + 1, closeBracket);
          for (const _ of linkText) {
            plainToMd.set(plainIdx, mdIdx + 1 + plainIdx);
            plainIdx++;
          }
          mdIdx = closeParen + 1;
          continue;
        }
      }
    }

    // 普通字符：建立映射
    plainToMd.set(plainIdx, mdIdx);
    plainIdx++;
    mdIdx++;
  }

  return { plainToMd };
}

/**
 * 解析注入标记后的文本，提取锚点段。
 * 用于 ReactMarkdown 自定义渲染。
 */
export interface ParsedSegment {
  type: "text" | "anchor-range" | "anchor-point";
  content: string;
  anchorId?: string;
}

export function parseAnchoredText(text: string): ParsedSegment[] {
  const segments: ParsedSegment[] = [];
  // 匹配 [[anchor:id]]...[[/anchor]] 或 [[point:id]]
  const pattern = /\[\[(anchor|point):([^\]]+)\]\](?:([\s\S]*?)\[\[\/anchor\]\])?/g;
  let lastIndex = 0;
  let match: RegExpExecArray | null;

  while ((match = pattern.exec(text)) !== null) {
    // 前面的普通文本
    if (match.index > lastIndex) {
      segments.push({
        type: "text",
        content: text.slice(lastIndex, match.index),
      });
    }

    if (match[1] === "anchor") {
      segments.push({
        type: "anchor-range",
        content: match[3] ?? "",
        anchorId: match[2],
      });
    } else {
      // point
      segments.push({
        type: "anchor-point",
        content: "",
        anchorId: match[2],
      });
    }

    lastIndex = pattern.lastIndex;
  }

  // 剩余文本
  if (lastIndex < text.length) {
    segments.push({
      type: "text",
      content: text.slice(lastIndex),
    });
  }

  return segments;
}

/**
 * 根据锚点 ID 获取对应的节点颜色。
 */
export function getAnchorColor(
  anchorId: string,
  anchors: NodeAnchor[],
  getNodeColorFn: (nodeId: string) => string,
): string {
  const anchor = anchors.find((a) => a.id === anchorId);
  if (!anchor) return "#9e9e9e";
  return getNodeColorFn(anchor.nodeId);
}

/**
 * 生成下一个点锚定的序号（①②③...）。
 * 使用 anchors 中 point 类型的数量 + 1。
 */
export function getNextPointIndex(anchors: NodeAnchor[]): number {
  const pointCount = anchors.filter((a) => a.type === "point").length;
  return pointCount + 1;
}

/**
 * 将数字转换为带圈数字（①②③...⑨⑩等）。
 * 超过 20 时回退为 [1] [2] 格式。
 */
export function toCircledNumber(n: number): string {
  if (n < 1) return "";
  if (n > 20) return `[${n}]`;
  // Unicode 带圈数字 ①=U+2460
  return String.fromCharCode(0x2460 + n - 1);
}

/**
 * 正文内容变更后，尝试用 textPreview 重新定位锚点。
 * 无法定位的标记为 invalid。
 */
export function relocateAnchors(
  anchors: NodeAnchor[],
  newPlainText: string,
): NodeAnchor[] {
  return anchors.map((anchor) => {
    if (anchor.type === "range" && anchor.textPreview) {
      const newStart = newPlainText.indexOf(anchor.textPreview);
      if (newStart >= 0) {
        return {
          ...anchor,
          start: newStart,
          end: newStart + anchor.textPreview.length,
          invalid: false,
        };
      }
    }
    // point 锚定无法自动重定位（位置语义已变）
    if (anchor.type === "point") {
      // 如果 start 仍在范围内，保留
      if (anchor.start >= 0 && anchor.start <= newPlainText.length) {
        return { ...anchor, invalid: false };
      }
    }
    return { ...anchor, invalid: true };
  });
}
