type SupabaseRouteClient = {
  from: (table: string) => any;
};

export type DocsSupabaseClient = SupabaseRouteClient;

export type DocsToolContext = {
  userId: string;
};

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

const escapeIlike = (value: string) =>
  // 注意：PostgREST 的 or(...) 语法里逗号/括号有特殊含义，这里一并转义，避免解析失败
  value.replace(/[%_,()]/g, (m) => `\\${m}`);

const snippetAround = (text: string, q: string, maxLen: number) => {
  const s = String(text || "");
  const query = String(q || "").trim();
  if (!s) return "";
  const limit = Math.max(80, Math.min(2000, Math.floor(maxLen || 0) || 320));
  if (!query) return s.slice(0, limit);
  const lower = s.toLowerCase();
  const ql = query.toLowerCase();
  const idx = lower.indexOf(ql);
  if (idx === -1) return s.slice(0, limit);
  const start = Math.max(0, idx - Math.floor(limit / 3));
  const end = Math.min(s.length, start + limit);
  const head = start > 0 ? "…" : "";
  const tail = end < s.length ? "…" : "";
  return `${head}${s.slice(start, end)}${tail}`.trim();
};

const loadWorkspaceIds = async (supabase: DocsSupabaseClient, userId: string) => {
  const { data, error } = await supabase
    .from("workspace_members")
    .select("workspace_id")
    .eq("user_id", userId);
  if (error) {
    throw new Error(isRecord(error) && typeof error.message === "string" ? error.message : "读取工作区失败");
  }
  const rows = Array.isArray(data) ? data : [];
  return rows.map((r) => String((r as any)?.workspace_id ?? "")).filter(Boolean);
};

export const createDocsServerTools = (args: {
  supabase?: DocsSupabaseClient;
  ctx: DocsToolContext;
  allowedToolIds: Set<string>;
  // 说明：该文件现在主要服务于非 Convex 模式或兼容兜底；Convex 主链下 docs_* 已改由 Rust runtime 产出结果。
  // 说明：如果提供该能力，则走本地兼容 transport，不依赖 Supabase。
  searchDocs?: (args: {
    userId: string;
    query: string;
    limit: number;
    workspaceId: string | null;
    includeDeleted: boolean;
  }) => Promise<
    Array<{
      id: string;
      title: string;
      workspaceId: string;
      parentId: string | null;
      updatedAt: unknown;
      snippet: string;
    }>
  >;
  readDoc?: (args: {
    userId: string;
    documentId: string;
    maxChars: number;
    includeContent: boolean;
  }) => Promise<{
    ok: true;
    documentId: string;
    title: string;
    workspaceId: string;
    parentId: string | null;
    updatedAt: unknown;
    rawTextLength: number;
    rawText: string;
    content?: unknown;
  }>;
}) => {
  const run = async (toolId: string, toolArgs: Record<string, unknown>) => {
    if (!args.allowedToolIds.has(toolId)) {
      throw new Error(`工具未被允许：${toolId}`);
    }

    if (toolId === "docs_search") {
      const query = String(toolArgs.query ?? "").trim();
      if (!query) throw new Error("缺少 query");
      const limitRaw = Number(toolArgs.limit ?? 12);
      const limit = Math.max(1, Math.min(30, Number.isFinite(limitRaw) ? Math.floor(limitRaw) : 12));
      const workspaceId = String(toolArgs.workspaceId ?? "").trim() || null;
      const includeDeleted = Boolean(toolArgs.includeDeleted ?? false);

      if (args.searchDocs) {
        const results = await args.searchDocs({
          userId: args.ctx.userId,
          query,
          limit,
          workspaceId,
          includeDeleted,
        });
        return { ok: true, query, results };
      }

      if (!args.supabase) {
        throw new Error("缺少 Supabase 数据源（请在 Convex 模式下传入 searchDocs/readDoc）");
      }

      const wsIds = await loadWorkspaceIds(args.supabase, args.ctx.userId);
      const wsFilter = workspaceId ? [workspaceId] : wsIds;
      if (wsFilter.length === 0) return { ok: true, query, results: [] };

      const pattern = `%${escapeIlike(query)}%`;
      const q = args.supabase
        .from("documents")
        .select("id,title,workspace_id,parent_id,updated_at,raw_text,deleted_at")
        .in("workspace_id", wsFilter)
        .or(`title.ilike.${pattern},raw_text.ilike.${pattern}`)
        .order("updated_at", { ascending: false })
        .limit(limit);
      if (!includeDeleted) q.is("deleted_at", null);

      const { data, error } = await q;
      if (error) {
        throw new Error(isRecord(error) && typeof error.message === "string" ? error.message : "搜索文档失败");
      }
      const rows = Array.isArray(data) ? data : [];
      const results = rows.map((r) => {
        const id = String((r as any)?.id ?? "");
        const title = String((r as any)?.title ?? "");
        const raw = String((r as any)?.raw_text ?? "");
        const updatedAt = (r as any)?.updated_at ?? null;
        const wid = String((r as any)?.workspace_id ?? "");
        const pid = (r as any)?.parent_id ? String((r as any)?.parent_id) : null;
        return {
          id,
          title,
          workspaceId: wid,
          parentId: pid,
          updatedAt,
          snippet: snippetAround(raw, query, 360),
        };
      });

      return { ok: true, query, results };
    }

    if (toolId === "docs_read") {
      const documentId = String(toolArgs.documentId ?? "").trim();
      if (!documentId) throw new Error("缺少 documentId");
      const maxCharsRaw = Number(toolArgs.maxChars ?? 2500);
      const maxChars = Math.max(200, Math.min(20_000, Number.isFinite(maxCharsRaw) ? Math.floor(maxCharsRaw) : 2500));
      const includeContent = Boolean(toolArgs.includeContent ?? false);

      if (args.readDoc) {
        return await args.readDoc({
          userId: args.ctx.userId,
          documentId,
          maxChars,
          includeContent,
        });
      }

      if (!args.supabase) {
        throw new Error("缺少 Supabase 数据源（请在 Convex 模式下传入 searchDocs/readDoc）");
      }

      const { data, error } = await args.supabase
        .from("documents")
        .select(includeContent ? "id,title,raw_text,content,workspace_id,parent_id,updated_at" : "id,title,raw_text,workspace_id,parent_id,updated_at")
        .eq("id", documentId)
        .single();
      if (error) {
        throw new Error(isRecord(error) && typeof error.message === "string" ? error.message : "读取文档失败");
      }
      const title = isRecord(data) ? String(data.title ?? "") : "";
      const rawText = isRecord(data) ? String(data.raw_text ?? "") : "";
      const trimmed = rawText.length > maxChars ? `${rawText.slice(0, maxChars)}…` : rawText;
      const workspaceId = isRecord(data) ? String(data.workspace_id ?? "") : "";
      const parentId = isRecord(data) && data.parent_id ? String(data.parent_id) : null;
      const updatedAt = isRecord(data) ? (data as any).updated_at ?? null : null;
      const content = includeContent && isRecord(data) ? (data as any).content ?? null : null;

      return {
        ok: true,
        documentId,
        title,
        workspaceId,
        parentId,
        updatedAt,
        rawTextLength: rawText.length,
        rawText: trimmed,
        ...(includeContent ? { content } : {}),
      };
    }

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

  return { run };
};
