// Reusable primitives — money formatting, deltas, sparklines, mini charts, icons

const { useState, useEffect, useRef, useMemo, useLayoutEffect } = React;

// ── Format helpers ──────────────────────────────────────────────
function fmtGBP(v, decimals){
  if (v == null) return "—";
  const abs = Math.abs(v);
  let d = decimals;
  if (d == null) d = abs >= 1 ? 2 : abs >= 0.01 ? 4 : 6;
  const sign = v < 0 ? "-" : "";
  return sign + "£" + abs.toFixed(d);
}
function fmtUSD(v, decimals){
  if (v == null) return "—";
  const abs = Math.abs(v);
  let d = decimals;
  if (d == null) d = abs >= 1 ? 2 : abs >= 0.01 ? 4 : 6;
  const sign = v < 0 ? "-" : "";
  return sign + "$" + abs.toFixed(d);
}
function fmtInt(n){ if (n==null) return "—"; return n.toLocaleString(); }
function fmtPct(n, d=1){ return (n*100).toFixed(d)+"%" }
function fmtTokens(n){
  if (n==null) return "—";
  if (n >= 1e6) return (n/1e6).toFixed(2)+"M";
  if (n >= 1e3) return (n/1e3).toFixed(1)+"k";
  return n.toLocaleString();
}

// "£97.01" with subtle visual hierarchy — dim £, emphasize number
function Money({ value, currency="gbp", size="md", strong=true }){
  if (value == null) return <span className="mono" style={{ color:"var(--fg-3)" }}>—</span>;
  const sym = currency==="usd" ? "$" : "£";
  const abs = Math.abs(value);
  const d = abs >= 1 ? 2 : abs >= 0.01 ? 4 : 6;
  const sign = value < 0 ? "-" : "";
  const sizes = { sm:{fs:12,fw:500}, md:{fs:14,fw:500}, lg:{fs:22,fw:600}, xl:{fs:34,fw:600}, hero:{fs:44,fw:600} };
  const s = sizes[size] || sizes.md;
  return (
    <span className="mono" style={{ fontSize:s.fs, fontWeight:s.fw, color: strong ? "var(--fg-0)" : "var(--fg-1)", whiteSpace:"nowrap", letterSpacing:size==="xl"||size==="hero"?-0.5:-0.2 }}>
      <span style={{ color:"var(--fg-3)", fontWeight:400, marginRight:1 }}>{sign}{sym}</span>{abs.toFixed(d)}
    </span>
  );
}

function Delta({ pct, invert=false, size="md" }){
  if (pct == null || !isFinite(pct)) return <span className="delta delta--flat">—</span>;
  if (Math.abs(pct) < 0.001) return <span className="delta delta--flat">±0%</span>;
  const up = pct > 0;
  const cls = invert ? (up ? "delta--down" : "delta--up") : (up ? "delta--up" : "delta--down");
  const arrow = up ? "↑" : "↓";
  const fs = size==="lg" ? 13 : 12;
  return <span className={`delta ${cls}`} style={{ fontSize:fs }}>{arrow}{Math.abs(pct*100).toFixed(1)}%</span>;
}

// ── Sparkline (area+line) ─────────────────────────────────────────
function Sparkline({ data, w=72, h=22, color="var(--accent)", fill=true, dot=false }){
  if (!data || data.length===0) return null;
  const max = Math.max(...data, 0.0001);
  const min = Math.min(...data, 0);
  const range = max - min || 1;
  const x = i => (i/(data.length-1))*w;
  const y = v => h - ((v-min)/range)*(h-3) - 1.5;
  const pts = data.map((v,i)=>`${x(i).toFixed(2)},${y(v).toFixed(2)}`).join(" ");
  const area = `M0,${h} L${pts.split(" ").join(" L")} L${w},${h} Z`;
  return (
    <svg width={w} height={h} style={{ display:"block", overflow:"visible" }}>
      {fill && <path d={area} fill={color} fillOpacity="0.12"/>}
      <polyline points={pts} fill="none" stroke={color} strokeWidth="1.35" strokeLinecap="round" strokeLinejoin="round"/>
      {dot && <circle cx={x(data.length-1)} cy={y(data[data.length-1])} r="2" fill={color} stroke="#fff" strokeWidth="1"/>}
    </svg>
  );
}

// ── Provider visuals ─────────────────────────────────────────────
function ProviderMark({ provider, size="md" }){
  const p = PROVIDERS[provider] || PROVIDERS.unknown;
  const cls = size==="sm" ? "pmark pmark--sm" : "pmark";
  return <span className={cls} style={{ background:p.color }}>{p.mark}</span>;
}
function ProviderDot({ provider }){
  const p = PROVIDERS[provider] || PROVIDERS.unknown;
  return <span className="pdot" style={{ background:p.color }}/>;
}
function ProviderPill({ provider }){
  const p = PROVIDERS[provider] || PROVIDERS.unknown;
  return (
    <span style={{ display:"inline-flex", alignItems:"center", gap:6, padding:"3px 9px 3px 6px", borderRadius:99, background:"var(--bg-3)", border:"1px solid var(--line-1)", fontSize:12, color:"var(--fg-1)" }}>
      <span className="pdot" style={{ background:p.color }}/>{p.name}
    </span>
  );
}
function ModelBadge({ model }){
  const m = MODELS[model] || MODELS.unknown;
  const p = PROVIDERS[m.provider];
  return (
    <span style={{ display:"inline-flex", alignItems:"center", gap:5, padding:"2px 7px 2px 5px", borderRadius:99, background:p.color+"14", border:"1px solid "+p.color+"33", fontSize:11, color:"var(--fg-0)" }}>
      <span style={{ width:5, height:5, borderRadius:"50%", background:p.color }}/>
      <span className="mono" style={{ fontSize:11 }}>{m.short}</span>
    </span>
  );
}

// ── Icons (lucide-ish line set) ─────────────────────────────────
function Icon({ name, size=16, stroke="currentColor", strokeWidth=1.75, fill="none" }){
  const paths = {
    home:    <><path d="M3 10.5L12 3l9 7.5"/><path d="M5 9.5V21h14V9.5"/><path d="M10 21v-6h4v6"/></>,
    folder:  <><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7Z"/></>,
    check:   <><rect x="3" y="3" width="18" height="18" rx="3"/><path d="m7.5 12.5 3 3 6-7"/></>,
    cog:     <><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.9 2.9l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.9-2.9l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.9-2.9l.1.1a1.7 1.7 0 0 0 1.8.3 1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1.1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.9 2.9l-.1.1a1.7 1.7 0 0 0-.3 1.8 1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1Z"/></>,
    chart:   <><path d="M4 20V8M10 20v-7M16 20V4M22 20H2"/></>,
    search:  <><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></>,
    download:<><path d="M12 4v12M6 12l6 6 6-6M4 20h16"/></>,
    chevron: <><path d="m9 6 6 6-6 6"/></>,
    chevDown:<><path d="m6 9 6 6 6-6"/></>,
    chevLeft:<><path d="m15 6-6 6 6 6"/></>,
    plus:    <><path d="M12 5v14M5 12h14"/></>,
    arrow:   <><path d="M5 12h14M13 6l6 6-6 6"/></>,
    dot:     <><circle cx="12" cy="12" r="3" fill={stroke} stroke="none"/></>,
    ext:     <><path d="M14 4h6v6"/><path d="M20 4 10 14"/><path d="M20 14v6H4V4h6"/></>,
    filter:  <><path d="M4 5h16M7 12h10M10 19h4"/></>,
    calendar:<><rect x="3" y="5" width="18" height="16" rx="2"/><path d="M3 10h18M8 3v4M16 3v4"/></>,
    clock:   <><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></>,
    activity:<><path d="M3 12h4l3-8 4 16 3-8h4"/></>,
    info:    <><circle cx="12" cy="12" r="9"/><path d="M12 11v5M12 7.5v.5"/></>,
    refresh: <><path d="M21 12a9 9 0 1 1-3-6.7L21 8"/><path d="M21 3v5h-5"/></>,
    print:   <><path d="M6 9V3h12v6M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></>,
    alert:   <><path d="M12 3 1 22h22L12 3Z"/><path d="M12 10v5M12 18v.5"/></>,
    wallet:  <><rect x="3" y="6" width="18" height="14" rx="2"/><path d="M16 12h3"/><path d="M3 10h18"/></>,
    cube:    <><path d="m12 3 9 5v8l-9 5-9-5V8l9-5Z"/><path d="M3 8l9 5 9-5M12 13v9"/></>,
    layers:  <><path d="m12 3 10 5-10 5L2 8l10-5Z"/><path d="m2 13 10 5 10-5"/></>,
    sparkles:<><path d="M5 3v4M3 5h4M19 13v4M17 15h4M12 4l1.7 4.6L18 10l-4.3 1.4L12 16l-1.7-4.6L6 10l4.3-1.4L12 4Z"/></>,
    bolt:    <><path d="M13 2 3 14h7l-1 8 10-12h-7l1-8Z"/></>,
    moon:    <><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8Z"/></>,
    target:  <><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><circle cx="12" cy="12" r="1" fill={stroke} stroke="none"/></>,
    flag:    <><path d="M4 21V4h14l-3 5 3 5H4"/></>,
    user:    <><circle cx="12" cy="8" r="4"/><path d="M4 21a8 8 0 0 1 16 0"/></>,
    help:    <><circle cx="12" cy="12" r="9"/><path d="M9.5 9a2.5 2.5 0 1 1 3.5 2.3c-1 .4-1 1-1 1.7M12 17v.5"/></>,
    box:     <><rect x="3" y="3" width="18" height="18" rx="2"/></>,
    ring:    <><circle cx="12" cy="12" r="9"/></>,
    starOn:  <><path d="m12 3 2.7 6.1 6.6.6-5 4.5 1.5 6.5L12 17.5 6.2 20.7l1.5-6.5-5-4.5 6.6-.6L12 3Z" fill={stroke}/></>,
    star:    <><path d="m12 3 2.7 6.1 6.6.6-5 4.5 1.5 6.5L12 17.5 6.2 20.7l1.5-6.5-5-4.5 6.6-.6L12 3Z"/></>,
    archive: <><rect x="3" y="4" width="18" height="4" rx="1"/><path d="M5 8v11a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V8M10 13h4"/></>,
    coins:   <><ellipse cx="9" cy="8" rx="6" ry="3"/><path d="M3 8v4c0 1.7 2.7 3 6 3s6-1.3 6-3V8"/><path d="M3 12v4c0 1.7 2.7 3 6 3s6-1.3 6-3v-4"/><ellipse cx="15" cy="14" rx="6" ry="3"/><path d="M9 14v4c0 1.7 2.7 3 6 3s6-1.3 6-3v-4"/></>,
    tokens:  <><circle cx="8" cy="8" r="4"/><circle cx="16" cy="16" r="4"/><path d="m11.5 11.5 1 1"/></>,
    peak:    <><path d="m3 17 4-8 5 6 4-4 5 4"/><circle cx="12" cy="9" r="2.2" fill={stroke} stroke="none"/></>,
    chip:    <><rect x="5" y="5" width="14" height="14" rx="2"/><path d="M9 2v3M15 2v3M9 19v3M15 19v3M2 9h3M2 15h3M19 9h3M19 15h3"/><rect x="9" y="9" width="6" height="6"/></>,
    receipt: <><path d="M5 3v18l2-1.5L9 21l2-1.5 2 1.5 2-1.5 2 1.5V3z"/><path d="M8 8h8M8 12h8M8 16h5"/></>,
  };
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill={fill} stroke={stroke} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink:0 }}>
      {paths[name] || paths.dot}
    </svg>
  );
}

// ── Buttons ─────────────────────────────────────────────────────
function btnPrimary(){
  return { height:34, padding:"0 14px", border:"1px solid var(--accent)", background:"var(--accent)", color:"#fff", borderRadius:8, fontSize:13, fontWeight:500, display:"inline-flex", alignItems:"center", gap:6, boxShadow:"0 1px 0 rgba(255,255,255,0.16) inset, 0 1px 2px rgba(91,80,238,0.25)" };
}
function btnSecondary(){
  return { height:34, padding:"0 12px", border:"1px solid var(--line-2)", background:"#fff", color:"var(--fg-0)", borderRadius:8, fontSize:13, fontWeight:500, display:"inline-flex", alignItems:"center", gap:6 };
}
function btnGhost(){
  return { height:30, padding:"0 10px", border:"1px solid var(--line-1)", background:"transparent", color:"var(--fg-1)", borderRadius:7, fontSize:12, display:"inline-flex", alignItems:"center", gap:6 };
}
function btnSubtle(){
  return { height:26, padding:"0 8px", border:"none", background:"transparent", color:"var(--fg-1)", borderRadius:6, fontSize:12, display:"inline-flex", alignItems:"center", gap:5 };
}

// ── Section header ──────────────────────────────────────────────
function SectionHeader({ title, subtitle, action, eyebrow }){
  return (
    <div style={{ display:"flex", alignItems:"flex-start", justifyContent:"space-between", marginBottom:14, gap:16 }}>
      <div>
        {eyebrow && <div style={{ fontSize:10.5, letterSpacing:0.08, textTransform:"uppercase", color:"var(--fg-3)", fontWeight:500, marginBottom:5 }}>{eyebrow}</div>}
        <h2 style={{ margin:0, fontSize:14, fontWeight:600, color:"var(--fg-0)", letterSpacing:-0.1 }}>{title}</h2>
        {subtitle && <div style={{ fontSize:11.5, color:"var(--fg-2)", marginTop:3 }}>{subtitle}</div>}
      </div>
      {action}
    </div>
  );
}

// ── Container size hook ─────────────────────────────────────────
function useWidth(ref){
  const [w, setW] = useState(800);
  useLayoutEffect(() => {
    const el = ref.current;
    if (!el) return;
    const ro = new ResizeObserver(() => setW(el.clientWidth));
    ro.observe(el);
    setW(el.clientWidth);
    return () => ro.disconnect();
  }, []);
  return w;
}

Object.assign(window, {
  fmtGBP, fmtUSD, fmtInt, fmtPct, fmtTokens,
  Money, Delta, Sparkline,
  ProviderMark, ProviderDot, ProviderPill, ModelBadge,
  Icon, btnPrimary, btnSecondary, btnGhost, btnSubtle, SectionHeader, useWidth,
});
