// ═══════════════════════════════════════════════════════════
//  graph.js — 從知識庫筆記內 [[wikilink]] 建築節點與邊（給圖譜視圖）
// ═══════════════════════════════════════════════════════════
const WIKI_RE = /\[\[([^\]]+)\]\]/g;

const KIND_META = {
  overview: { color: '#0071e3', shape: 'box' },
  principleMap: { color: '#5856d6', shape: 'box' },
  quiz: { color: '#ff2d55', shape: 'box' },
  category: { color: '#0071e3', shape: 'box' },
  case: { color: '#34c759', shape: 'dot' },
  principle: { color: '#af52de', shape: 'dot' },
  term: { color: '#ff9500', shape: 'dot' },
  company: { color: '#5ac8fa', shape: 'dot' },
  episode: { color: '#8e8e93', shape: 'dot' },
};

function nodeKey(hit) { return `${hit.kind}:${hit.id}`; }

function resolveLink(linkMap, target) {
  const t = (target || '').trim();
  return linkMap[t] || linkMap[t.split('#').pop()] || linkMap[t.split('/').pop()] || null;
}

function shortLabel(title, max = 28) {
  const s = String(title || '');
  return s.length > max ? s.slice(0, max - 1) + '…' : s;
}

export function buildGraph(knowledge, opts = {}) {
  const linkMap = knowledge.linkMap || {};
  const includeIndex = opts.includeIndex === '1' || opts.includeIndex === true;
  const kinds = opts.kinds ? new Set(String(opts.kinds).split(',')) : null;
  const limit = Math.min(Number(opts.limit) || 450, 800);
  const center = opts.center ? String(opts.center) : null;
  const depth = Math.min(Math.max(Number(opts.depth) || 2, 1), 4);

  const nodes = new Map();
  const edgeSet = new Set();
  const edges = [];

  const addNode = (hit) => {
    if (!hit) return null;
    if (kinds && !kinds.has(hit.kind)) return null;
    const id = nodeKey(hit);
    if (nodes.size >= limit && !nodes.has(id)) return null;
    if (!nodes.has(id)) {
      const meta = KIND_META[hit.kind] || { color: '#8e8e93', shape: 'dot' };
      nodes.set(id, {
        id, label: shortLabel(hit.title || hit.id),
        title: hit.title || hit.id,
        kind: hit.kind,
        color: meta.color,
        shape: meta.shape,
      });
    }
    return id;
  };

  const addEdge = (from, to) => {
    if (!from || !to || from === to) return;
    const k = `${from}|${to}`;
    if (edgeSet.has(k)) return;
    edgeSet.add(k);
    edges.push({ from, to });
  };

  const parseBody = (body, fromId) => {
    if (!body || !fromId) return;
    let m;
    WIKI_RE.lastIndex = 0;
    while ((m = WIKI_RE.exec(body)) !== null) {
      const hit = resolveLink(linkMap, m[1]);
      const toId = addNode(hit);
      if (toId) addEdge(fromId, toId);
    }
  };

  const seed = (hit, body) => {
    const id = addNode(hit);
    if (id && body) parseBody(body, id);
    return id;
  };

  if (knowledge.overview) seed({ kind: 'overview', id: knowledge.overview.id, title: knowledge.overview.title }, knowledge.overview.body);
  if (knowledge.principleMap) seed({ kind: 'principleMap', id: knowledge.principleMap.id, title: knowledge.principleMap.title }, knowledge.principleMap.body);
  if (knowledge.quiz) seed({ kind: 'quiz', id: knowledge.quiz.id, title: knowledge.quiz.title }, knowledge.quiz.body);

  for (const c of (knowledge.categories || [])) seed({ kind: 'category', id: c.id, title: c.title }, c.body);
  for (const c of (knowledge.cases || [])) seed({ kind: 'case', id: c.id, title: c.title }, c.body);
  for (const p of (knowledge.principles || [])) seed({ kind: 'principle', id: p.id, title: p.title }, p.body);

  if (includeIndex) {
    for (const x of (knowledge.index || [])) addNode({ kind: x.kind, id: x.id, title: x.title });
  }

  // 以某節點為中心：BFS 保留 depth 層內的節點與邊
  if (center && nodes.has(center)) {
    const keep = new Set([center]);
    let frontier = [center];
    for (let d = 0; d < depth; d++) {
      const next = [];
      for (const e of edges) {
        if (frontier.includes(e.from) && nodes.has(e.to)) { keep.add(e.to); next.push(e.to); }
        if (frontier.includes(e.to) && nodes.has(e.from)) { keep.add(e.from); next.push(e.from); }
      }
      frontier = [...new Set(next)];
    }
    const filtEdges = edges.filter(e => keep.has(e.from) && keep.has(e.to));
    const filtNodes = [...nodes.values()].filter(n => keep.has(n.id));
    return { nodes: filtNodes, edges: filtEdges, center, depth, total: filtNodes.length };
  }

  return { nodes: [...nodes.values()], edges, total: nodes.size };
}
