// Map route — 藏書地圖（library-map SDD M2；源頭 Arcrun#39）。
// 聚合 SQL 只准住基本盤（D6 推論，design §2 關鍵歸屬裁定）——本 route 是薄殼，SQL 全在
// actions/library-map.ts。auth 照 base route 既有慣例：raw worker 不驗 key，owner 隔離由
// cypher proxy（X-Arcrun-API-Key → owner_id 注入）／caller 帶 owner_id 參數完成。
import { Hono } from 'hono';
import type { Bindings } from '../types';
import {
  ensureFreshLibraryMaps,
  getLibraryMapDetail,
  listLibraryMaps,
  recomputeLibraryMap,
} from '../actions/library-map';

export const mapRoutes = new Hono<{ Bindings: Bindings }>();

// POST /map/recompute?library=X — 對該庫重算地圖（ingest 尾端／backfill 呼叫；R2 增量重算）。
// body（皆選填）：{ narrative, commit_hash, owner_id, source_prefix, triplet_template, top_n }
//   - narrative：本輪由 caller 傳入（wiki 首段抽取屬 ingest 端 M3，base 不抽取）。
//   - source_prefix：舊 triplet（無 library slot 值）的 source_uri 前綴 fallback（M3 backfill 前過渡）。
// 寫入順序安全：先建新 active map block，再把舊 block 標 superseded（見 action 註解）。
mapRoutes.post('/recompute', async (c) => {
  const body = (await c.req.json().catch(() => ({}))) as Record<string, unknown>;
  const library = c.req.query('library') || (typeof body.library === 'string' ? body.library : '');
  if (!library || !library.trim()) return c.json({ success: false, error: 'library required' }, 400);
  try {
    const result = await recomputeLibraryMap(c.env.DB, {
      library,
      narrative: typeof body.narrative === 'string' ? body.narrative : undefined,
      commit_hash: typeof body.commit_hash === 'string' ? body.commit_hash : undefined,
      owner_id: typeof body.owner_id === 'string' ? body.owner_id : c.req.query('owner_id') || undefined,
      source_prefix: typeof body.source_prefix === 'string' ? body.source_prefix : undefined,
      triplet_template: typeof body.triplet_template === 'string' ? body.triplet_template : undefined,
      top_n: typeof body.top_n === 'number' ? body.top_n : undefined,
    });
    return c.json({ success: true, ...result });
  } catch (e) {
    return c.json({ success: false, error: e instanceof Error ? e.message : String(e) }, 400);
  }
});

// GET /map — 全館地圖：每庫一行（library＋narrative＋top 3 entities＋triplet_count）。
// 形狀給 MCP instructions／GUI 首頁共用（R3/R4），設計在數百 token 內。
//
// 2026-08-08：讀前先 ensureFreshLibraryMaps（即時新鮮度層，見 actions/library-map.ts 段落註解）——
// 不再只讀靜態快取，讀的當下順手核對即時三元組數、落差就地補算。失敗吞掉不擋讀取（地圖是加分）。
mapRoutes.get('/', async (c) => {
  const owner = c.req.query('owner_id') || undefined;
  await ensureFreshLibraryMaps(c.env.DB, owner).catch(() => {});
  const libraries = await listLibraryMaps(c.env.DB, owner);
  return c.json({ success: true, libraries, count: libraries.length });
});

// GET /map/:library — 該庫詳圖（完整 slots＋可嵌人話 content）。
// 同樣先跑即時新鮮度層。之後仍查不到 → 誠實 404（這個名字這個租戶的資料裡從沒出現過，
// 不是「這庫是空的」——已知但目前 0 三元組的庫會被上一步補成一筆 triplet_count:0 的 map，
// 走得到 200，不會落到這條 404）。
mapRoutes.get('/:library', async (c) => {
  const owner = c.req.query('owner_id') || undefined;
  const library = c.req.param('library');
  await ensureFreshLibraryMaps(c.env.DB, owner).catch(() => {});
  const map = await getLibraryMapDetail(c.env.DB, library, owner);
  if (!map) return c.json({ success: false, error: 'not found' }, 404);
  return c.json({ success: true, map });
});
