/**
 * Search-box.ai
 * Author: Simon-Pierre Boucher
 * Contact: contact@spboucher.ai
 * File: apps/web/components/ResearchGraph.tsx
 * Description: Signature "bloom" — the question expands into objectives and claims as real events arrive.
 */

"use client";

import type { Claim } from "@search-box/shared";

const W = 980;
const QX = 84;

function truncate(s: string, n: number): string {
  return s.length > n ? `${s.slice(0, n - 1)}…` : s;
}

const STATUS_COLOR: Record<string, string> = {
  supported: "var(--teal)",
  contradicted: "var(--accent)",
  uncertain: "var(--amber)",
  exploring: "var(--ink-faint)"
};

/**
 * Deterministic layout: objectives fan out from the question node; claims
 * bloom further right, each visually attached to an objective branch.
 * Every element appears because a real backend event arrived.
 */
export function ResearchGraph({
  objectives,
  claims,
  live,
  hasContradiction
}: {
  objectives: string[];
  claims: Claim[];
  live: boolean;
  hasContradiction: boolean;
}) {
  const nObj = objectives.length;
  const rows = Math.max(nObj, 1);
  const rowGap = Math.min(46, 190 / rows);
  const height = Math.max(150, rows * rowGap + 70);
  const qy = height / 2;

  const objY = (i: number) => qy + (i - (nObj - 1) / 2) * rowGap;
  const objX = 470;
  const claimX = 830;
  const claimY = (i: number) =>
    qy + (i - (claims.length - 1) / 2) * Math.min(52, (height - 60) / Math.max(claims.length, 1));

  return (
    <div className="bloom-wrap" role="img" aria-label="Research map: question, objectives and claims">
      <svg viewBox={`0 0 ${W} ${height}`} preserveAspectRatio="xMidYMid meet">
        {/* objective branches */}
        {objectives.map((o, i) => {
          const y = objY(i);
          return (
            <g key={`o${i}`}>
              <path
                className="rg-branch"
                style={{ animationDelay: `${i * 90}ms` }}
                d={`M${QX + 14},${qy} C ${QX + 170},${qy} ${objX - 190},${y} ${objX - 10},${y}`}
              />
              <circle className="rg-node" style={{ animationDelay: `${i * 90 + 250}ms` }} cx={objX} cy={y} r={4} fill="var(--ink)" />
              <text className="rg-label rg-sm" x={objX + 12} y={y + 3.5}>
                {truncate(o, 44)}
              </text>
            </g>
          );
        })}

        {/* claim blooms */}
        {claims.map((c, i) => {
          const anchor = nObj > 0 ? objY(i % nObj) : qy;
          const y = claimY(i);
          const color = STATUS_COLOR[c.status] ?? "var(--ink-faint)";
          return (
            <g key={c.id}>
              <path
                className="rg-twig"
                style={{ animationDelay: "120ms" }}
                d={`M${objX + 4},${anchor} C ${objX + 150},${anchor} ${claimX - 140},${y} ${claimX - 12},${y}`}
              />
              <circle className="rg-node" cx={claimX} cy={y} r={6} fill={color} opacity={0.18} style={{ animationDelay: "300ms" }} />
              <circle className="rg-node" cx={claimX} cy={y} r={Math.max(3, 5.5 * c.confidence)} fill={color} style={{ animationDelay: "300ms" }} />
              <text className="rg-label rg-sm" x={claimX + 12} y={y + 3.5} fill={color}>
                {truncate(c.text, 22)} · {Math.round(c.confidence * 100)}%
              </text>
            </g>
          );
        })}

        {/* the question node */}
        <g>
          {live && <circle className="rg-qring live" cx={QX} cy={qy} r={16} />}
          <circle className="rg-qring" cx={QX} cy={qy} r={11} />
          <circle className="rg-node" cx={QX} cy={qy} r={7} fill={hasContradiction ? "var(--accent)" : "var(--ink)"} />
          <text className="rg-label strong" x={QX - 8} y={qy + 30}>
            question
          </text>
        </g>
      </svg>
    </div>
  );
}
