// primitives.jsx — shared building blocks: score dials, gauges, radar,
// bars, pills, panels, stats, empty states, count-up hook.
(function () {
  const { useState, useEffect, useRef } = React;

  // ---- count-up hook (eases a number up when mounted/visible) -----------
  function useCountUp(target, dur = 1100, run = true) {
    const [v, setV] = useState(0);
    useEffect(() => {
      if (!run) { setV(target); return; }
      let raf, start, done = false;
      const reduce = matchMedia('(prefers-reduced-motion: reduce)').matches
        || document.documentElement.getAttribute('data-motion') === 'off';
      if (reduce) { setV(target); return; }
      const step = (t) => {
        if (start == null) start = t;
        const p = Math.min(1, (t - start) / dur);
        const e = 1 - Math.pow(1 - p, 3);
        setV(target * e);
        if (p < 1) raf = requestAnimationFrame(step); else done = true;
      };
      raf = requestAnimationFrame(step);
      // safety: guarantee the final value even if rAF is throttled/paused
      const fallback = setTimeout(() => { if (!done) setV(target); }, dur + 120);
      return () => { cancelAnimationFrame(raf); clearTimeout(fallback); };
    }, [target, run]);
    return v;
  }

  // tone by score → returns {cls, color}
  function tone(score) {
    if (score >= 75) return { cls: '', color: 'var(--lime)' };
    if (score >= 50) return { cls: 'is-amber', color: 'var(--gold)' };
    return { cls: 'is-coral', color: 'var(--coral)' };
  }
  window.scoreTone = tone;

  // ---- ScoreDial — big SVG ring with centred count-up -------------------
  function ScoreDial({ value, label, delta, size = 'xl', sub }) {
    const v = useCountUp(value);
    const t = tone(value);
    const R = 46, C = 2 * Math.PI * R;
    const off = C * (1 - v / 100);
    return (
      <div className={'dial dial--' + size}>
        <svg viewBox="0 0 100 100">
          <circle cx="50" cy="50" r={R} fill="none" stroke="var(--surface-2)" strokeWidth="7" />
          <circle cx="50" cy="50" r={R} fill="none" stroke={t.color} strokeWidth="7"
            strokeLinecap="round" strokeDasharray={C} strokeDashoffset={off}
            style={{ filter: 'drop-shadow(0 0 8px ' + t.color + ')', transition: 'stroke-dashoffset .2s linear' }} />
        </svg>
        <div className="center">
          <div className={'num ' + t.cls}>{Math.round(v)}</div>
          {label && <div className="lbl">{label}</div>}
          {delta != null && <div className={'delta ' + (delta >= 0 ? 'up' : 'dn')}>{delta >= 0 ? '+' : ''}{delta} since last</div>}
          {sub && <div className="lbl" style={{ marginTop: 6 }}>{sub}</div>}
        </div>
      </div>
    );
  }
  window.ScoreDial = ScoreDial;

  // ---- MiniGauge — small ring used in the 7-dimension grid --------------
  function MiniGauge({ value, name, k, why, onClick }) {
    const v = useCountUp(value, 900);
    const t = tone(value);
    const R = 42, C = 2 * Math.PI * R;
    const off = C * (1 - v / 100);
    return (
      <div className="js-gauge" onClick={onClick} role={onClick ? 'button' : null}>
        {onClick && <span className="lab-why-pill">Why <Icon name="arrow" size={11} /></span>}
        <div className="g-ring">
          <svg viewBox="0 0 100 100">
            <circle cx="50" cy="50" r={R} fill="none" stroke="var(--surface-2)" strokeWidth="8" />
            <circle cx="50" cy="50" r={R} fill="none" stroke={t.color} strokeWidth="8"
              strokeLinecap="round" strokeDasharray={C} strokeDashoffset={off} />
          </svg>
          <div className="g-num" style={{ color: t.color }}>{Math.round(v)}</div>
        </div>
        <div className="g-name">{name}</div>
        <div className="g-key">{k} · /100</div>
        {why && <div className="g-why">{why}</div>}
      </div>
    );
  }
  window.MiniGauge = MiniGauge;

  // ---- RadarChart — 6-axis spider of the score dimensions (data-art) ----
  function RadarChart({ dims, compare }) {
    const cx = 130, cy = 125, R = 96;
    const n = dims.length;
    const pt = (i, r) => {
      const a = (Math.PI * 2 * i) / n - Math.PI / 2;
      return [cx + Math.cos(a) * r, cy + Math.sin(a) * r];
    };
    const poly = (vals) => vals.map((val, i) => pt(i, R * val / 100).join(',')).join(' ');
    const rings = [25, 50, 75, 100];
    return (
      <svg className="js-radar" viewBox="0 0 260 250">
        {rings.map(r => (
          <polygon key={r} points={dims.map((_, i) => pt(i, R * r / 100).join(',')).join(' ')}
            fill="none" stroke="var(--line-soft)" strokeWidth="1" />
        ))}
        {dims.map((_, i) => { const [x, y] = pt(i, R); return <line key={i} x1={cx} y1={cy} x2={x} y2={y} stroke="var(--line-soft)" strokeWidth="1" />; })}
        {compare && (
          <polygon points={poly(compare.map(d => d.value))} fill="rgba(123,220,140,0.06)" stroke="var(--mute)" strokeWidth="1.5" strokeDasharray="4 4" />
        )}
        <polygon points={poly(dims.map(d => d.value))} fill="rgba(232,181,60,0.14)" stroke="var(--gold)" strokeWidth="2"
          style={{ filter: 'drop-shadow(0 0 8px var(--gold-glow))' }} />
        {dims.map((d, i) => { const [x, y] = pt(i, R * d.value / 100); return <circle key={i} cx={x} cy={y} r="3.5" fill="var(--gold)" />; })}
        {dims.map((d, i) => {
          const [x, y] = pt(i, R + 18);
          return <text key={i} x={x} y={y} fill="var(--ink-soft)" fontSize="9.5" fontFamily="var(--font-mono)"
            textAnchor={Math.abs(x - cx) < 8 ? 'middle' : x > cx ? 'start' : 'end'} dominantBaseline="middle">{d.short}</text>;
        })}
      </svg>
    );
  }
  window.RadarChart = RadarChart;

  // ---- ScoreBar ----------------------------------------------------------
  function ScoreBar({ name, value, kind }) {
    const [w, setW] = useState(0);
    useEffect(() => { const id = setTimeout(() => setW(value), 60); return () => clearTimeout(id); }, [value]);
    const fill = kind || (value >= 75 ? '' : value >= 50 ? 'fill--gold' : 'fill--coral');
    return (
      <div className="score-bar">
        <div className="score-bar-head"><span className="score-bar-name">{name}</span><span className="score-bar-val">{value}</span></div>
        <div className="track"><div className={'fill ' + fill} style={{ width: w + '%' }} /></div>
      </div>
    );
  }
  window.ScoreBar = ScoreBar;

  // ---- Pill --------------------------------------------------------------
  function Pill({ children, tone, dot }) {
    const cls = tone === 'green' ? 'pill--green' : tone === 'coral' ? 'pill--coral' : tone === 'amber' ? 'pill--amber' : '';
    return <span className={'pill ' + cls}>{dot !== false && <span className="dot" />}{children}</span>;
  }
  window.Pill = Pill;

  // ---- Sparkline ---------------------------------------------------------
  function Sparkline({ data, w = 240, h = 60, accent = 'var(--lime)' }) {
    const vals = data.map(d => d.total);
    const min = Math.min(...vals) - 4, max = Math.max(...vals) + 4;
    const X = i => (i / (data.length - 1)) * w;
    const Y = v => h - ((v - min) / (max - min)) * h;
    const line = vals.map((v, i) => `${X(i)},${Y(v)}`).join(' ');
    const area = `0,${h} ${line} ${w},${h}`;
    return (
      <svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} preserveAspectRatio="none">
        <defs><linearGradient id="spark" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={accent} stopOpacity="0.28" /><stop offset="100%" stopColor={accent} stopOpacity="0" />
        </linearGradient></defs>
        <polygon points={area} fill="url(#spark)" />
        <polyline points={line} fill="none" stroke={accent} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
        {vals.map((v, i) => <circle key={i} cx={X(i)} cy={Y(v)} r={i === vals.length - 1 ? 3.5 : 0} fill={accent} />)}
      </svg>
    );
  }
  window.Sparkline = Sparkline;

  // ---- MicroSpark — tiny trend line from a raw number[] -----------------
  function MicroSpark({ values, w = 132, h = 38, accent = 'var(--gold)', area = true }) {
    const min = Math.min(...values), max = Math.max(...values);
    const span = (max - min) || 1;
    const pad = 3;
    const X = i => pad + (i / (values.length - 1)) * (w - pad * 2);
    const Y = v => h - pad - ((v - min) / span) * (h - pad * 2);
    const line = values.map((v, i) => `${X(i).toFixed(1)},${Y(v).toFixed(1)}`).join(' ');
    const areaPts = `${X(0)},${h} ${line} ${X(values.length - 1)},${h}`;
    const last = values.length - 1;
    const gid = 'ms' + Math.random().toString(36).slice(2, 8);
    return (
      <svg viewBox={`0 0 ${w} ${h}`} width="100%" height={h} preserveAspectRatio="none" style={{ display: 'block' }}>
        {area && <defs><linearGradient id={gid} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={accent} stopOpacity="0.24" /><stop offset="100%" stopColor={accent} stopOpacity="0" />
        </linearGradient></defs>}
        {area && <polygon points={areaPts} fill={`url(#${gid})`} />}
        <polyline points={line} fill="none" stroke={accent} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
        <circle cx={X(last)} cy={Y(values[last])} r="2.6" fill={accent} />
      </svg>
    );
  }
  window.MicroSpark = MicroSpark;

  // accent token resolver shared by trend small-multiples
  window.accentVar = a => ({ gold: 'var(--gold)', lime: 'var(--lime)', coral: 'var(--coral)', cool: '#6FB0FF' }[a] || 'var(--gold)');

  // ---- Panel + PanelHead -------------------------------------------------
  function Panel({ children, raised, className, id, style }) {
    return <section id={id} className={'js-panel ' + (raised ? 'js-panel--raised ' : '') + (className || '')} style={style}>{children}</section>;
  }
  function PanelHead({ eyebrow, title, desc, children }) {
    return (
      <div className="js-panelhead">
        <div>
          {eyebrow && <p className="eyebrow">{eyebrow}</p>}
          <h3 dangerouslySetInnerHTML={{ __html: title }} />
          {desc && <p className="pdesc">{desc}</p>}
        </div>
        {children && <div className="js-head-actions">{children}</div>}
      </div>
    );
  }
  window.Panel = Panel; window.PanelHead = PanelHead;

  // ---- Stat strip --------------------------------------------------------
  function Stat({ k, v, sub, dir, icon }) {
    return (
      <div className="js-stat">
        <div className="k">{icon && <Icon name={icon} size={14} />}{k}</div>
        <div className="v">{v}</div>
        {sub && <div className="sub">{dir && <span className={dir === 'down' ? 'dn' : 'up'}>{dir === 'down' ? '▾' : dir === 'flat' ? '·' : '▴'}</span>}{sub}</div>}
      </div>
    );
  }
  window.Stat = Stat;

  // ---- EmptyState --------------------------------------------------------
  function EmptyState({ icon, title, desc, action }) {
    return (
      <div className="js-empty">
        <div className="e-ico"><Icon name={icon || 'sparkles'} size={26} /></div>
        <h4>{title}</h4>
        {desc && <p>{desc}</p>}
        {action}
      </div>
    );
  }
  window.EmptyState = EmptyState;

  // ---- Button ------------------------------------------------------------
  function Btn({ children, kind = 'primary', icon, iconRight, sm, lg, onClick, href, disabled }) {
    const cls = 'btn ' + (kind === 'ghost' ? 'btn--ghost ' : kind === 'gold' ? 'btn--gold ' : '') + (sm ? 'btn--sm ' : '') + (lg ? 'btn--lg ' : '');
    const inner = <>{icon && <Icon name={icon} size={sm ? 14 : 16} />}{children}{iconRight && <Icon name={iconRight} size={sm ? 14 : 16} />}</>;
    if (href) return <a className={cls} href={href} onClick={onClick} style={disabled ? { opacity: .5, pointerEvents: 'none' } : null}>{inner}</a>;
    return <button className={cls} onClick={onClick} disabled={disabled} style={disabled ? { opacity: .5 } : null}>{inner}</button>;
  }
  window.Btn = Btn;

  // ---- RegretBadge (LOW/MED/HIGH) ---------------------------------------
  function RegretBadge({ level }) {
    const t = level === 'LOW' ? 'green' : level === 'HIGH' ? 'coral' : 'amber';
    return <Pill tone={t}>{level} regret</Pill>;
  }
  window.RegretBadge = RegretBadge;

})();
