/**
 * KBDB 資料層 MCP 薄殼（kbdb-base Phase 9.1，HANDOFF §2）
 *
 * rule 07 §5（薄殼鐵律）：能力長在 API，MCP 只做介面轉換 + 暴露，無業務邏輯。
 *
 * ── 2026-08-12：改用「登入進來的那個人的身分」查詢 ────────────────────────────
 * leo：「人類進 Portal 輸入帳密表示你是主人，可以查到你權限所有東西；AI 透過輸入帳密的
 *   MCP 查詢表示是授權的 AI，可以查到主人允許查的任何東西。」
 *   「掛上 MCP 並輸入帳密，那個動作本身就是授權」⇒ 下游不得再要求第二次認證。
 *
 * 之前的路：MCP 驗完帳密只留一個布林值 → 查詢時無身分可帶 → 只好帶**服務內部金鑰**
 * （KBDB_INTERNAL_TOKEN）直打 KBDB。那條路繞過所有庫過濾，而且不管誰登入都看到同一格。
 *
 * 現在的路（identity.kind === 'portal'）：帶登入者的 portal session 打 cypher
 * `/portal/data/*`——庫過濾／租戶注入／停用即時生效全在 server 側，與人類走 portal 網頁
 * 是**同一道閘、同一份權限**。MCP 這邊一個判斷都不做。
 *
 * 服務級憑據（static token / partner key，identity.kind === 'service'）維持既有 KBDB 直連，
 * 零回歸——那類憑據本身就是真祕密、代表整個實例或租戶，不是某個人。
 *
 * KBDB 鐵律（leo 2026-06-14，頂層 DECISION-kbdb-v3-baseplane.md）：
 *   - 任何人不准動表；**不提供建表 / SQL tool**。
 *   - AI 想存新類型的資料時只有「建 template（name+slots）+ 填 record（slot→content）」可用。
 *   - 薄殼只調 HTTP API，不直連 D1、不寫 SQL。
 */

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import type { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
import { errorResponse, successResponse } from "../lib/cypher-client.js";
import {
  portalFetch,
  portalError,
  staleIdentityError,
  type KnowledgeIdentity,
} from "../lib/portal-client.js";

/** 走 portal 資料面時，呼叫端傳的 owner_id 一律無效（server 用登入者的歸屬）——如實告訴 AI。 */
const OWNER_IGNORED_HINT =
  "owner_id 在登入身分下不生效：查詢範圍由你的帳號權限決定（與你在 portal 網頁看到的一致）";

/** 註冊全部 KBDB 資料層工具（kbdb-base Phase 9.1）。不含建表/SQL tool（鐵律）。 */
export function registerAllKbdbDataTools(server: McpServer, env: Env, identity: KnowledgeIdentity) {
  registerCreateTemplate(server, env, identity);
  registerListTemplates(server, env, identity);
  registerCreateRecord(server, env, identity);
  registerGetRecord(server, env, identity);
  registerQuery(server, env, identity);
  registerSearch(server, env, identity);
}

/**
 * kbdb_create_template — 建一個 template（= 萬用表裡的一種「虛擬表/資料形狀」）。
 * 這是 AI 想存「新類型資料」時的唯一入口：沒有建表 API，改用 template + slots 描述欄位。
 */
export function registerCreateTemplate(server: McpServer, env: Env, identity: KnowledgeIdentity) {
  server.tool(
    "kbdb_create_template",
    "建一個 KBDB template（萬用表裡的一種資料形狀，類 Supabase 的虛擬表）。KBDB 不能建真的資料表——" +
      "要存「新類型」的結構化資料時，就建一個 template 並用 slots 列出它的欄位名，之後用 kbdb_create_record 填值。" +
      "例：name='contact', slots=['name','email','phone']。",
    {
      name: z.string().min(1).describe("template 名稱（唯一識別，之後填 record 用這個名字），如 'contact' / 'note'"),
      slots: z.array(z.string().min(1)).min(1).describe("欄位名清單，如 ['name','email','phone']"),
      description: z.string().optional().describe("這個 template 用途的簡述（選填）"),
      created_by: z.string().optional().describe("建立者標記（選填；登入身分下由 server 記錄，不吃此值）"),
    },
    async ({ name, slots, description, created_by }) => {
      if (identity.kind === "stale") return staleIdentityError();
      try {
        const res =
          identity.kind === "portal"
            ? await portalFetch(env, identity.portal.session, "/portal/data/templates", {
                method: "POST",
                body: { name, slots, description },
              })
            : await kbdbFetch(env, "/templates", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({ name, slots, description, created_by }),
              });
        if (!res.ok) {
          if (identity.kind === "portal") return portalError(res, `建 template「${name}」`);
          return errorResponse("create_template_failed", `建 template 失敗`, ["檢查 name 是否重複", "確認 slots 是非空字串陣列"], await res.text().catch(() => ""));
        }
        const data = await res.json();
        return successResponse(data, [
          `template「${name}」已建。用 kbdb_create_record(template='${name}', values={...}) 填一筆資料`,
        ]);
      } catch (e) {
        return errorResponse("internal_error", e instanceof Error ? e.message : String(e), ["稍後重試"]);
      }
    },
  );
}

/** kbdb_list_templates — 列出所有已建的 template（看有哪些資料形狀可用）。 */
export function registerListTemplates(server: McpServer, env: Env, identity: KnowledgeIdentity) {
  server.tool(
    "kbdb_list_templates",
    "列出 KBDB 裡所有 template（已定義的資料形狀）。要存資料前先看有沒有現成 template 可用，沒有再 kbdb_create_template。",
    {},
    async () => {
      if (identity.kind === "stale") return staleIdentityError();
      try {
        const res =
          identity.kind === "portal"
            ? await portalFetch(env, identity.portal.session, "/portal/data/templates")
            : await kbdbFetch(env, "/templates");
        if (!res.ok) {
          if (identity.kind === "portal") return portalError(res, "列 template");
          return errorResponse("list_templates_failed", `列 template 失敗`, ["稍後重試"], await res.text().catch(() => ""));
        }
        const data = await res.json();
        return successResponse(data, [
          "每個 template 的 slots_json 是它的欄位清單",
          "填資料用 kbdb_create_record",
          "template 是全域共享的「資料形狀」定義（schema），不含任何人的內容——內容的權限在 record/entry 那層",
        ]);
      } catch (e) {
        return errorResponse("internal_error", e instanceof Error ? e.message : String(e), ["稍後重試"]);
      }
    },
  );
}

/** kbdb_create_record — 依某 template 填一筆 record（slot → 內容）。 */
export function registerCreateRecord(server: McpServer, env: Env, identity: KnowledgeIdentity) {
  server.tool(
    "kbdb_create_record",
    "依某 template 填一筆 record（一列資料）。values 是 {slot名: 內容}，slot 名要對得上 template 的 slots。" +
      "template 不存在會失敗——先 kbdb_list_templates 確認，或 kbdb_create_template 建一個。",
    {
      template: z.string().min(1).describe("template 的 name 或 id"),
      values: z.record(z.string()).describe("欄位內容 {slot名: 字串內容}，如 {name:'Leo', email:'leo@x.com'}"),
      owner_id: z.string().optional().describe("資料歸屬標記（選填；登入身分下一律由 server 定成你的歸屬，不吃此值）"),
    },
    async ({ template, values, owner_id }) => {
      if (identity.kind === "stale") return staleIdentityError();
      try {
        const res =
          identity.kind === "portal"
            ? await portalFetch(env, identity.portal.session, "/portal/data/records", {
                method: "POST",
                body: { template, values },
              })
            : await kbdbFetch(env, "/records", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({ template, values, owner_id }),
              });
        if (!res.ok) {
          if (identity.kind === "portal") return portalError(res, `填 record（template「${template}」）`);
          return errorResponse("create_record_failed", `填 record 失敗`, [
            `確認 template「${template}」存在（kbdb_list_templates）`,
            "values 的 slot 名要對得上 template 的 slots",
          ], await res.text().catch(() => ""));
        }
        const data = await res.json();
        return successResponse(data, [
          `已存入。用 kbdb_query(template='${template}') 列出此 template 的所有 record`,
          ...(identity.kind === "portal" ? [OWNER_IGNORED_HINT] : []),
        ]);
      } catch (e) {
        return errorResponse("internal_error", e instanceof Error ? e.message : String(e), ["稍後重試"]);
      }
    },
  );
}

/** kbdb_get_record — 用 record_id 取單筆 record。 */
export function registerGetRecord(server: McpServer, env: Env, identity: KnowledgeIdentity) {
  server.tool(
    "kbdb_get_record",
    "用 record_id 取一筆 record 的所有欄位內容。record_id 從 kbdb_create_record 回傳或 kbdb_query 列出取得。",
    {
      record_id: z.string().min(1).describe("record 的 id（rec_xxx）"),
    },
    async ({ record_id }) => {
      if (identity.kind === "stale") return staleIdentityError();
      try {
        const res =
          identity.kind === "portal"
            ? await portalFetch(env, identity.portal.session, `/portal/data/records/${encodeURIComponent(record_id)}`)
            : await kbdbFetch(env, `/records/${encodeURIComponent(record_id)}`);
        if (res.status === 404) {
          // 登入身分下，「不是你的」與「不存在」刻意同回 404（不洩存在性，portal 同一條紅線）。
          return errorResponse("not_found", `查無 record「${record_id}」（不存在，或不在你的權限範圍內）`, [
            "確認 record_id 正確",
            "用 kbdb_query 列出某 template 的 record 取 id",
          ]);
        }
        if (!res.ok) {
          if (identity.kind === "portal") return portalError(res, "取 record");
          return errorResponse("get_record_failed", `取 record 失敗`, ["稍後重試"], await res.text().catch(() => ""));
        }
        const data = await res.json();
        return successResponse(data);
      } catch (e) {
        return errorResponse("internal_error", e instanceof Error ? e.message : String(e), ["稍後重試"]);
      }
    },
  );
}

/** kbdb_query — 列出某 template 底下的所有 record（結構化查詢）。 */
export function registerQuery(server: McpServer, env: Env, identity: KnowledgeIdentity) {
  server.tool(
    "kbdb_query",
    "列出某 template 底下的所有 record（結構化查詢，按 template 取整批資料）。要按關鍵字找內容用 kbdb_search。",
    {
      template: z.string().min(1).describe("template 的 name 或 id"),
      owner_id: z.string().optional().describe("只取某歸屬的 record（選填；登入身分下不生效，範圍由你的權限決定）"),
    },
    async ({ template, owner_id }) => {
      if (identity.kind === "stale") return staleIdentityError();
      try {
        const res =
          identity.kind === "portal"
            ? await portalFetch(
                env,
                identity.portal.session,
                `/portal/data/records/by-template/${encodeURIComponent(template)}`,
              )
            : await kbdbFetch(
                env,
                `/records/by-template/${encodeURIComponent(template)}` +
                  (owner_id ? `?owner_id=${encodeURIComponent(owner_id)}` : ""),
              );
        if (!res.ok) {
          if (identity.kind === "portal") return portalError(res, `查詢 template「${template}」的 record`);
          return errorResponse("query_failed", `查詢 record 失敗`, [`確認 template「${template}」存在`], await res.text().catch(() => ""));
        }
        const data = await res.json();
        return successResponse(data, [
          "用 kbdb_get_record(record_id) 取單筆全文",
          "按關鍵字找內容改用 kbdb_search",
          ...(identity.kind === "portal" ? [OWNER_IGNORED_HINT] : []),
        ]);
      } catch (e) {
        return errorResponse("internal_error", e instanceof Error ? e.message : String(e), ["稍後重試"]);
      }
    },
  );
}

/**
 * kbdb_search — 對 entries 做搜尋。mode=keyword（D1 LIKE，基本盤永遠可用）或 semantic（向量，需開 embed 模組）。
 * 語義/關鍵字都在同一 KBDB MCP（用戶資料 RAG），不分散（issue #7 / D17 邊界）。
 * mode=semantic 但沒開 vectorize → base 自動降級 keyword + 回 capability_hint（發現閉環，叫 CC 幫開）。
 */
export function registerSearch(server: McpServer, env: Env, identity: KnowledgeIdentity) {
  server.tool(
    "kbdb_search",
    "搜尋 KBDB 內容。mode='keyword'（預設，D1 LIKE 關鍵字，基本盤永遠可用）或 'semantic'（AI 向量語義搜尋，" +
      "需先開 embed 模組）。語義沒開時會自動降級關鍵字並告訴你怎麼開。要按 template 取整批結構化資料用 kbdb_query。",
    {
      q: z.string().min(1).describe("搜尋關鍵字 / 語義查詢句"),
      owner_id: z.string().optional().describe("限定某歸屬範圍內搜（選填；登入身分下不生效，範圍由你的權限決定）"),
      source: z.string().optional().describe("只搜某來源（ingest source.uri，選填）"),
      mode: z.enum(["keyword", "semantic"]).optional().describe("keyword（預設）或 semantic（需開 vectorize）"),
    },
    async ({ q, owner_id, source, mode }) => {
      if (identity.kind === "stale") return staleIdentityError();
      try {
        let res: Response;
        if (identity.kind === "portal") {
          // /portal/data/search 只吃在權限範圍內「再收窄」的 filter；owner_id/library 由 server 定死。
          res = await portalFetch(env, identity.portal.session, "/portal/data/search", {
            query: { q, mode },
          });
        } else {
          const qs = new URLSearchParams({ q });
          if (owner_id) qs.set("owner_id", owner_id);
          if (source) qs.set("source", source);
          if (mode) qs.set("mode", mode);
          res = await kbdbFetch(env, `/entries/search?${qs.toString()}`);
        }
        if (!res.ok) {
          if (identity.kind === "portal") return portalError(res, "搜尋");
          return errorResponse("search_failed", `搜尋失敗`, ["稍後重試"], await res.text().catch(() => ""));
        }
        const data = (await res.json()) as { mode?: string; capability_hint?: string; note?: string };
        // base 回 capability_hint → 語義沒開、已降級 keyword。把它當 next-step 傳給 AI（發現閉環）。
        const hints =
          data.capability_hint
            ? [data.capability_hint, "要開：跟用戶確認後，CC 可代開（寫 config kbdb_embed:true + acr update）"]
            : data.mode === "semantic"
              ? ["mode:semantic = AI 向量語義搜尋"]
              : ["mode:keyword = D1 LIKE（基本盤）", "想要語義搜尋：mode='semantic'（需先開 vectorize）"];
        if (identity.kind === "portal") hints.push(OWNER_IGNORED_HINT);
        if (data.note) hints.push(data.note);
        return successResponse(data, hints);
      } catch (e) {
        return errorResponse("internal_error", e instanceof Error ? e.message : String(e), ["稍後重試"]);
      }
    },
  );
}
