/* ============================================================
   Eval Pulse — chart primitives + helpers (inline SVG)
   ============================================================ */
const { useState, useEffect, useRef, useMemo } = React;

/* ---------- score bands (DS semantic colors) ---------- */
const BAND_COLOR = { pass: "#16A34A", partial: "#EAB308", fail: "#DC2626" };
function band(v) { return v >= 0.85 ? "pass" : v >= 0.40 ? "partial" : "fail"; }
function bandColor(v) { return BAND_COLOR[band(v)]; }
function bandLabel(v) { return v >= 0.85 ? "Pass" : v >= 0.40 ? "Partial" : "Fail"; }

/* ---------- formatting ---------- */
const pct = (v) => Math.round(v * 100);
const pct1 = (v) => (v * 100).toFixed(1);
function signedPts(d) {
  const s = d > 0 ? "+" : d < 0 ? "\u2212" : "";
  return s + Math.abs(d * 100).toFixed(1);
}
function deltaTone(d, eps = 0.005) {
  if (d > eps) return "up";
  if (d < -eps) return "down";
  return "flat";
}

/* ---------- smooth path (Catmull-Rom -> bezier) ---------- */
function smoothLine(pts) {
  if (!pts.length) return "";
  if (pts.length < 2) return `M${pts[0].x},${pts[0].y}`;
  let d = `M${pts[0].x},${pts[0].y}`;
  for (let i = 0; i < pts.length - 1; i++) {
    const p0 = pts[i - 1] || pts[i];
    const p1 = pts[i];
    const p2 = pts[i + 1];
    const p3 = pts[i + 2] || p2;
    const c1x = p1.x + (p2.x - p0.x) / 6;
    const c1y = p1.y + (p2.y - p0.y) / 6;
    const c2x = p2.x - (p3.x - p1.x) / 6;
    const c2y = p2.y - (p3.y - p1.y) / 6;
    d += ` C${c1x.toFixed(2)},${c1y.toFixed(2)} ${c2x.toFixed(2)},${c2y.toFixed(2)} ${p2.x.toFixed(2)},${p2.y.toFixed(2)}`;
  }
  return d;
}

/* ============================================================
   Ring — Apple Activity-style circular progress
   ============================================================ */
function Ring({ size = 240, stroke = 22, progress = 0, color = "#FF4F17", trackColor, glow = true, children, delay = 200 }) {
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  const target = c * (1 - Math.max(0, Math.min(1, progress)));
  const arcRef = useRef(null);
  useEffect(() => {
    const el = arcRef.current;
    if (!el) return;
    el.style.strokeDashoffset = target;            // default: filled (safe when backgrounded)
    const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    if (document.visibilityState !== "visible" || reduce) return;
    let raf;
    const dur = 1250, t0 = performance.now();
    const ease = (x) => 1 - Math.pow(1 - x, 3);
    el.style.strokeDashoffset = c;                 // start empty, then sweep
    const tick = (now) => {
      const e = now - t0 - delay;
      if (e <= 0) { raf = requestAnimationFrame(tick); return; }
      const p = Math.min(1, e / dur);
      el.style.strokeDashoffset = c + (target - c) * ease(p);
      if (p < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, c, delay]);
  const gid = useMemo(() => "rg" + Math.random().toString(36).slice(2, 8), []);
  return (
    <div className="ring-wrap" style={{ width: size, height: size }}>
      <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} style={{ transform: "rotate(-90deg)" }}>
        <defs>
          <linearGradient id={gid} x1="0%" y1="0%" x2="100%" y2="100%">
            <stop offset="0%" stopColor={color} stopOpacity="0.85" />
            <stop offset="100%" stopColor={color} />
          </linearGradient>
        </defs>
        <circle cx={size / 2} cy={size / 2} r={r} fill="none"
          stroke={trackColor || "var(--ring-track)"} strokeWidth={stroke} />
        <circle ref={arcRef} cx={size / 2} cy={size / 2} r={r} fill="none"
          stroke={`url(#${gid})`} strokeWidth={stroke} strokeLinecap="round"
          strokeDasharray={c} strokeDashoffset={target}
          style={{ filter: glow ? `drop-shadow(0 0 6px ${color}55)` : "none" }} />
      </svg>
      <div className="ring-center">{children}</div>
    </div>
  );
}

/* ============================================================
   TrendChart — smooth area chart with scrub tooltip, 2 series
   ============================================================ */
function TrendChart({ series, show, height = 300, caption }) {
  const H = height;
  const padX = 18, padTop = 26, padBot = 34;
  const n = series.length;
  const [hover, setHover] = useState(null);
  const [W, setW] = useState(900);
  const wrapRef = useRef(null);
  useEffect(() => {
    if (!wrapRef.current) return;
    const ro = new ResizeObserver(entries => {
      const cw = entries[0].contentRect.width;
      if (cw > 0) setW(cw);
    });
    ro.observe(wrapRef.current);
    return () => ro.disconnect();
  }, []);

  const vals = [];
  series.forEach(p => { if (show.pr) vals.push(p.pr); if (show.sy) vals.push(p.sy); });
  let lo = Math.min(...vals), hi = Math.max(...vals);
  const span = Math.max(0.04, hi - lo);
  lo = Math.max(0, lo - span * 0.35);
  hi = Math.min(1, hi + span * 0.35);

  const X = (i) => padX + (i / Math.max(1, n - 1)) * (W - padX * 2);
  const Y = (v) => padTop + (1 - (v - lo) / (hi - lo)) * (H - padTop - padBot);

  const mk = (key) => series.map((p, i) => ({ x: X(i), y: Y(p[key]) }));
  const prPts = mk("pr"), syPts = mk("sy");
  const linePr = smoothLine(prPts), lineSy = smoothLine(syPts);
  const areaPr = linePr ? `${linePr} L${X(n - 1)},${H - padBot} L${X(0)},${H - padBot} Z` : "";

  function onMove(e) {
    const rect = wrapRef.current.getBoundingClientRect();
    const x = (e.clientX - rect.left) / rect.width * W;
    let i = Math.round(((x - padX) / (W - padX * 2)) * (n - 1));
    i = Math.max(0, Math.min(n - 1, i));
    setHover(i);
  }

  const hp = hover != null ? series[hover] : null;
  const hoverLeft = hover != null ? X(hover) : 0;
  const flip = hoverLeft > W * 0.6;

  return (
    <div className="trend-wrap" ref={wrapRef} style={{ height: H }}
      onPointerMove={onMove} onPointerLeave={() => setHover(null)}>
      <svg width={W} height={H} viewBox={`0 0 ${W} ${H}`} style={{ display: "block" }}>
        <defs>
          <linearGradient id="areaPr" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="#FF4F17" stopOpacity="0.22" />
            <stop offset="100%" stopColor="#FF4F17" stopOpacity="0" />
          </linearGradient>
        </defs>
        {/* faint baseline gridlines */}
        {[0.25, 0.5, 0.75].map((t, k) => {
          const gy = padTop + t * (H - padTop - padBot);
          return <line key={k} x1={padX} x2={W - padX} y1={gy} y2={gy} stroke="var(--grid)" strokeWidth="1" />;
        })}
        {show.pr && <path d={areaPr} fill="url(#areaPr)" />}
        {show.sy && <path d={lineSy} fill="none" stroke="#0052B4" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />}
        {show.pr && <path d={linePr} fill="none" stroke="#FF4F17" strokeWidth="3.5" strokeLinecap="round" strokeLinejoin="round" />}
        {/* scrub guide */}
        {hp && (
          <g>
            <line x1={X(hover)} x2={X(hover)} y1={padTop - 6} y2={H - padBot} stroke="var(--scrub)" strokeWidth="1.5" strokeDasharray="3 4" />
            {show.pr && <circle cx={X(hover)} cy={Y(hp.pr)} r="6" fill="#FF4F17" stroke="var(--card)" strokeWidth="3" />}
            {show.sy && <circle cx={X(hover)} cy={Y(hp.sy)} r="6" fill="#0052B4" stroke="var(--card)" strokeWidth="3" />}
          </g>
        )}
      </svg>
      {/* x labels */}
      <div className="trend-xlabels">
        {series.map((p, i) => (
          p.label ? <span key={i} style={{ position: "absolute", left: X(i), transform: "translateX(-50%)" }}>{p.label}</span> : null
        ))}
      </div>
      {/* tooltip */}
      {hp && (
        <div className={"trend-tip" + (flip ? " flip" : "")} style={{ left: hoverLeft }}>
          <div className="trend-tip-date">{hp.date}</div>
          {show.pr && <div className="trend-tip-row"><span className="dot" style={{ background: "#FF4F17" }}></span>Pass rate<b>{pct(hp.pr)}%</b></div>}
          {show.sy && <div className="trend-tip-row"><span className="dot" style={{ background: "#0052B4" }}></span>Synthesis<b>{hp.sy.toFixed(2)}</b></div>}
        </div>
      )}
    </div>
  );
}

/* ============================================================
   Sparkline — tiny smooth trend line
   ============================================================ */
function Sparkline({ points, color = "#FF4F17", width = 96, height = 32, fill = true }) {
  if (!points || points.length < 2) return <svg width={width} height={height} />;
  const lo = Math.min(...points), hi = Math.max(...points);
  const span = Math.max(0.001, hi - lo);
  const pad = 3;
  const X = (i) => pad + (i / (points.length - 1)) * (width - pad * 2);
  const Y = (v) => pad + (1 - (v - lo) / span) * (height - pad * 2);
  const pts = points.map((v, i) => ({ x: X(i), y: Y(v) }));
  const line = smoothLine(pts);
  const area = `${line} L${X(points.length - 1)},${height} L${X(0)},${height} Z`;
  const gid = useMemo(() => "sp" + Math.random().toString(36).slice(2, 8), []);
  return (
    <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`}>
      <defs>
        <linearGradient id={gid} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity="0.20" />
          <stop offset="100%" stopColor={color} stopOpacity="0" />
        </linearGradient>
      </defs>
      {fill && <path d={area} fill={`url(#${gid})`} />}
      <path d={line} fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
      <circle cx={X(points.length - 1)} cy={Y(points[points.length - 1])} r="2.6" fill={color} />
    </svg>
  );
}

Object.assign(window, {
  BAND_COLOR, band, bandColor, bandLabel,
  pct, pct1, signedPts, deltaTone, smoothLine,
  Ring, TrendChart, Sparkline
});
