/* ============================================================
   NeuroRoute — shared UI primitives + chart wrappers (Chart.js)
   ============================================================ */
const {useState, useEffect, useRef, useMemo, useCallback, createContext, useContext} = React;

const CSSV = (n)=>getComputedStyle(document.documentElement).getPropertyValue(n).trim();
const COL = {blue:'#3b82f6',cyan:'#22d3ee',magenta:'#e24fd6',amber:'#f59e0b',green:'#10b981',
  purple:'#8b5cf6',pink:'#ec4899',red:'#f43f5e',tx2:'#727b91',line:'rgba(255,255,255,.07)'};

/* ---------- toasts ---------- */
const ToastCtx = createContext(()=>{});
const useToast = ()=>useContext(ToastCtx);
function ToastHost({children}){
  const [toasts,setToasts]=useState([]);
  const push = useCallback((t)=>{
    const id=Math.random().toString(36).slice(2);
    setToasts(x=>[...x,{id,kind:'success',...t}]);
    setTimeout(()=>setToasts(x=>x.filter(y=>y.id!==id)), t.duration||3600);
  },[]);
  const map={success:['check','b-green'],error:['x','b-red'],info:['info','b-blue'],warn:['alert','b-amber']};
  return (
    <ToastCtx.Provider value={push}>
      {children}
      <div className="toast-wrap">
        {toasts.map(t=>{
          const [ico,cls]=map[t.kind]||map.success;
          return (
            <div className="toast" key={t.id}>
              <div className={'t-ico badge '+cls} style={{borderRadius:8}}><Icon name={ico} size={16}/></div>
              <div style={{flex:1}}>
                <div className="t-title">{t.title}</div>
                {t.msg && <div className="t-msg">{t.msg}</div>}
              </div>
              <button className="iconbtn" style={{width:24,height:24}} onClick={()=>setToasts(x=>x.filter(y=>y.id!==t.id))}><Icon name="x" size={13}/></button>
            </div>
          );
        })}
      </div>
    </ToastCtx.Provider>
  );
}

/* ---------- modal ---------- */
function Modal({title, sub, onClose, children, footer, lg}){
  useEffect(()=>{
    const h=(e)=>{if(e.key==='Escape')onClose();};
    window.addEventListener('keydown',h); return ()=>window.removeEventListener('keydown',h);
  },[onClose]);
  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className={'modal'+(lg?' modal-lg':'')} onClick={e=>e.stopPropagation()}>
        <div className="modal-hd">
          <div><div className="card-title">{title}</div>{sub&&<div className="card-sub">{sub}</div>}</div>
          <button className="iconbtn" onClick={onClose}><Icon name="x" size={15}/></button>
        </div>
        <div className="modal-bd">{children}</div>
        {footer && <div className="modal-ft">{footer}</div>}
      </div>
    </div>
  );
}

/* ---------- small bits ---------- */
function Badge({color='gray', dot, children, style}){
  return <span className={'badge b-'+color} style={style}>{dot&&<span className="dot"/>}{children}</span>;
}
function TaskBadge({task}){ return <Badge color={task.color}>{task.label}</Badge>; }

function Switch({on, onChange}){
  return <div className={'switch'+(on?' on':'')} onClick={()=>onChange(!on)} role="switch" aria-checked={on}/>;
}

function Field({label, hint, children}){
  return <label className="field">{label&&<span className="label">{label}</span>}{children}{hint&&<span className="hint">{hint}</span>}</label>;
}

function CopyBtn({text, label='Copy'}){
  const t=useToast(); const [done,setDone]=useState(false);
  return (
    <button className="btn btn-soft btn-sm" onClick={()=>{
      navigator.clipboard?.writeText(text); setDone(true); t({title:'Copied to clipboard'});
      setTimeout(()=>setDone(false),1400);
    }}><Icon name={done?'check':'copy'} size={14}/>{done?'Copied':label}</button>
  );
}

function ProviderLogo({id, size}){
  const p=DATA.providerById(id)||{abbr:'?',color:'#888'};
  return <div className="plogo" style={{background:p.color+'22',color:p.color,
    width:size,height:size,borderColor:p.color+'44'}}>{p.abbr}</div>;
}

function Metric({icon, color='blue', value, label, delta, deltaUp, sub}){
  return (
    <div className="card metric">
      <div className="m-top">
        <div className="m-ico" style={{background:`var(--${color}-t)`,color:`var(--${color})`}}><Icon name={icon} size={19}/></div>
        {delta!=null && <span className="m-delta" style={{color: deltaUp?'var(--green)':'var(--red)'}}>
          <Icon name={deltaUp?'trendup':'trenddown'} size={13}/>{delta}</span>}
      </div>
      <div className="m-val tnum">{value}</div>
      <div className="m-label">{label}{sub&&<span style={{color:'var(--tx-3)'}}> · {sub}</span>}</div>
    </div>
  );
}

/* ============================================================
   CHARTS (Chart.js wrappers)
   ============================================================ */
function useChart(cfgFactory, deps){
  const ref=useRef(null); const inst=useRef(null);
  useEffect(()=>{
    if(!ref.current||!window.Chart) return;
    const cfg=cfgFactory();
    inst.current=new Chart(ref.current.getContext('2d'), cfg);
    return ()=>inst.current && inst.current.destroy();
  }, deps);
  return ref;
}
const gridCfg = {color:COL.line, drawTicks:false};
const tickCfg = {color:COL.tx2, font:{family:'JetBrains Mono', size:10.5}, padding:6};
const tooltipCfg = {
  backgroundColor:'#171b29', borderColor:'rgba(255,255,255,.12)', borderWidth:1,
  titleColor:'#eef1f8', bodyColor:'#aab2c5', padding:11, cornerRadius:9, displayColors:true,
  boxPadding:4, titleFont:{family:'Manrope',weight:'700'}, bodyFont:{family:'JetBrains Mono',size:11.5},
};

function LineDual({labels, a, b, height=260}){
  const ref=useChart(()=>{
    const mk=(ctx,c)=>{const g=ctx.createLinearGradient(0,0,0,height);g.addColorStop(0,c+'55');g.addColorStop(1,c+'00');return g;};
    return {type:'line', data:{labels, datasets:[
      {label:'GPT-4o baseline', data:b, borderColor:COL.red, borderWidth:2, borderDash:[5,4],
        pointRadius:0, tension:.35, fill:false},
      {label:'With NeuroRoute', data:a, borderColor:COL.blue, borderWidth:2.5, pointRadius:0,
        tension:.35, fill:true, backgroundColor:(c)=>mk(c.chart.ctx,COL.blue)},
    ]},
    options:{responsive:true,maintainAspectRatio:false,interaction:{mode:'index',intersect:false},
      plugins:{legend:{display:false}, tooltip:{...tooltipCfg, callbacks:{label:(c)=>' '+c.dataset.label+': $'+c.raw.toLocaleString()}}},
      scales:{x:{grid:{display:false},ticks:{...tickCfg,maxTicksLimit:8}},
        y:{grid:gridCfg,border:{display:false},ticks:{...tickCfg,callback:v=>'$'+v}}}}};
  },[labels.join(),a.join(),b.join()]);
  return <div style={{height}}><canvas ref={ref}/></div>;
}

function BarStacked({labels, series, height=260}){
  const ref=useChart(()=>({
    type:'bar', data:{labels, datasets:series.map(s=>({label:s.label,data:s.data,backgroundColor:s.color,
      borderRadius:4, borderSkipped:false, barPercentage:.7, categoryPercentage:.78, stack:'a'}))},
    options:{responsive:true,maintainAspectRatio:false,interaction:{mode:'index',intersect:false},
      plugins:{legend:{display:false},tooltip:{...tooltipCfg,callbacks:{label:c=>' '+c.dataset.label+': '+(c.raw/1e6).toFixed(2)+'M'}}},
      scales:{x:{stacked:true,grid:{display:false},ticks:{...tickCfg,maxTicksLimit:10}},
        y:{stacked:true,grid:gridCfg,border:{display:false},ticks:{...tickCfg,callback:v=>(v/1e6).toFixed(1)+'M'}}}}}),
  [labels.join(),series.map(s=>s.data.join()).join()]);
  return <div style={{height}}><canvas ref={ref}/></div>;
}

function BarSimple({labels, data, color=COL.blue, height=240, fmt}){
  const ref=useChart(()=>({
    type:'bar', data:{labels, datasets:[{data, backgroundColor:color+'cc', hoverBackgroundColor:color,
      borderRadius:5, borderSkipped:false, barPercentage:.66, categoryPercentage:.8}]},
    options:{responsive:true,maintainAspectRatio:false,
      plugins:{legend:{display:false},tooltip:{...tooltipCfg,callbacks:{label:c=>' '+(fmt?fmt(c.raw):c.raw.toLocaleString())}}},
      scales:{x:{grid:{display:false},ticks:{...tickCfg,maxTicksLimit:12}},
        y:{grid:gridCfg,border:{display:false},ticks:{...tickCfg,callback:v=>fmt?fmt(v):v.toLocaleString()}}}}}),
  [labels.join(),data.join()]);
  return <div style={{height}}><canvas ref={ref}/></div>;
}

function Donut({items, height=230, centerLabel, centerSub}){
  const palette=[COL.cyan,COL.blue,COL.magenta,COL.green,COL.purple,COL.amber,COL.pink];
  const ref=useChart(()=>({
    type:'doughnut', data:{labels:items.map(i=>i.label), datasets:[{data:items.map(i=>i.value),
      backgroundColor:items.map((i,k)=>i.color||palette[k%palette.length]), borderColor:'#11141f', borderWidth:3, hoverOffset:6}]},
    options:{responsive:true,maintainAspectRatio:false,cutout:'68%',
      plugins:{legend:{display:false},tooltip:{...tooltipCfg,callbacks:{label:c=>' '+c.label+': '+c.raw+'%'}}}}}),
  [items.map(i=>i.value).join()]);
  return (
    <div style={{height,position:'relative'}}>
      <canvas ref={ref}/>
      {centerLabel!=null&&<div style={{position:'absolute',inset:0,display:'grid',placeItems:'center',pointerEvents:'none'}}>
        <div style={{textAlign:'center'}}>
          <div className="display tnum" style={{fontSize:26}}>{centerLabel}</div>
          <div className="faint" style={{fontSize:11.5}}>{centerSub}</div>
        </div></div>}
    </div>
  );
}

function Gauge({value, size=96, color, label}){
  const c = color || (value>=98?COL.green:value>=95?COL.cyan:value>=90?COL.amber:COL.red);
  const R=42, C=2*Math.PI*R, off=C*(1-value/100);
  return (
    <div className="gauge" style={{width:size,height:size}}>
      <svg width={size} height={size} viewBox="0 0 96 96">
        <circle cx="48" cy="48" r={R} fill="none" stroke="rgba(255,255,255,.07)" strokeWidth="7"/>
        <circle cx="48" cy="48" r={R} fill="none" stroke={c} strokeWidth="7" strokeLinecap="round"
          strokeDasharray={C} strokeDashoffset={off} style={{transition:'stroke-dashoffset 1s ease'}}/>
      </svg>
      <div className="g-val" style={{color:c}}>{value}{label||'%'}</div>
    </div>
  );
}

/* tiny inline SVG sparkline */
function Spark({data, color=COL.green, w=120, h=34, fill=true}){
  const max=Math.max(...data), min=Math.min(...data), rng=(max-min)||1;
  const pts=data.map((v,i)=>[i/(data.length-1)*w, h-4-((v-min)/rng)*(h-8)]);
  const d=pts.map((p,i)=>(i?'L':'M')+p[0].toFixed(1)+' '+p[1].toFixed(1)).join(' ');
  const id='sp'+useMemo(()=>Math.random().toString(36).slice(2,7),[]);
  return (
    <svg className="spark" width={w} height={h} viewBox={`0 0 ${w} ${h}`}>
      <defs><linearGradient id={id} x1="0" y1="0" x2="0" y2="1">
        <stop offset="0" stopColor={color} stopOpacity=".35"/><stop offset="1" stopColor={color} stopOpacity="0"/></linearGradient></defs>
      {fill&&<path d={d+` L${w} ${h} L0 ${h} Z`} fill={`url(#${id})`}/>}
      <path d={d} fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

/* shared page header: context line + actions (title is in topbar) */
function PageHead({desc, children}){
  return (
    <div className="spread" style={{marginBottom:22,flexWrap:'wrap',gap:12}}>
      <p style={{color:'var(--tx-2)',fontSize:14,margin:0,maxWidth:620}}>{desc}</p>
      {children && <div className="row" style={{gap:10}}>{children}</div>}
    </div>
  );
}
/* horizontal proportion bar used in tables */
function MiniBar({pct, color=COL.blue}){
  return <div className="meter" style={{width:90,display:'inline-block',verticalAlign:'middle'}}><span style={{width:pct+'%',background:color}}/></div>;
}
function fmtUSD(n,d=2){return '$'+n.toLocaleString('en-US',{minimumFractionDigits:d,maximumFractionDigits:d});}
function fmtNum(n){return n.toLocaleString('en-US');}
function fmtTok(n){return n>=1e6?(n/1e6).toFixed(2)+'M':n>=1e3?(n/1e3).toFixed(1)+'K':n;}

/* ── Loading / Empty states ─────────────────────────────── */
function LoadingPage(){
  return (
    <div style={{display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',minHeight:400,gap:16}}>
      <div style={{width:40,height:40,borderRadius:'50%',border:'3px solid var(--line)',borderTopColor:'var(--blue)',animation:'spin .8s linear infinite'}}/>
      <span className="faint" style={{fontSize:13}}>Loading…</span>
      <style>{`@keyframes spin{to{transform:rotate(360deg)}}`}</style>
    </div>
  );
}

function EmptyState({icon, title, desc, actions}){
  return (
    <div className="card" style={{padding:'60px 40px',textAlign:'center'}}>
      <div style={{width:56,height:56,borderRadius:16,background:'var(--bg-inset)',display:'flex',alignItems:'center',justifyContent:'center',margin:'0 auto 16px'}}>
        <Icon name={icon||'layers'} size={24} style={{color:'var(--tx-2)'}}/>
      </div>
      <h3 style={{margin:'0 0 8px',fontSize:18}}>{title}</h3>
      {desc && <p className="faint" style={{margin:'0 0 20px',maxWidth:440,marginLeft:'auto',marginRight:'auto',lineHeight:1.6}}>{desc}</p>}
      {actions && actions.length > 0 && <div className="row" style={{gap:10,justifyContent:'center'}}>
        {actions.map((a,i)=><button key={i} className={a.primary?'btn btn-primary btn-sm':'btn btn-ghost btn-sm'} onClick={a.onClick}>{a.label}</button>)}
      </div>}
    </div>
  );
}

/* ── Health sidebar component ──────────────────────────── */
function HealthSidebar({healthData}){
  const providers = groupHealthByProvider(healthData);
  if(providers.length===0) return (
    <SectionCard title="Provider health" sub="No providers connected">
      <div className="faint" style={{padding:20,textAlign:'center'}}>No provider data available.</div>
    </SectionCard>
  );
  return (
    <SectionCard title="Provider health" sub="Live status across providers" pad={false}>
      <div style={{padding:'6px 0'}}>
        {providers.map(h=>{
          const c = h.status==='optimal'?COL.green:h.status==='degraded'?COL.amber:COL.red;
          return (
            <div key={h.id} className="spread" style={{padding:'11px 20px',borderBottom:'1px solid var(--line)'}}>
              <span className="row" style={{gap:10}}>
                <span className="statusdot pulse" style={{background:c,color:c}}/>
                <ProviderLogo id={h.id} size={26}/>
                <span style={{fontWeight:600}}>{h.name}</span>
              </span>
              <span className="row" style={{gap:8}}>
                <span className="mono" style={{fontSize:12,color:c}}>{h.healthy}/{h.total}</span>
              </span>
            </div>
          );
        })}
      </div>
    </SectionCard>
  );
}

function SectionCard({title, sub, action, children, pad=true}){
  return (
    <div className="card">
      <div className="card-hd"><div><div className="card-title">{title}</div>{sub&&<div className="card-sub">{sub}</div>}</div>{action}</div>
      <div style={pad?{padding:20}:null}>{children}</div>
    </div>
  );
}

Object.assign(window,{ToastHost,useToast,Modal,Badge,TaskBadge,Switch,Field,CopyBtn,
  ProviderLogo,Metric,LineDual,BarStacked,BarSimple,Donut,Gauge,Spark,COL,
  PageHead,MiniBar,fmtUSD,fmtNum,fmtTok,
  LoadingPage,EmptyState,HealthSidebar,SectionCard});
