import { createDocBlockOpsAdapter } from "./blockOpsAdapter";

export const DOC_TOOL_IDS = ["doc_get", "doc_find", "doc_insert_blocks", "doc_replace_range"] as const;

export type DocToolId = (typeof DOC_TOOL_IDS)[number];

export const isDocToolId = (toolId: string): toolId is DocToolId =>
  (DOC_TOOL_IDS as readonly string[]).includes(toolId);

export const getDocToolInvocationKind = (toolId: DocToolId) =>
  toolId === "doc_get" || toolId === "doc_find" ? "query" : "command";

export type DocToolContext = {
  documentId: string;
  userId: string;
  /**
   * 来自前端的“最新文档快照”（优先使用，避免覆盖用户尚未落盘的编辑）。
   * 允许是 blocks 数组，或 { blocks } 结构。
   */
  baseBlocks?: unknown;
};

type SupabaseRouteClient = {
  from: (table: string) => {
    select: (columns: string) => SupabaseQuery;
    update: (values: Record<string, unknown>) => SupabaseUpdateQuery;
  };
};

type SupabaseQuery = {
  eq: (column: string, value: unknown) => SupabaseQuery;
  single: () => Promise<{ data: unknown; error: unknown }>;
};

type SupabaseUpdateQuery = {
  eq: (column: string, value: unknown) => SupabaseUpdateQuery;
};

export type DocSupabaseClient = SupabaseRouteClient;

type DocBlockSummary = {
  id: string;
  type: string;
  text: string;
  depth: number;
  childCount: number;
};

const isRecord = (v: unknown): v is Record<string, unknown> =>
  typeof v === "object" && v !== null && !Array.isArray(v);

const getValue = (obj: unknown, key: string): unknown => (isRecord(obj) ? obj[key] : undefined);

const normalizeBlocks = (content: unknown): unknown[] => {
  if (Array.isArray(content)) return content;
  const blocks = getValue(content, "blocks");
  if (Array.isArray(blocks)) return blocks;
  return [];
};

const extractInlineText = (block: unknown): string => {
  const content = getValue(block, "content");
  const nodes = Array.isArray(content) ? content : [];
  const pieces: string[] = [];
  for (const n of nodes) {
    const t = getValue(n, "text");
    if (typeof t === "string") pieces.push(t);
  }
  return pieces.join("").trim();
};

const walkSummaries = (rootBlocks: unknown[], maxNodes: number): DocBlockSummary[] => {
  const list: DocBlockSummary[] = [];
  const queue: Array<{ block: unknown; depth: number }> = rootBlocks.map((b) => ({ block: b, depth: 0 }));
  while (queue.length > 0 && list.length < maxNodes) {
    const item = queue.shift();
    if (!item) break;
    const { block, depth } = item;
    const id = String(getValue(block, "id") ?? "").trim();
    const type = String(getValue(block, "type") ?? "").trim();
    const childrenRaw = getValue(block, "children");
    const children = Array.isArray(childrenRaw) ? childrenRaw : [];
    const text = extractInlineText(block);
    if (id) {
      list.push({ id, type: type || "unknown", text, depth, childCount: children.length });
    }
    children.forEach((c) => queue.push({ block: c, depth: depth + 1 }));
  }
  return list;
};

const loadDocumentBlocks = async (supabase: DocSupabaseClient, ctx: DocToolContext) => {
  const base = normalizeBlocks(ctx.baseBlocks);
  if (base.length > 0) {
    return { blocks: base, source: "client" as const };
  }

  const { data, error } = await supabase
    .from("documents")
    .select("id,content")
    .eq("id", ctx.documentId)
    .eq("user_id", ctx.userId)
    .single();
  if (error) {
    throw new Error(isRecord(error) && typeof error.message === "string" ? error.message : "读取文档失败");
  }
  const content = isRecord(data) ? data.content : null;
  const blocks = normalizeBlocks(content);
  return { blocks, source: "db" as const };
};

export const createDocServerTools = (args: {
  supabase?: DocSupabaseClient;
  ctx: DocToolContext;
  allowedToolIds: Set<string>;
  // 说明：Convex 迁移阶段用于“去 Supabase 化”。如果提供该能力，则完全不依赖 Supabase。
  loadBlocks?: () => Promise<{ blocks: unknown[]; source: string }>;
}) => {
  const blockOps = createDocBlockOpsAdapter({
    baseBlocks: args.ctx.baseBlocks,
    loadBlocks: args.loadBlocks,
  });

  const loadBlocks = async () => {
    if (args.loadBlocks) return await args.loadBlocks();
    if (!args.supabase) throw new Error("缺少 Supabase 数据源（请在 Convex 模式下传入 loadBlocks/saveBlocks）");
    return await loadDocumentBlocks(args.supabase, args.ctx);
  };

  const run = async (toolId: string, toolArgs: Record<string, unknown>) => {
    if (!args.allowedToolIds.has(toolId)) {
      throw new Error(`工具未被允许：${toolId}`);
    }

    if (toolId === "doc_get") {
      const maxNodesRaw = Number(toolArgs.maxBlocks ?? 80);
      const maxBlocks = Math.max(10, Math.min(240, Number.isFinite(maxNodesRaw) ? Math.floor(maxNodesRaw) : 80));
      const { blocks, source } = await loadBlocks();
      const summary = walkSummaries(blocks, maxBlocks);
      return { ok: true, source, totalTopLevelBlocks: blocks.length, blocks: summary };
    }

    if (toolId === "doc_find") {
      const query = String(toolArgs.query ?? "").trim();
      if (!query) throw new Error("缺少 query");
      const maxRaw = Number(toolArgs.maxResults ?? 8);
      const maxResults = Math.max(1, Math.min(30, Number.isFinite(maxRaw) ? Math.floor(maxRaw) : 8));
      const { blocks, source } = await loadBlocks();
      const summary = walkSummaries(blocks, 400);
      const q = query.toLowerCase();
      const hits = summary.filter((x) => x.text.toLowerCase().includes(q)).slice(0, maxResults);
      return { ok: true, source, query, results: hits };
    }

    if (toolId === "doc_insert_blocks") {
      const afterBlockId = String(toolArgs.afterBlockId ?? "").trim();
      const beforeBlockId = String(toolArgs.beforeBlockId ?? "").trim();
      const specsRaw = toolArgs.blocks;
      if (!Array.isArray(specsRaw) || specsRaw.length === 0) throw new Error("缺少 blocks");
      if (specsRaw.length > 20) throw new Error("blocks 过多（最多 20）");

      const specs = specsRaw.map((x) => {
        const t = isRecord(x) ? String(x.type ?? "paragraph") : "paragraph";
        const text = isRecord(x) ? String(x.text ?? "") : "";
        const level = isRecord(x) ? Number(x.level ?? 2) : 2;
        return {
          type: (t === "heading" ? "heading" : "paragraph") as "heading" | "paragraph",
          text,
          level,
        };
      });

      const { data, inserted, source } = await blockOps.insertBlocks({
        afterBlockId: afterBlockId || undefined,
        beforeBlockId: beforeBlockId || undefined,
        blocks: specs,
      });
      return {
        ok: true,
        source,
        inserted,
        data,
      };
    }

    if (toolId === "doc_replace_range") {
      const blockId = String(toolArgs.blockId ?? "").trim();
      const text = String(toolArgs.text ?? "").trim();
      if (!blockId) throw new Error("缺少 blockId");
      if (!text) throw new Error("缺少 text");
      const modeRaw = String(toolArgs.mode ?? "replace").trim();
      const mode = modeRaw === "append" || modeRaw === "prepend" ? modeRaw : "replace";

      const { data, source } = await blockOps.replaceRange({
        blockId,
        text,
        mode,
      });
      return { ok: true, source, blockId, mode, data };
    }

    throw new Error(`未知工具：${toolId}`);
  };

  return { run };
};
