// graph-chat-panel.jsx — QA streaming panel above the compose bar.
// Renders the user's question + a live-streaming LLM answer, with [[uuid]]
// tokens in the answer parsed into clickable chips. Chips click → selectById
// (focuses the camera + opens the drawer for that node).

const { useMemo: useMemoQP, useEffect: useEffectQP, useRef: useRefQP } = React;

function GraphChatPanel(props) {
  const { qa, graphData, onClose, onChipClick } = props;
  const scrollRef = useRefQP(null);

  // Build name lookup once per graphData change.
  const nodeById = useMemoQP(() => {
    const m = new Map();
    for (const n of (graphData && graphData.nodes) || []) m.set(n.id, n);
    return m;
  }, [graphData && graphData.nodes]);

  // Parse the streaming answer into a sequence of text/chip parts.
  // Two token forms accepted:
  //   - `[[n12]]`     — short alias from buildQAContext; resolve via aliasMap.
  //   - `[[uuid]]`    — raw UUID, used as a fallback.
  const parts = useMemoQP(() => {
    const text = (qa && qa.answer) || "";
    if (!text) return [];
    const aliasMap = (qa && qa.aliasMap) || {};
    const re = /\[\[(n\d{1,4}|[0-9a-fA-F\-]{8,40})\]\]/g;
    const out = [];
    let last = 0;
    let m;
    while ((m = re.exec(text)) !== null) {
      if (m.index > last) out.push({ kind: "text", text: text.slice(last, m.index) });
      const token = m[1];
      const uuid = /^n\d+$/.test(token) ? aliasMap[token] : token;
      out.push({ kind: "chip", uuid: uuid || null, node: uuid ? (nodeById.get(uuid) || null) : null });
      last = re.lastIndex;
    }
    if (last < text.length) out.push({ kind: "text", text: text.slice(last) });
    return out;
  }, [qa && qa.answer, qa && qa.aliasMap, nodeById]);

  // Auto-scroll to bottom while streaming, but only if user is already near
  // the bottom (so they can scroll up to read without being yanked down).
  useEffectQP(() => {
    const el = scrollRef.current;
    if (!el) return;
    const distFromBottom = el.scrollHeight - (el.scrollTop + el.clientHeight);
    if (distFromBottom < 80) el.scrollTop = el.scrollHeight;
  }, [qa && qa.answer]);

  if (!qa) return null;

  return (
    <div className="qa-panel" onMouseDown={(e) => e.stopPropagation()}>
      <div className="qa-head">
        <span className="qa-q-pill">问</span>
        <span className="qa-q">{qa.question}</span>
        {qa.loading && <span className="qa-loading-dot" />}
        <button className="qa-close" onClick={onClose} title="关闭">×</button>
      </div>
      <div className="qa-body" ref={scrollRef}>
        {parts.length === 0 && qa.loading && (
          <span className="qa-typing">思考中…</span>
        )}
        {parts.map((p, i) => p.kind === "text" ? (
          <React.Fragment key={i}>{p.text}</React.Fragment>
        ) : (
          <span
            key={i}
            className={"qa-chip" + (p.node ? "" : " missing")}
            onClick={() => p.node && onChipClick && onChipClick(p.uuid)}
            title={p.node
              ? (p.node.domainPath || p.node.name)
              : (p.uuid ? `节点 ${String(p.uuid).slice(0, 8)}… 不在当前图谱内` : "未知节点引用")}
          >
            {p.node ? (p.node.name || p.node.nameEn || "(node)") : "?"}
          </span>
        ))}
        {qa.loading && parts.length > 0 && <span className="qa-caret">▍</span>}
        {qa.error && <div className="qa-error">{qa.error}</div>}
      </div>
      {qa.citations && qa.citations.length > 0 && (
        <div className="qa-citations">
          <span className="qa-cite-label">来源</span>
          {qa.citations.slice(0, 5).map((c, i) => (
            <a key={i} href={c.url} target="_blank" rel="noreferrer" className="qa-cite" title={c.url}>
              {c.title || c.url}
            </a>
          ))}
        </div>
      )}
      {qa.usage && (
        <div className="qa-usage" title="prompt / completion / total tokens · elapsed">
          <span>{qa.model || "model"}</span>
          <span className="dot">·</span>
          <span>{qa.usage.prompt_tokens}↑ {qa.usage.completion_tokens}↓ {qa.usage.total_tokens} tok</span>
          {typeof qa.elapsedMs === "number" && (
            <>
              <span className="dot">·</span>
              <span>{(qa.elapsedMs / 1000).toFixed(1)}s</span>
            </>
          )}
          {qa.region && (
            <>
              <span className="dot">·</span>
              <span>{qa.region}</span>
            </>
          )}
        </div>
      )}
    </div>
  );
}

window.GraphChatPanel = GraphChatPanel;
