function pushText(out: string[], value: unknown) {
  const s = typeof value === "string" ? value : null;
  if (!s) return;
  const trimmed = s.replace(/\s+/g, " ").trim();
  if (!trimmed) return;
  out.push(trimmed);
}

export function extractTextFromDocumentContent(content: unknown, maxChars = 60000): string {
  const out: string[] = [];

  const readPropText = (value: unknown) => {
    if (!value || typeof value !== "object") return null;
    const t = (value as Record<string, unknown>).text;
    return typeof t === "string" ? t : null;
  };

  const walk = (node: unknown) => {
    if (out.join("\n").length >= maxChars) return;

    if (node == null) return;
    if (typeof node === "string") {
      pushText(out, node);
      return;
    }
    if (typeof node === "number" || typeof node === "boolean") return;

    if (Array.isArray(node)) {
      for (const item of node) {
        walk(item);
        if (out.join("\n").length >= maxChars) return;
      }
      return;
    }

    if (typeof node === "object") {
      const obj = node as Record<string, unknown>;

      // 兼容常见 BlockNote 结构：
      // - block.props.text
      // - block.content: [{ text: "..." }, ...]
      // - block.children: [...]
      pushText(out, obj.text);
      pushText(out, readPropText(obj.props));

      const maybeContent = obj.content;
      if (Array.isArray(maybeContent)) {
        for (const seg of maybeContent) {
          if (typeof seg === "string") pushText(out, seg);
          else if (seg && typeof seg === "object") pushText(out, readPropText(seg));
          if (out.join("\n").length >= maxChars) return;
        }
      }

      const children = obj.children;
      if (Array.isArray(children)) {
        for (const c of children) {
          walk(c);
          if (out.join("\n").length >= maxChars) return;
        }
      }

      // 兜底：遍历其它字段（避免遗漏 title/caption 等）
      for (const [k, v] of Object.entries(obj)) {
        if (k === "children" || k === "content" || k === "props" || k === "text") continue;
        if (typeof v === "string") pushText(out, v);
        else if (Array.isArray(v) || (v && typeof v === "object")) walk(v);
        if (out.join("\n").length >= maxChars) return;
      }
    }
  };

  walk(content);

  const joined = out.join("\n").trim();
  return joined.length > maxChars ? `${joined.slice(0, maxChars)}…` : joined;
}

type MindmapNode = { data?: { text?: unknown }; children?: unknown[] };

export function extractTextFromMindmapData(data: unknown, maxChars = 60000): string {
  const out: string[] = [];

  const walk = (node: unknown) => {
    if (out.join("\n").length >= maxChars) return;
    if (!node || typeof node !== "object") return;
    const n = node as MindmapNode;
    pushText(out, n.data?.text);
    if (Array.isArray(n.children)) {
      for (const c of n.children) {
        walk(c);
        if (out.join("\n").length >= maxChars) return;
      }
    }
  };

  walk(data);

  const joined = out.join("\n").trim();
  return joined.length > maxChars ? `${joined.slice(0, maxChars)}…` : joined;
}
