/* ============================================================
   NeuroRoute — Usage Analytics Widget
   Stacked bar chart + drill-down pivot table
   ============================================================ */

const PALETTE = ['#06b6d4','#3b82f6','#d946ef','#10b981','#f59e0b','#ef4444','#8b5cf6','#64748b'];

const gridCfgUA = {color:'rgba(255,255,255,.07)', drawTicks:false};
const tickCfgUA = {color:'#8892a4', font:{family:'JetBrains Mono', size:10.5}, padding:6};
const tooltipCfgUA = {
  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 finerGran(g){
  const order=['month','week','day','hour'];
  const i=order.indexOf(g);
  return i>=0&&i<order.length-1 ? order[i+1] : g;
}

function periodBounds(period, gran){
  // The API's `to` is the INCLUSIVE last day, so return the last day within the
  // period (not the exclusive start of the next one) or the drill leaks a day.
  const d = new Date(gran==='month' ? period+'-01T00:00:00Z' : period+'T00:00:00Z');
  if(gran==='month'){ const e=new Date(d); e.setUTCMonth(e.getUTCMonth()+1); e.setUTCDate(e.getUTCDate()-1); return {from:period+'-01',to:e.toISOString().slice(0,10)}; }
  if(gran==='week') { const e=new Date(d); e.setUTCDate(e.getUTCDate()+6);   return {from:period,to:e.toISOString().slice(0,10)}; }
  if(gran==='day')  { return {from:period, to:period}; }
  return {from:period.slice(0,10), to:period.slice(0,10)};
}

function fmtPeriodLabel(period, gran){
  if(!period) return period;
  if(gran==='month') return period;
  if(gran==='hour')  return period.slice(5,16).replace('T',' ');
  return period.slice(5,10);
}

function getPresetDates(p){
  const today = new Date();
  const ymd = d => d.toISOString().slice(0,10);
  if(p==='7D'){
    const f=new Date(today); f.setUTCDate(f.getUTCDate()-7);
    return {from:ymd(f), to:ymd(today)};
  }
  if(p==='15D'){
    const f=new Date(today); f.setUTCDate(f.getUTCDate()-15);
    return {from:ymd(f), to:ymd(today)};
  }
  if(p==='30D'){
    const f=new Date(today); f.setUTCDate(f.getUTCDate()-30);
    return {from:ymd(f), to:ymd(today)};
  }
  if(p==='90D'){
    const f=new Date(today); f.setUTCDate(f.getUTCDate()-90);
    return {from:ymd(f), to:ymd(today)};
  }
  if(p==='3M'){
    const f=new Date(today); f.setUTCMonth(f.getUTCMonth()-3);
    return {from:ymd(f), to:ymd(today)};
  }
  if(p==='6M'){
    const f=new Date(today); f.setUTCMonth(f.getUTCMonth()-6);
    return {from:ymd(f), to:ymd(today)};
  }
  if(p==='12M'){
    const f=new Date(today); f.setUTCMonth(f.getUTCMonth()-12);
    return {from:ymd(f), to:ymd(today)};
  }
  if(p==='TM'){
    const f=new Date(Date.UTC(today.getUTCFullYear(),today.getUTCMonth(),1));
    return {from:ymd(f), to:ymd(today)};
  }
  if(p==='LM'){
    const lm=new Date(Date.UTC(today.getUTCFullYear(),today.getUTCMonth()-1,1));
    // Inclusive last day of last month = day before the 1st of this month.
    const lme=new Date(Date.UTC(today.getUTCFullYear(),today.getUTCMonth(),1)); lme.setUTCDate(lme.getUTCDate()-1);
    return {from:ymd(lm), to:ymd(lme)};
  }
  return {from:'', to:''};
}

const GROUP_OPTIONS = [
  {value:'model', label:'Model'},
  {value:'provider', label:'Provider'},
  {value:'key', label:'API Key'},
  {value:'total', label:'Total'},
];

const GRAN_OPTIONS = [
  {value:'', label:'Auto'},
  {value:'hour', label:'Hour'},
  {value:'day', label:'Day'},
  {value:'week', label:'Week'},
  {value:'month', label:'Month'},
];

const METRIC_OPTIONS = [
  {value:'cost', label:'Cost'},
  {value:'tokens', label:'Tokens'},
  {value:'requests', label:'Requests'},
];

function SegControl({options, value, onChange, style}){
  return (
    <div className="seg" style={style}>
      {options.map(o=>(
        <button key={o.value} className={value===o.value?'active':''}
          onClick={()=>onChange(o.value)}>{o.label}</button>
      ))}
    </div>
  );
}

function getValue(row, metric){
  if(metric==='cost') return row.provider_cost||0;
  if(metric==='tokens') return (row.input_tokens||0)+(row.output_tokens||0);
  return row.requests||0;
}

function fmtMetric(v, metric){
  if(metric==='cost') return fmtUSD(v,4);
  if(metric==='tokens') return fmtTok(v);
  return fmtNum(v);
}

/* ============================================================
   UsageAnalyticsWidget
   ============================================================ */
/* groupOptions (optional) restricts the group-by control to a subset of
   GROUP_OPTIONS values, e.g. ['model','total'] on the customer-facing page
   where which upstream provider or key served a request is not the customer's
   concern. Omit it to expose all four (SISL admin needs provider + key
   breakdowns for invoice reconciliation). */
function UsageAnalyticsWidget({apiCall, title, groupOptions}){
  const [stack, setStack] = React.useState([
    {preset:'30D', from:'', to:'', gran:'', groupBy:'model', label:'Last 30 days'}
  ]);
  const [metric, setMetric] = React.useState('cost');
  const [data, setData] = React.useState(null);
  const [loading, setLoading] = React.useState(false);
  const [error, setError] = React.useState('');
  const [customFrom, setCustomFrom] = React.useState('');
  const [customTo, setCustomTo] = React.useState('');

  const cur = stack[stack.length-1];

  /* ---- fetch ---- */
  React.useEffect(()=>{
    setLoading(true);
    setError('');
    const params = {};
    if(cur.from) params.from = cur.from;
    if(cur.to)   params.to   = cur.to;
    if(cur.gran) params.granularity = cur.gran;
    if(cur.groupBy) params.group_by = cur.groupBy;
    (apiCall||NR_API.usageTimeseries)(params)
      .then(res=>{setData(res); setLoading(false);})
      .catch(e=>{setError(e&&e.message||'Failed to load'); setLoading(false);});
  }, [cur.from, cur.to, cur.gran, cur.groupBy]);

  /* ---- preset apply ---- */
  const applyPreset = (p) => {
    if(p==='custom'){
      setStack([{preset:'custom', from:customFrom, to:customTo, gran:'', groupBy:cur.groupBy, label:'Custom range'}]);
      return;
    }
    const {from,to} = getPresetDates(p);
    const labels = {'7D':'Last 7 days','15D':'Last 15 days','30D':'Last 30 days','90D':'Last 90 days',
      '3M':'Last 3 months','6M':'Last 6 months','12M':'Last 12 months',
      'TM':'This month','LM':'Last month'};
    setStack([{preset:p, from, to, gran:'', groupBy:cur.groupBy, label:labels[p]||p}]);
  };

  const applyCustom = () => {
    setStack([{preset:'custom', from:customFrom, to:customTo, gran:'', groupBy:cur.groupBy, label:'Custom range'}]);
  };

  /* ---- drill-down ---- */
  const drillInto = (period) => {
    const gran = data && data.granularity ? data.granularity : 'month';
    const nextGran = finerGran(gran);
    if(nextGran===gran) return; // already at finest
    const {from,to} = periodBounds(period, gran);
    setStack(prev=>[...prev, {
      preset:'custom', from, to,
      gran:nextGran,
      groupBy:cur.groupBy,
      label:period,
    }]);
  };

  const goToCrumb = (idx) => {
    setStack(prev=>prev.slice(0,idx+1));
  };

  const resetStack = () => {
    setStack([{preset:'30D', from:'', to:'', gran:'', groupBy:cur.groupBy, label:'Last 30 days'}]);
  };

  const handleGroupBy = (v) => {
    setStack(prev=>{
      const updated=[...prev];
      updated[updated.length-1]={...updated[updated.length-1], groupBy:v};
      return updated;
    });
  };

  const handleGran = (v) => {
    setStack(prev=>{
      const updated=[...prev];
      updated[updated.length-1]={...updated[updated.length-1], gran:v};
      return updated;
    });
  };

  /* ---- derive chart data ---- */
  const rows = data && data.rows ? data.rows : [];
  const summary = data && data.summary ? data.summary : null;
  const gran = data && data.granularity ? data.granularity : 'month';

  const periods = [];
  const periodSet = new Set();
  rows.forEach(r=>{ if(!periodSet.has(r.period)){periodSet.add(r.period); periods.push(r.period);} });

  const groupKeys = [];
  const groupKeySet = new Set();
  rows.forEach(r=>{ const k=r.group_key||'total'; if(!groupKeySet.has(k)){groupKeySet.add(k); groupKeys.push(k);} });

  /* map: period → groupKey → value */
  const valueMap = {};
  rows.forEach(r=>{
    const k=r.group_key||'total';
    if(!valueMap[r.period]) valueMap[r.period]={};
    valueMap[r.period][k]=(valueMap[r.period][k]||0)+getValue(r,metric);
  });

  const chartLabels = periods.map(p=>fmtPeriodLabel(p, gran));
  const chartDatasets = groupKeys.map((gk,i)=>({
    label:gk,
    data:periods.map(p=>(valueMap[p]&&valueMap[p][gk])||0),
    backgroundColor:PALETTE[i%PALETTE.length],
    borderRadius:4,
    borderSkipped:false,
    barPercentage:.7,
    categoryPercentage:.78,
    stack:'a',
  }));

  /* ---- Chart.js canvas ---- */
  const canvasRef = React.useRef(null);
  const chartInst = React.useRef(null);

  React.useEffect(()=>{
    if(!canvasRef.current||!window.Chart) return;
    if(chartInst.current){ chartInst.current.destroy(); chartInst.current=null; }
    if(!chartLabels.length) return;

    const yFmt = v=>{
      if(metric==='cost') return '$'+v.toFixed(4);
      if(metric==='tokens') return fmtTok(v);
      return fmtNum(v);
    };

    chartInst.current = new Chart(canvasRef.current.getContext('2d'), {
      type:'bar',
      data:{labels:chartLabels, datasets:chartDatasets},
      options:{
        responsive:true, maintainAspectRatio:false,
        interaction:{mode:'index',intersect:false},
        onClick:(evt,elements)=>{
          if(!elements||!elements.length) return;
          const idx=elements[0].index;
          const period=periods[idx];
          if(period) drillInto(period);
        },
        plugins:{
          legend:{display:groupKeys.length>1, position:'bottom', labels:{color:'#8892a4',font:{family:'JetBrains Mono',size:11},boxWidth:12,padding:14}},
          tooltip:{...tooltipCfgUA, callbacks:{label:c=>' '+c.dataset.label+': '+yFmt(c.raw)}},
        },
        scales:{
          x:{stacked:true, grid:{display:false}, ticks:{...tickCfgUA,maxTicksLimit:12}},
          y:{stacked:true, grid:gridCfgUA, border:{display:false}, ticks:{...tickCfgUA, callback:yFmt}},
        },
      },
    });
    return ()=>{ if(chartInst.current){ chartInst.current.destroy(); chartInst.current=null; } };
  }, [chartLabels.join('|'), chartDatasets.map(d=>d.label+':'+d.data.join()).join('|'), metric]);

  /* ---- table: periods newest-first ---- */
  const tablePeriods = [...periods].reverse();

  /* group totals */
  const groupTotals = {};
  groupKeys.forEach(gk=>{
    groupTotals[gk] = periods.reduce((s,p)=>(s+((valueMap[p]&&valueMap[p][gk])||0)),0);
  });
  const grandTotal = groupKeys.reduce((s,gk)=>s+groupTotals[gk],0);

  /* ---- section card title ---- */
  const cardTitle = (title||'Usage Analytics') +
    (data ? ' — '+gran+' · '+(data.from||'')+(data.to?' → '+data.to:'') : '');

  /* ---- metric tab action ---- */
  const metricAction = (
    <SegControl options={METRIC_OPTIONS} value={metric} onChange={setMetric}/>
  );

  const groupOpts = (groupOptions && groupOptions.length)
    ? GROUP_OPTIONS.filter(function(o){ return groupOptions.indexOf(o.value)>=0; })
    : GROUP_OPTIONS;

  return (
    <div style={{display:'flex',flexDirection:'column',gap:14}}>

      {/* KPI summary strip */}
      {summary && (
        <div className="grid" style={{gridTemplateColumns:'repeat(auto-fit, minmax(150px, 1fr))', gap:12}}>
          <Metric icon="layers" color="blue" value={fmtNum(summary.total_requests||0)} label="Total Requests"/>
          <Metric icon="arrowdown" color="cyan"  value={fmtTok(summary.total_input_tokens||0)} label="Input Tokens"/>
          <Metric icon="arrowup" color="purple" value={fmtTok(summary.total_output_tokens||0)} label="Output Tokens"/>
          <Metric icon="billing" color="green" value={fmtUSD(summary.total_provider_cost||0,4)} label="Provider Cost"/>
          <Metric icon="billing" color="amber" value={fmtUSD(summary.total_platform_fee||0,4)} label="Platform Fee"/>
        </div>
      )}

      {/* Breadcrumb */}
      {stack.length>1 && (
        <div className="row" style={{gap:6,flexWrap:'wrap',alignItems:'center'}}>
          {stack.map((lvl,i)=>(
            i<stack.length-1
              ? <React.Fragment key={i}>
                  <button className="btn btn-ghost btn-sm" style={{padding:'2px 8px',fontSize:12}}
                    onClick={()=>goToCrumb(i)}>{lvl.label}</button>
                  <span style={{color:'var(--tx-3)'}}>›</span>
                </React.Fragment>
              : <span key={i} style={{fontSize:12,color:'var(--tx-0)',fontWeight:600}}>{lvl.label}</span>
          ))}
          <button className="btn btn-ghost btn-sm" style={{marginLeft:8,padding:'2px 8px',fontSize:12}}
            onClick={resetStack}>↩ Reset</button>
        </div>
      )}

      {/* Controls bar */}
      <div className="spread" style={{flexWrap:'wrap',gap:10,alignItems:'center'}}>
        <div className="row" style={{gap:8,flexWrap:'wrap'}}>
          {/* Preset segment */}
          {stack.length===1 && (
            <SegControl
              options={['7D','15D','30D','90D','3M','6M','12M','TM','LM','Custom'].map(v=>({value:v.toLowerCase()==='custom'?'custom':v, label:v}))}
              value={cur.preset}
              onChange={p=>applyPreset(p==='custom'?'custom':p)}
            />
          )}

          {/* Custom date range (only at top level, only when custom preset) */}
          {cur.preset==='custom' && stack.length===1 && (
            <div className="row" style={{gap:6}}>
              <input type="date" className="input input-sm" value={customFrom}
                onChange={e=>setCustomFrom(e.target.value)} style={{width:130}}/>
              <span style={{color:'var(--tx-3)'}}>→</span>
              <input type="date" className="input input-sm" value={customTo}
                onChange={e=>setCustomTo(e.target.value)} style={{width:130}}/>
              <button className="btn btn-primary btn-sm" onClick={applyCustom}>Apply</button>
            </div>
          )}
        </div>

        <div className="row" style={{gap:8,flexWrap:'wrap'}}>
          {/* Granularity */}
          <SegControl options={GRAN_OPTIONS} value={cur.gran} onChange={handleGran}/>
          {/* Group by */}
          <SegControl options={groupOpts} value={cur.groupBy} onChange={handleGroupBy}/>
        </div>
      </div>

      {/* Chart + Table in a SectionCard */}
      <SectionCard title={cardTitle} sub="Click a bar or period to drill down" action={metricAction}>
        {loading && (
          <div style={{display:'flex',alignItems:'center',justifyContent:'center',height:240,gap:12}}>
            <div style={{width:28,height:28,borderRadius:'50%',border:'2px solid var(--line)',borderTopColor:'var(--blue)',animation:'spin .8s linear infinite'}}/>
            <span className="faint" style={{fontSize:13}}>Loading…</span>
          </div>
        )}
        {!loading && error && (
          <div style={{padding:24,textAlign:'center',color:'var(--red)',fontSize:13}}>{error}</div>
        )}
        {!loading && !error && !rows.length && (
          <EmptyState icon="chart" title="No usage data" desc="No usage data found for the selected range."/>
        )}
        {!loading && !error && rows.length>0 && <>
          {/* Chart */}
          <div style={{height:280,padding:'0 4px'}}>
            <canvas ref={canvasRef}/>
          </div>

          {/* Table */}
          <div className="scroll-x" style={{marginTop:20}}>
            <table className="tbl" style={{minWidth:500}}>
              <thead>
                <tr>
                  <th style={{textAlign:'left',padding:'7px 12px',fontSize:12,color:'var(--tx-2)'}}>Period</th>
                  {groupKeys.map(gk=>(
                    <th key={gk} style={{textAlign:'right',padding:'7px 12px',fontSize:12,color:'var(--tx-2)'}}>{gk}</th>
                  ))}
                  <th style={{textAlign:'right',padding:'7px 12px',fontSize:12,color:'var(--tx-2)'}}>Total</th>
                </tr>
              </thead>
              <tbody>
                {tablePeriods.map(period=>{
                  const periodTotal=groupKeys.reduce((s,gk)=>s+((valueMap[period]&&valueMap[period][gk])||0),0);
                  return (
                    <tr key={period}>
                      <td style={{padding:'6px 12px',fontSize:12.5}}>
                        {finerGran(gran)!==gran
                          ? <button className="btn-link" style={{fontFamily:'JetBrains Mono',fontSize:12,color:'var(--blue)',background:'none',border:'none',cursor:'pointer',padding:0}}
                              onClick={()=>drillInto(period)}>{fmtPeriodLabel(period,gran)}</button>
                          : <span style={{fontFamily:'JetBrains Mono',fontSize:12}}>{fmtPeriodLabel(period,gran)}</span>
                        }
                      </td>
                      {groupKeys.map(gk=>(
                        <td key={gk} style={{textAlign:'right',padding:'6px 12px',fontSize:12,fontFamily:'JetBrains Mono',color:'var(--tx-1)'}}>
                          {fmtMetric((valueMap[period]&&valueMap[period][gk])||0, metric)}
                        </td>
                      ))}
                      <td style={{textAlign:'right',padding:'6px 12px',fontSize:12,fontFamily:'JetBrains Mono',color:'var(--tx-0)',fontWeight:600}}>
                        {fmtMetric(periodTotal, metric)}
                      </td>
                    </tr>
                  );
                })}
              </tbody>
              <tfoot>
                <tr style={{borderTop:'1px solid var(--line)'}}>
                  <td style={{padding:'7px 12px',fontSize:12,fontWeight:600,color:'var(--tx-0)'}}>Total</td>
                  {groupKeys.map(gk=>(
                    <td key={gk} style={{textAlign:'right',padding:'7px 12px',fontSize:12,fontFamily:'JetBrains Mono',fontWeight:600,color:'var(--tx-0)'}}>
                      {fmtMetric(groupTotals[gk]||0, metric)}
                    </td>
                  ))}
                  <td style={{textAlign:'right',padding:'7px 12px',fontSize:12,fontFamily:'JetBrains Mono',fontWeight:700,color:'var(--blue)'}}>
                    {fmtMetric(grandTotal, metric)}
                  </td>
                </tr>
              </tfoot>
            </table>
          </div>
        </>}
      </SectionCard>
    </div>
  );
}

window.UsageAnalyticsWidget = UsageAnalyticsWidget;

/* ============================================================
   CustomerAnalyticsPanel — the complete per-customer picture:
   billing (month-scoped) + usage time series (range-scoped).

   The two halves intentionally use DIFFERENT time controls because
   they answer different questions: an invoice is always a calendar
   month, while usage analysis wants arbitrary ranges and drill-down.
   Mixing them would make the invoice figures meaningless.
   ============================================================ */

/* Last 12 calendar months, newest first, as {value:'YYYY-MM', label:'Mon YYYY'}. */
function recentMonthOptions(){
  const MON=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  const now=new Date();
  const out=[];
  for(let i=0;i<12;i++){
    const d=new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth()-i, 1));
    const m=d.getUTCMonth(), y=d.getUTCFullYear();
    out.push({value:y+'-'+String(m+1).padStart(2,'0'), label:MON[m]+' '+y});
  }
  return out;
}

function StatBlock({label, value, hint, color}){
  return (
    <div style={{padding:'11px 14px',background:'var(--bg-inset)',borderRadius:11}}>
      <div className="faint" style={{fontSize:11.5}}>{label}</div>
      <div className="mono" style={{fontSize:19,marginTop:3,color:color||'var(--tx-0)'}}>{value}</div>
      {hint && <div className="faint" style={{fontSize:10.5,marginTop:2}}>{hint}</div>}
    </div>
  );
}

function CustomerAnalyticsPanel({orgId, orgName, tier}){
  const [month, setMonth] = React.useState(recentMonthOptions()[0].value);
  const [bill, setBill]   = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState('');

  React.useEffect(()=>{
    if(!orgId) return;
    setLoading(true); setError('');
    NR_API.orgBilling(orgId, month)
      .then(r=>{ setBill(r); setLoading(false); })
      .catch(e=>{ setError((e&&e.message)||'Failed to load billing'); setLoading(false); });
  }, [orgId, month]);

  const n = v => (typeof v==='number' ? v : 0);
  const models = (bill && bill.by_model) || [];

  return (
    <div style={{display:'flex',flexDirection:'column',gap:16}}>

      {/* ---- Billing (calendar month) ---- */}
      <SectionCard
        title={'Billing — '+(orgName||orgId||'')}
        sub="Invoices are always a calendar month. Revenue = base fee + token fee."
        action={
          <select className="input input-sm" style={{width:132}} value={month}
            onChange={e=>setMonth(e.target.value)}>
            {recentMonthOptions().map(o=><option key={o.value} value={o.value}>{o.label}</option>)}
          </select>
        }>
        {loading ? <div className="faint" style={{padding:'10px 2px'}}>Loading billing…</div>
         : error ? <div style={{padding:'10px 2px',color:'var(--red)'}}>{error}</div>
         : !bill ? <div className="faint" style={{padding:'10px 2px'}}>No billing data.</div>
         : <div style={{display:'flex',flexDirection:'column',gap:14}}>

            {/* Revenue math, in the order the invoice builds it up */}
            <div className="grid" style={{gridTemplateColumns:'repeat(auto-fit, minmax(150px, 1fr))', gap:10}}>
              <StatBlock label="Base fee"     value={fmtUSD(n(bill.base_fee),2)}  hint="monthly flat"/>
              <StatBlock label="Token fee"    value={fmtUSD(n(bill.token_fee),4)} hint="Σ per-request fee"/>
              <StatBlock label="Revenue"      value={fmtUSD(n(bill.platform_fee),2)} hint="base + token" color="var(--green)"/>
              <StatBlock label="Provider cost" value={fmtUSD(n(bill.sisl_managed_cost),4)} hint="pass-through at cost"/>
              <StatBlock label="Total charge" value={fmtUSD(n(bill.total_charge),2)} hint="revenue + provider cost" color="var(--blue)"/>
              <StatBlock label="Projected"    value={fmtUSD(n(bill.projected_total),2)} hint="end of month"/>
            </div>

            {/* Savings + rate card */}
            <div className="grid" style={{gridTemplateColumns:'repeat(auto-fit, minmax(150px, 1fr))', gap:10}}>
              <StatBlock label="Routing savings"     value={fmtUSD(n(bill.routing_savings),4)}/>
              <StatBlock label="Compression savings" value={fmtUSD(n(bill.compression_savings),4)}/>
              <StatBlock label="Total savings"       value={fmtUSD(n(bill.total_savings),4)} color="var(--cyan)"/>
              <StatBlock label="Requests"            value={fmtNum(n(bill.request_count))}/>
              <StatBlock label="Tier"                value={bill.tier||tier||'—'}
                hint={bill.rate_has_custom_terms ? 'custom terms' : 'list price'}/>
              <StatBlock label="Rate in / out"
                value={fmtUSD(n(bill.rate_in_usd_1m),2)+' / '+fmtUSD(n(bill.rate_out_usd_1m),2)}
                hint={'per 1M tok'+(n(bill.rate_discount_pct)>0 ? ' · '+n(bill.rate_discount_pct)+'% off' : '')}/>
            </div>

            {/* Per-model breakdown — the invoice-justification table */}
            {models.length>0 && (
              <div className="scroll-x">
                <table className="table" style={{minWidth:520}}>
                  <thead><tr>
                    <th>Model</th>
                    <th style={{textAlign:'right'}}>Requests</th>
                    <th style={{textAlign:'right'}}>Input tok</th>
                    <th style={{textAlign:'right'}}>Output tok</th>
                    <th style={{textAlign:'right'}}>Cost</th>
                  </tr></thead>
                  <tbody>{models.map(function(m,i){
                    return (
                      <tr key={(m.model||'')+i}>
                        <td>{m.model||'—'}</td>
                        <td className="num" style={{textAlign:'right'}}>{fmtNum(n(m.requests))}</td>
                        <td className="num" style={{textAlign:'right'}}>{fmtTok(n(m.input_tokens))}</td>
                        <td className="num" style={{textAlign:'right'}}>{fmtTok(n(m.output_tokens))}</td>
                        <td className="num" style={{textAlign:'right'}}>{fmtUSD(n(m.cost||m.provider_cost),4)}</td>
                      </tr>
                    );
                  })}</tbody>
                </table>
              </div>
            )}

            {/* Exports — plain links so the browser handles the download/auth cookie */}
            <div className="row" style={{gap:8,flexWrap:'wrap'}}>
              <button className="btn btn-ghost btn-sm"
                onClick={function(){ window.open('/v1/admin/organizations/'+orgId+'/billing/'+month+'/erp-export?format=csv','_blank'); }}>
                Export ERP (CSV)
              </button>
              <button className="btn btn-ghost btn-sm"
                onClick={function(){ window.open('/v1/admin/organizations/'+orgId+'/billing/'+month+'/erp-export?format=json','_blank'); }}>
                Export ERP (JSON)
              </button>
            </div>
          </div>}
      </SectionCard>

      {/* ---- Usage time series (arbitrary range + drill-down) ---- */}
      <UsageAnalyticsWidget
        apiCall={function(params){ return NR_API.orgUsageTimeseries(orgId, params); }}
        title="Usage"
      />
    </div>
  );
}

window.CustomerAnalyticsPanel = CustomerAnalyticsPanel;
