/* global React, WORKS, RELEASES, SOCIETIES, Drawer, CloseBtn, KV, toast, Pill, Ic */
// ───────────────────────────────────────────────────────────── STATEMENT DETAIL DRAWER
// Opens when a row in the Royalties statement-inbox table is clicked.
// Fires on `astro-open-statement` event with detail.id = '<srcId>·<periodId>'.
// Shapes data as if from astro.royalty_statements + astro.royalty_statement_lines.

(function(){
const { useState, useMemo, useEffect } = React;

// Tiny PRNG seeded by string so per-statement detail data is stable
const seed = (s) => { let h=2166136261; for (let i=0;i<s.length;i++){ h^=s.charCodeAt(i); h=Math.imul(h,16777619); } return ()=>{ h=Math.imul(h^(h>>>15), 2246822507); h=Math.imul(h^(h>>>13), 3266489909); return ((h^=h>>>16)>>>0)/4294967296; }; };

// Country codes used to fabricate territory rows on lines.
// In production these come from ref.territories joined to royalty_statement_lines.territory_id.
const TERRITORIES = [
  ['US','United States'],['GB','United Kingdom'],['DE','Germany'],['FR','France'],
  ['JP','Japan'],['CA','Canada'],['BR','Brazil'],['MX','Mexico'],
  ['AU','Australia'],['ES','Spain'],['IT','Italy'],['NL','Netherlands'],
  ['SE','Sweden'],['KR','South Korea'],['IN','India'],['ZA','South Africa'],
];

// Royalty types — mirrors ref.royalty_types
const ROYALTY_TYPES = [
  { code:'STREAM_NET',  label:'Streaming · ad-supported',     short:'STR-AD' },
  { code:'STREAM_PREM', label:'Streaming · premium',          short:'STR-P'  },
  { code:'DOWNLOAD',    label:'Permanent download',           short:'DL'     },
  { code:'MECH_PHYS',   label:'Mechanical · physical',        short:'MEC-P'  },
  { code:'MECH_DIG',    label:'Mechanical · digital',         short:'MEC-D'  },
  { code:'PERF_BCAST',  label:'Performance · broadcast',      short:'PERF-B' },
  { code:'PERF_PUBLIC', label:'Performance · public venue',   short:'PERF-V' },
  { code:'PERF_DIG',    label:'Performance · digital',        short:'PERF-D' },
  { code:'NEIGHBOUR',   label:'Neighboring rights',           short:'NEIGH'  },
  { code:'SYNC',        label:'Synchronisation',              short:'SYNC'   },
  { code:'PUBLIC_PERF', label:'Public performance · radio',   short:'PERF-R' },
];

// Map source kind → which royalty types are plausible
const TYPES_BY_KIND = {
  DSP:    ['STREAM_NET','STREAM_PREM','DOWNLOAD','MECH_DIG'],
  PRO:    ['PERF_BCAST','PERF_PUBLIC','PERF_DIG','PUBLIC_PERF'],
  MRO:    ['MECH_DIG','MECH_PHYS'],
  NRO:    ['NEIGHBOUR'],
  SUBPUB: ['MECH_DIG','PERF_BCAST','SYNC','STREAM_PREM'],
};

// ─────────────────────────── real-statement line adapter
// Convert a real statement (from window.__STMT_INDEX) into the line shape
// the existing OverviewTab/LinesTab/AnomaliesTab consume. This keeps all the
// UI machinery working without changing tab implementations.
function buildRealLines(realStmt) {
  if (!realStmt || !realStmt.lines) return [];
  const parser = realStmt.parser;
  return realStmt.lines.map((l, i) => {
    let title, sub, iswc, isrc, workId, recId, terrCode, terrName, typeMeta, units, rate, gross, matched, anomaly;
    if (parser === 'rsd-distributor') {
      title = l.trackTitle || l.releaseName || '—';
      sub = l.trackArtists || l.releaseArtists || '';
      isrc = l.isrc;
      workId = l.__matchedWorkId;
      recId  = l.__matchedRecordingId;
      // Map RSD 3-letter territory to 2-letter for display (best-effort, fallback to raw)
      terrCode = (l.territory || '').slice(0,2).toUpperCase();
      terrName = l.territory;
      const typeCode = (l.delivery === 'Streaming') ? 'STREAM_PREM' : (l.delivery === 'Download') ? 'DOWNLOAD' : 'MECH_DIG';
      typeMeta = ROYALTY_TYPES.find(t => t.code === typeCode);
      units = l.count;
      gross = l.royaltyUsd;
      rate  = units ? gross / units : 0;
      matched = !!recId;
    } else if (parser === 'bmi') {
      title = l.titleName || '—';
      sub = l.participantName || '';
      workId = l.__matchedWorkId;
      // Country: BMI uses full name; pass through
      terrCode = (l.country || '').slice(0,2).toUpperCase();
      terrName = l.country;
      const typeCode = /YOUTUBE|SPOTIFY|TIKTOK|PANDORA|APPLE/i.test(l.perfSource||'') ? 'PERF_DIG' : 'PERF_BCAST';
      typeMeta = ROYALTY_TYPES.find(t => t.code === typeCode);
      units = l.perfCount || 1;
      gross = l.royaltyUsd;
      rate  = units ? gross / units : 0;
      matched = !!workId;
    } else if (parser === 'ascap-foreign') {
      title = l.workTitle || '—';
      sub = l.licensor || '';
      workId = l.__matchedWorkId;
      terrCode = (l.territory || l.country || '').slice(0,2).toUpperCase();
      terrName = l.country || l.territory;
      typeMeta = ROYALTY_TYPES.find(t => t.code === 'PERF_BCAST');
      units = 1; gross = l.royaltyUsd; rate = gross;
      matched = !!workId;
    } else if (parser === 'ascap-intl') {
      title = l.workTitle || '—';
      sub = l.performingArtist || l.composerName || l.musicUser || '';
      workId = l.__matchedWorkId;
      terrCode = (l.territory || '').slice(0,2).toUpperCase();
      terrName = l.territory;
      const typeCode = /STREAM|YOUTUBE|SPOTIFY|TIKTOK|APPLE|PANDORA/i.test(l.perfSource||'') ? 'PERF_DIG' : 'PERF_BCAST';
      typeMeta = ROYALTY_TYPES.find(t => t.code === typeCode);
      units = l.plays || 1;
      gross = l.royaltyUsd;
      rate  = units ? gross / units : 0;
      matched = !!workId;
    } else if (parser === 'hfa-mech') {
      title = l.songTitle || '—';
      sub = l.licenseeName || l.artists || '';
      isrc = l.isrc;
      workId = l.__matchedWorkId;
      recId  = l.__matchedRecordingId;
      terrCode = (l.territory || '').slice(0,2).toUpperCase();
      terrName = l.territory;
      // HFA covers digital + physical mech depending on configCode
      const isPhys = /^(CD|LP|VINYL|SVR|7IN|12IN)$/i.test(l.configCode || '');
      const typeCode = isPhys ? 'MECH_PHYS' : 'MECH_DIG';
      typeMeta = ROYALTY_TYPES.find(t => t.code === typeCode);
      units = l.units || 0;
      gross = l.grossAmount || 0;
      rate  = units ? gross / units : 0;
      matched = !!(workId || recId);
    } else if (parser === 'tiktok-mri') {
      title = l.compositionTitle || '—';
      sub = l.artist || l.publisher || '';
      isrc = l.isrc;
      workId = l.__matchedWorkId;
      recId  = l.__matchedRecordingId;
      terrCode = 'XX';
      terrName = 'Worldwide';
      typeMeta = ROYALTY_TYPES.find(t => t.code === 'PERF_DIG');
      units = l.creations || 0;
      gross = l.royaltyAmount || 0;
      rate  = units ? gross / units : 0;
      matched = !!(workId || recId);
    } else {
      typeMeta = ROYALTY_TYPES[0]; units = 0; rate = 0; gross = 0; matched = false;
    }
    return {
      id: realStmt.id + '·' + i,
      title, sub, iswc: null, isrc, workId, recId, releaseId: null,
      territory: terrCode, territoryName: terrName,
      type: typeMeta || ROYALTY_TYPES[0],
      units, rate, gross,
      matched,
      anomaly: !matched ? 'unmatched' : null,
    };
  });
}

// ─────────────────────────── line generation
function buildLines(stmt, count) {
  const r = seed(stmt.id+'·lines');
  const works = (typeof window.WORKS !== 'undefined') ? window.WORKS : [];
  const recs  = (typeof window.RECORDINGS !== 'undefined') ? window.RECORDINGS : [];
  const releases = (typeof window.RELEASES !== 'undefined') ? window.RELEASES : [];
  const types = TYPES_BY_KIND[stmt.source.kind] || ['STREAM_PREM'];

  const lines = [];
  // Generate `count` synthetic lines, each tied (probabilistically) to a known work or recording
  for (let i=0; i<count; i++) {
    const useWork = stmt.source.kind === 'PRO' || stmt.source.kind === 'MRO' || r() < 0.5;
    const useRec  = !useWork && (stmt.source.kind === 'DSP' || stmt.source.kind === 'NRO');
    const w = (useWork && works.length) ? works[Math.floor(r()*works.length)] : null;
    const rec = (useRec && recs.length) ? recs[Math.floor(r()*recs.length)] : null;
    const rel = releases.length ? releases[Math.floor(r()*releases.length)] : null;
    const terr = TERRITORIES[Math.floor(r()*TERRITORIES.length)];
    const typeCode = types[Math.floor(r()*types.length)];
    const typeMeta = ROYALTY_TYPES.find(t => t.code === typeCode);
    const units = stmt.source.kind === 'DSP' ? Math.round(50 + r()*180000) : Math.round(1 + r()*120);
    const rate = stmt.source.kind === 'DSP' ? (0.0008 + r()*0.0042) : (0.4 + r()*8.5);
    const gross = Math.round((units * rate) * 100) / 100;
    // Match status: did the parser successfully link this to a known work/recording?
    const matched = (w || rec) ? (r() > 0.06) : false; // 94% match rate when we have a candidate
    lines.push({
      id: stmt.id + '·' + i,
      title: w ? w.title : (rec ? rec.title : `[unknown · ${typeMeta.short}]`),
      sub:   w ? w.writers?.[0] : (rec ? rec.artist : ''),
      iswc:  w ? w.iswc : null,
      isrc:  rec ? rec.isrc : null,
      workId: w?.id, recId: rec?.id, releaseId: rel?.id,
      territory: terr[0], territoryName: terr[1],
      type: typeMeta,
      units, rate, gross,
      matched,
      anomaly: !matched ? 'unmatched' : (r() < 0.04 ? 'duplicate' : (r() < 0.03 ? 'rate-variance' : null)),
    });
  }
  return lines;
}

// ─────────────────────────── helpers
const fmtCur = (v, ccy='USD') => {
  if (v == null || isNaN(v)) return '—';
  if (ccy === 'JPY') return '¥' + Math.round(v).toLocaleString();
  if (ccy === 'EUR') return '€' + Math.round(v).toLocaleString();
  if (ccy === 'GBP') return '£' + Math.round(v).toLocaleString();
  return '$' + Math.round(v).toLocaleString();
};
const fmtCur2 = (v, ccy='USD') => {
  if (v == null || isNaN(v)) return '—';
  const sym = ccy==='EUR'?'€':ccy==='GBP'?'£':ccy==='JPY'?'¥':'$';
  return sym + (ccy==='JPY' ? Math.round(v).toLocaleString() : v.toFixed(2));
};

function StatusChip({ s }) {
  const META = window.__STMT_STATUS_META;
  const m = META[s];
  return (
    <span className="ff-mono upper" style={{
      display:'inline-flex',alignItems:'center',gap:6,padding:'4px 10px',fontSize:10,
      letterSpacing:'.1em',fontWeight:600,
      color: m.invert ? 'var(--bg)' : m.fg,
      background: m.bg, border:`1px solid ${m.bd}`,
    }}>
      <span style={{width:5,height:5,background:m.dot,display:'inline-block'}}/>
      {m.label}
    </span>
  );
}

// ─────────────────────────── tab content components

function OverviewTab({ stmt, lines }) {
  // Money breakdown by royalty type
  const byType = {};
  lines.forEach(l => {
    const k = l.type.code;
    if (!byType[k]) byType[k] = { type:l.type, gross:0, units:0, n:0 };
    byType[k].gross += l.gross;
    byType[k].units += l.units;
    byType[k].n += 1;
  });
  const types = Object.values(byType).sort((a,b)=>b.gross-a.gross);
  const total = lines.reduce((a,l)=>a+l.gross, 0);

  // Territory breakdown
  const byTerr = {};
  lines.forEach(l => {
    if (!byTerr[l.territory]) byTerr[l.territory] = { code:l.territory, name:l.territoryName, gross:0, n:0 };
    byTerr[l.territory].gross += l.gross;
    byTerr[l.territory].n += 1;
  });
  const terrs = Object.values(byTerr).sort((a,b)=>b.gross-a.gross);
  const terrTop = terrs.slice(0,8);
  const terrOther = terrs.slice(8).reduce((a,t)=>a+t.gross,0);

  return (
    <div>
      {/* Money summary strip */}
      <div style={{padding:'24px 28px',borderBottom:'1px solid var(--rule)',display:'grid',gridTemplateColumns:'1fr 1fr 1fr',gap:0}}>
        <div>
          <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em',marginBottom:8}}>GROSS · {stmt.currency}</div>
          <div className="ff-display num" style={{fontSize:32,fontWeight:600,letterSpacing:'-0.03em',lineHeight:1}}>{fmtCur(stmt.gross, stmt.currency)}</div>
        </div>
        <div style={{borderLeft:'1px solid var(--rule)',paddingLeft:24}}>
          <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em',marginBottom:8}}>USD EQUIV.</div>
          <div className="ff-display num" style={{fontSize:32,fontWeight:600,letterSpacing:'-0.03em',lineHeight:1}}>{fmtCur(stmt.grossUsd)}</div>
          <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:4,letterSpacing:'.04em'}}>FX · {stmt.currency==='USD'?'1.0000':window.__STMT_FX[stmt.currency]?.toFixed(4)}</div>
        </div>
        <div style={{borderLeft:'1px solid var(--rule)',paddingLeft:24}}>
          <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em',marginBottom:8}}>NET (after admin)</div>
          <div className="ff-display num" style={{fontSize:32,fontWeight:600,letterSpacing:'-0.03em',lineHeight:1,color:'var(--ink-3)'}}>{fmtCur(stmt.grossUsd*0.85)}</div>
          <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:4,letterSpacing:'.04em'}}>15% admin fee</div>
        </div>
      </div>

      {/* Metadata grid */}
      <div style={{padding:'20px 28px',borderBottom:'1px solid var(--rule)'}}>
        <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:14}}>STATEMENT METADATA</div>
        <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:'14px 24px'}}>
          <KV k="SOURCE"        v={stmt.source.name}/>
          <KV k="KIND"          v={stmt.source.kind}/>
          <KV k="PERIOD"        v={`${stmt.period.start} → ${stmt.period.end}`}/>
          <KV k="FREQUENCY"     v={stmt.source.freq}/>
          <KV k="EXPECTED"      v={stmt.expectedDate.toISOString().slice(0,10)}/>
          <KV k="PROCESSED"     v={stmt.processedAt ? stmt.processedAt.toISOString().slice(0,10) : '—'}/>
          <KV k="LINES"         v={`${lines.length.toLocaleString()} · ${lines.filter(l=>l.matched).length} matched`}/>
          <KV k="ANOMALIES"     v={`${lines.filter(l=>l.anomaly).length} flagged`}/>
          <KV k="STATEMENT ID"  v={stmt.id.toUpperCase()}/>
          <KV k="ASTRO AID"     v="a93f-dc4e-…7720"/>
        </div>
      </div>

      {/* By royalty type */}
      <div style={{padding:'20px 28px',borderBottom:'1px solid var(--rule)'}}>
        <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:14}}>BY ROYALTY TYPE</div>
        {types.length === 0 && <div style={{fontSize:13,color:'var(--ink-3)'}}>No lines yet.</div>}
        {types.map(t => {
          const pct = total > 0 ? Math.round(t.gross / total * 100) : 0;
          return (
            <div key={t.type.code} style={{display:'grid',gridTemplateColumns:'180px 1fr 100px 50px',gap:12,padding:'9px 0',borderBottom:'1px solid var(--rule-soft)',alignItems:'center'}}>
              <div>
                <div style={{fontSize:12,fontWeight:500}}>{t.type.label}</div>
                <div className="ff-mono" style={{fontSize:9,color:'var(--ink-3)',marginTop:2,letterSpacing:'.04em'}}>{t.type.code} · {t.n} lines · {t.units.toLocaleString()} units</div>
              </div>
              <div style={{height:5,background:'var(--bg-2)',position:'relative'}}>
                <div style={{position:'absolute',inset:0,width:`${pct}%`,background:'var(--ink)'}}/>
              </div>
              <span className="ff-mono num" style={{fontSize:12,textAlign:'right'}}>{fmtCur2(t.gross, stmt.currency)}</span>
              <span className="ff-mono num" style={{fontSize:10,color:'var(--ink-3)',textAlign:'right'}}>{pct}%</span>
            </div>
          );
        })}
      </div>

      {/* By territory */}
      <div style={{padding:'20px 28px',borderBottom:'1px solid var(--rule)'}}>
        <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:14}}>BY TERRITORY · TOP 8</div>
        <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:0,border:'1px solid var(--rule-soft)'}}>
          {terrTop.map((t,i) => {
            const pct = total > 0 ? Math.round(t.gross / total * 100) : 0;
            return (
              <div key={t.code} style={{padding:'12px',borderRight: (i+1)%4 ? '1px solid var(--rule-soft)' : 'none', borderBottom: i < 4 ? '1px solid var(--rule-soft)' : 'none'}}>
                <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em'}}>{t.code}</div>
                <div className="ff-display num" style={{fontSize:18,fontWeight:600,marginTop:2,letterSpacing:'-0.02em'}}>{fmtCur(t.gross, stmt.currency)}</div>
                <div className="ff-mono num" style={{fontSize:9,color:'var(--ink-4)',marginTop:2}}>{pct}% · {t.n} lines</div>
              </div>
            );
          })}
        </div>
        {terrOther > 0 && <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:8,letterSpacing:'.04em'}}>+ {terrs.length - 8} more territories · {fmtCur(terrOther, stmt.currency)}</div>}
      </div>
    </div>
  );
}

function LinesTab({ stmt, lines }) {
  const [search, setSearch] = useState('');
  const [matchFilter, setMatchFilter] = useState('all'); // all | matched | unmatched
  const [page, setPage] = useState(0);
  const PAGE_SIZE = 30;

  const filtered = useMemo(() => {
    let r = lines;
    if (matchFilter === 'matched') r = r.filter(l => l.matched);
    if (matchFilter === 'unmatched') r = r.filter(l => !l.matched);
    if (search.trim()) {
      const q = search.toLowerCase();
      r = r.filter(l => l.title.toLowerCase().includes(q) || l.iswc?.toLowerCase().includes(q) || l.isrc?.toLowerCase().includes(q));
    }
    return r;
  }, [lines, matchFilter, search]);

  const pageLines = filtered.slice(page*PAGE_SIZE, (page+1)*PAGE_SIZE);
  const totalPages = Math.ceil(filtered.length / PAGE_SIZE) || 1;

  return (
    <div>
      <div style={{padding:'18px 28px',borderBottom:'1px solid var(--rule)'}}>
        <div style={{display:'flex',gap:8,alignItems:'center',marginBottom:14,flexWrap:'wrap'}}>
          {[
            {k:'all',l:'ALL',n:lines.length},
            {k:'matched',l:'MATCHED',n:lines.filter(l=>l.matched).length},
            {k:'unmatched',l:'UNMATCHED',n:lines.filter(l=>!l.matched).length},
          ].map(f => (
            <button key={f.k} onClick={()=>{setMatchFilter(f.k);setPage(0);}} className="ff-mono upper" style={{
              fontSize:10,letterSpacing:'.08em',padding:'5px 10px',
              background: matchFilter===f.k ? 'var(--ink)' : 'transparent',
              color: matchFilter===f.k ? 'var(--bg)' : 'var(--ink-2)',
              border:'1px solid var(--rule)',cursor:'pointer',
            }}>{f.l} <span style={{opacity:.55,marginLeft:4}}>{f.n}</span></button>
          ))}
          <span style={{flex:1}}/>
          <input value={search} onChange={e=>{setSearch(e.target.value);setPage(0);}} placeholder="search title / ISWC / ISRC…" className="ff-mono"
            style={{fontSize:11,padding:'5px 10px',border:'1px solid var(--rule)',background:'var(--bg)',color:'var(--ink)',width:200,outline:'none'}}/>
        </div>
        <div style={{display:'grid',gridTemplateColumns:'1.6fr 60px 70px 70px 80px 14px',gap:10,
          padding:'8px 12px',background:'var(--bg-2)',border:'1px solid var(--rule)',borderBottom:0}}>
          <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em'}}>WORK / RECORDING</span>
          <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em',textAlign:'center'}}>TERR</span>
          <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em',textAlign:'right'}}>UNITS</span>
          <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em',textAlign:'right'}}>RATE</span>
          <span className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.08em',textAlign:'right'}}>GROSS</span>
          <span/>
        </div>
        <div style={{border:'1px solid var(--rule)',borderTop:0}}>
          {pageLines.length === 0 && <div style={{padding:'30px',textAlign:'center',color:'var(--ink-3)',fontSize:13}}>No lines match these filters.</div>}
          {pageLines.map(l => (
            <div key={l.id}
              onClick={() => {
                if (l.workId) window.dispatchEvent(new CustomEvent('astro-open-statement-close-then-work',{detail:{id:l.workId}}));
                else if (l.recId) window.dispatchEvent(new CustomEvent('astro-open-recording',{detail:{id:l.recId}}));
              }}
              style={{display:'grid',gridTemplateColumns:'1.6fr 60px 70px 70px 80px 14px',gap:10,
                padding:'9px 12px',borderBottom:'1px solid var(--rule-soft)',alignItems:'center',
                cursor: (l.workId || l.recId) ? 'pointer' : 'default',
                background: !l.matched ? 'rgba(199,149,56,.05)' : 'transparent'}}>
              <div style={{minWidth:0}}>
                <div style={{fontSize:12,fontWeight:500,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis',display:'flex',alignItems:'center',gap:6}}>
                  {!l.matched && <span style={{flexShrink:0,width:5,height:5,background:'#c79538'}}/>}
                  <span style={{minWidth:0,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{l.title}</span>
                </div>
                <div className="ff-mono" style={{fontSize:9,color:'var(--ink-3)',marginTop:2,letterSpacing:'.04em',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>
                  {l.type.short} · {l.iswc || l.isrc || (l.matched ? 'matched' : 'no identifier')}
                </div>
              </div>
              <span className="ff-mono upper" style={{fontSize:10,color:'var(--ink-2)',letterSpacing:'.05em',textAlign:'center'}}>{l.territory}</span>
              <span className="ff-mono num" style={{fontSize:11,textAlign:'right'}}>{l.units.toLocaleString()}</span>
              <span className="ff-mono num" style={{fontSize:10,color:'var(--ink-3)',textAlign:'right'}}>{l.rate < 0.01 ? l.rate.toFixed(5) : l.rate.toFixed(3)}</span>
              <span className="ff-mono num" style={{fontSize:11,textAlign:'right',fontWeight:600}}>{fmtCur2(l.gross, stmt.currency)}</span>
              <Ic.Right width={11} height={11} style={{color:'var(--ink-4)'}}/>
            </div>
          ))}
        </div>
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginTop:14}}>
          <span className="ff-mono upper" style={{fontSize:10,color:'var(--ink-3)',letterSpacing:'.1em'}}>
            {filtered.length === 0 ? '0 lines' : `${page*PAGE_SIZE+1}–${Math.min((page+1)*PAGE_SIZE, filtered.length)} OF ${filtered.length.toLocaleString()}`}
          </span>
          <div style={{display:'flex',gap:6}}>
            <button onClick={()=>setPage(Math.max(0,page-1))} disabled={page===0} className="ff-mono upper" style={{
              fontSize:10,letterSpacing:'.08em',padding:'5px 10px',
              background:'transparent',color: page===0 ? 'var(--ink-4)' : 'var(--ink-2)',
              border:'1px solid var(--rule)',cursor: page===0?'default':'pointer',
            }}>← PREV</button>
            <span className="ff-mono" style={{fontSize:10,padding:'5px 10px',color:'var(--ink-3)'}}>{page+1} / {totalPages}</span>
            <button onClick={()=>setPage(Math.min(totalPages-1,page+1))} disabled={page>=totalPages-1} className="ff-mono upper" style={{
              fontSize:10,letterSpacing:'.08em',padding:'5px 10px',
              background:'transparent',color: page>=totalPages-1 ? 'var(--ink-4)' : 'var(--ink-2)',
              border:'1px solid var(--rule)',cursor: page>=totalPages-1?'default':'pointer',
            }}>NEXT →</button>
          </div>
        </div>
      </div>
    </div>
  );
}

function AnomaliesTab({ stmt, lines, dec, decideLine }) {
  const flagged = lines.filter(l => l.anomaly);
  const groups = {
    'unmatched':     { label:'Unmatched lines',           desc:'Statement line could not be linked to a known work or recording.',  tone:'#c79538' },
    'duplicate':     { label:'Duplicate lines',           desc:'Same work + period + DSP appears more than once in this statement.', tone:'#c79538' },
    'rate-variance': { label:'Rate variance',             desc:'Rate is >25% off the period median for this royalty type.',         tone:'#a04432' },
  };
  const byKind = {};
  flagged.forEach(l => { if (!byKind[l.anomaly]) byKind[l.anomaly] = []; byKind[l.anomaly].push(l); });

  const totalAffected = flagged.reduce((a,l)=>a+l.gross, 0);
  return (
    <div style={{padding:'24px 28px'}}>
      {flagged.length === 0 ? (
        <div style={{padding:'48px 0',textAlign:'center'}}>
          <div className="ff-display" style={{fontSize:42,fontWeight:600,letterSpacing:'-0.04em',marginBottom:8}}>0</div>
          <div className="ff-mono upper" style={{fontSize:11,color:'var(--ink-3)',letterSpacing:'.1em'}}>NO ANOMALIES DETECTED</div>
          <div style={{fontSize:13,color:'var(--ink-3)',marginTop:8,maxWidth:300,margin:'8px auto 0'}}>
            All {lines.length.toLocaleString()} lines parsed cleanly and matched to known works or recordings.
          </div>
        </div>
      ) : (
        <>
          <div style={{display:'grid',gridTemplateColumns:'1fr 1fr 1fr',gap:0,border:'1px solid var(--rule)',marginBottom:24}}>
            <div style={{padding:18}}>
              <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em',marginBottom:8}}>FLAGGED</div>
              <div className="ff-display num" style={{fontSize:36,fontWeight:600,letterSpacing:'-0.04em',lineHeight:1,color:'#c79538'}}>{flagged.length}</div>
              <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:4}}>of {lines.length.toLocaleString()} lines</div>
            </div>
            <div style={{padding:18,borderLeft:'1px solid var(--rule)'}}>
              <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em',marginBottom:8}}>$ AFFECTED</div>
              <div className="ff-display num" style={{fontSize:36,fontWeight:600,letterSpacing:'-0.04em',lineHeight:1}}>{fmtCur(totalAffected, stmt.currency)}</div>
              <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:4}}>{Math.round(totalAffected/lines.reduce((a,l)=>a+l.gross,0)*100)}% of gross</div>
            </div>
            <div style={{padding:18,borderLeft:'1px solid var(--rule)'}}>
              <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em',marginBottom:8}}>SEVERITY</div>
              <div className="ff-display num" style={{fontSize:36,fontWeight:600,letterSpacing:'-0.04em',lineHeight:1}}>{flagged.filter(l=>l.anomaly==='rate-variance').length ? 'HIGH' : 'MED'}</div>
              <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:4}}>auto-blocks reconciliation</div>
            </div>
          </div>

          {Object.entries(byKind).map(([kind, items]) => (
            <div key={kind} style={{marginBottom:32}}>
              <div className="ff-mono upper" style={{fontSize:10,color:'var(--ink)',letterSpacing:'.12em',marginBottom:6,display:'flex',alignItems:'center',gap:8}}>
                <span style={{width:6,height:6,background:groups[kind].tone}}/>
                {groups[kind].label} · {items.length}
              </div>
              <div style={{fontSize:11,color:'var(--ink-3)',marginBottom:10,maxWidth:540}}>{groups[kind].desc}</div>
              <div style={{border:'1px solid var(--rule)'}}>
                {items.slice(0,8).map((l,i) => {
                  const d = dec && dec.lineDecisions ? dec.lineDecisions[l.id] : null;
                  return (
                  <div key={l.id} style={{display:'grid',gridTemplateColumns:'1fr 80px 80px 60px 60px',gap:12,
                    padding:'10px 14px',borderBottom: i < items.slice(0,8).length-1 ? '1px solid var(--rule-soft)' : 'none',
                    alignItems:'center',
                    background: d === 'matched' ? 'rgba(45,106,63,.06)' : d === 'accepted' ? 'rgba(0,0,0,.02)' : d === 'hold' ? 'rgba(160,68,50,.06)' : 'transparent',
                    opacity: d ? 0.78 : 1}}>
                    <div style={{minWidth:0}}>
                      <div style={{fontSize:12,fontWeight:500,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{l.title}</div>
                      <div className="ff-mono" style={{fontSize:9,color:'var(--ink-3)',marginTop:2,letterSpacing:'.04em'}}>
                        {l.type.short} · {l.territory}
                        {d && <span style={{marginLeft:8,color:d==='matched'?'#2d6a3f':d==='hold'?'#a04432':'var(--ink-2)',fontWeight:600}}>· {d.toUpperCase()}</span>}
                      </div>
                    </div>
                    <span className="ff-mono num" style={{fontSize:11,textAlign:'right'}}>{fmtCur2(l.gross, stmt.currency)}</span>
                    <button onClick={()=>{ decideLine && decideLine(l.id, 'matched'); toast(`Matching workflow opened: ${l.title}`); }}
                      className="ff-mono upper"
                      style={{fontSize:9,letterSpacing:'.06em',padding:'4px 8px',
                        background: d==='matched' ? '#2d6a3f' : 'var(--ink)',
                        color: d==='matched' ? '#fff' : 'var(--bg)',border:0,cursor:'pointer'}}>
                      {d==='matched' ? '✓ MATCH' : 'MATCH'}
                    </button>
                    <button onClick={()=>{ decideLine && decideLine(l.id, d==='accepted' ? null : 'accepted'); toast(d==='accepted' ? 'Decision cleared' : 'Line accepted as-is'); }}
                      className="ff-mono upper"
                      style={{fontSize:9,letterSpacing:'.06em',padding:'4px 8px',
                        background: d==='accepted' ? 'var(--ink)' : 'transparent',
                        color: d==='accepted' ? 'var(--bg)' : 'var(--ink-2)',
                        border:'1px solid var(--rule)',cursor:'pointer'}}>
                      {d==='accepted' ? '✓ ACCEPT' : 'ACCEPT'}
                    </button>
                    <button onClick={()=>{ decideLine && decideLine(l.id, d==='hold' ? null : 'hold'); toast(d==='hold' ? 'Hold removed' : 'Line marked for hold'); }}
                      className="ff-mono upper"
                      style={{fontSize:9,letterSpacing:'.06em',padding:'4px 8px',
                        background: d==='hold' ? '#a04432' : 'transparent',
                        color: d==='hold' ? '#fff' : '#a04432',
                        border:'1px solid #a04432',cursor:'pointer'}}>
                      {d==='hold' ? '✓ HOLD' : 'HOLD'}
                    </button>
                  </div>
                  );
                })}
                {items.length > 8 && (
                  <div style={{padding:'10px 14px',background:'var(--bg-2)',borderTop:'1px solid var(--rule)'}}>
                    <span className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',color:'var(--ink-3)'}}>+ {items.length - 8} more</span>
                  </div>
                )}
              </div>
            </div>
          ))}
        </>
      )}
    </div>
  );
}

function ReconciliationTab({ stmt, lines, liveStatus, onMarkReconciled }) {
  // Mock reconciliation checks
  const rec = useMemo(() => {
    const r = seed(stmt.id+'·recon');
    const priorPeriodGross = stmt.grossUsd * (0.92 + r()*0.16); // ±8%
    const variance = (stmt.grossUsd - priorPeriodGross) / priorPeriodGross;
    const forecastGross = stmt.grossUsd * (0.94 + r()*0.12);
    const forecastVar = (stmt.grossUsd - forecastGross) / forecastGross;
    const fxAtClose = stmt.currency === 'USD' ? 1 : window.__STMT_FX[stmt.currency];
    const fxAtPay = fxAtClose * (1 + (r()-.5) * 0.04);
    const withholding = stmt.currency === 'JPY' ? Math.round(stmt.gross*0.10) : (stmt.currency === 'EUR' ? 0 : Math.round(stmt.gross * 0.05));
    return { priorPeriodGross, variance, forecastGross, forecastVar, fxAtClose, fxAtPay, withholding };
  }, [stmt.id]);

  const matchedLines = lines.filter(l => l.matched).length;
  const matchRate = lines.length ? matchedLines / lines.length : 0;

  const checks = [
    { label:'Lines parsed', pass: lines.length > 0, detail:`${lines.length.toLocaleString()} lines extracted from raw file` },
    { label:'Match rate ≥ 90%', pass: matchRate >= 0.9, detail:`${(matchRate*100).toFixed(1)}% matched · ${lines.length-matchedLines} unmatched` },
    { label:'Sum verifies', pass: true, detail:`Σ lines = ${fmtCur(lines.reduce((a,l)=>a+l.gross,0), stmt.currency)} = stated gross (±0.01)` },
    { label:'Period variance ≤ 15%', pass: Math.abs(rec.variance) < 0.15, detail:`${rec.variance >= 0 ? '+' : ''}${(rec.variance*100).toFixed(1)}% vs prior period` },
    { label:'Forecast variance ≤ 12%', pass: Math.abs(rec.forecastVar) < 0.12, detail:`${rec.forecastVar >= 0 ? '+' : ''}${(rec.forecastVar*100).toFixed(1)}% vs forecast` },
    { label:'No flagged anomalies blocking', pass: lines.filter(l=>l.anomaly==='rate-variance').length === 0, detail:`${lines.filter(l=>l.anomaly==='rate-variance').length} rate-variance flags` },
  ];
  const allPass = checks.every(c => c.pass);

  return (
    <div style={{padding:'24px 28px'}}>
      {/* Banner */}
      <div style={{padding:'16px 18px',background: liveStatus === 'reconciled' ? 'rgba(45,106,63,.1)' : allPass ? 'rgba(45,106,63,.06)' : 'rgba(199,149,56,.07)',
        border: `1px solid ${(liveStatus === 'reconciled' || allPass) ? '#2d6a3f' : '#c79538'}`,marginBottom:24,display:'flex',justifyContent:'space-between',alignItems:'center'}}>
        <div>
          <div className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',color: (liveStatus === 'reconciled' || allPass) ? '#2d6a3f' : '#c79538',marginBottom:4}}>
            {liveStatus === 'reconciled' ? '✓ RECONCILED' : (allPass ? '✓ READY TO RECONCILE' : '⚠ CHECKS PENDING')}
          </div>
          <div style={{fontSize:13,color:'var(--ink-2)'}}>
            {liveStatus === 'reconciled' ? 'Statement marked reconciled. Awaiting payout cycle.' : (allPass ? 'All reconciliation gates passed. Statement can be marked reconciled.' : `${checks.filter(c=>!c.pass).length} of ${checks.length} checks need review before reconciliation.`)}
          </div>
        </div>
        {allPass && liveStatus !== 'reconciled' && liveStatus !== 'paid' && (
          <button onClick={onMarkReconciled} className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',padding:'8px 14px',background:'#2d6a3f',color:'#fff',border:0,fontWeight:600,cursor:'pointer'}}>
            MARK RECONCILED
          </button>
        )}
      </div>

      {/* Checklist */}
      <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:14}}>RECONCILIATION CHECKS · {checks.filter(c=>c.pass).length}/{checks.length} PASS</div>
      <div style={{border:'1px solid var(--rule)',marginBottom:32}}>
        {checks.map((c,i) => (
          <div key={c.label} style={{display:'grid',gridTemplateColumns:'24px 1fr 90px',gap:12,
            padding:'12px 16px', borderBottom: i < checks.length-1 ? '1px solid var(--rule-soft)' : 'none',alignItems:'center'}}>
            <span style={{width:18,height:18,display:'flex',alignItems:'center',justifyContent:'center',
              background: c.pass ? '#2d6a3f' : 'transparent',
              border: c.pass ? 0 : '1px solid #c79538',
              color: c.pass ? '#fff' : '#c79538',fontSize:11,fontWeight:600}}>
              {c.pass ? '✓' : '!'}
            </span>
            <div>
              <div style={{fontSize:13,fontWeight:500,marginBottom:2}}>{c.label}</div>
              <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',letterSpacing:'.04em'}}>{c.detail}</div>
            </div>
            <span className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',textAlign:'right',color: c.pass ? '#2d6a3f' : '#c79538'}}>{c.pass ? 'PASS' : 'REVIEW'}</span>
          </div>
        ))}
      </div>

      {/* Variance — period over period */}
      <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:14}}>VARIANCE ANALYSIS</div>
      <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:0,border:'1px solid var(--rule)',marginBottom:32}}>
        <div style={{padding:'16px 18px'}}>
          <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em',marginBottom:6}}>VS PRIOR PERIOD</div>
          <div style={{display:'flex',alignItems:'baseline',gap:8}}>
            <span className="ff-display num" style={{fontSize:28,fontWeight:600,letterSpacing:'-0.03em',color: Math.abs(rec.variance)<0.15 ? 'var(--ink)' : '#c79538'}}>
              {rec.variance >= 0 ? '+' : ''}{(rec.variance*100).toFixed(1)}%
            </span>
            <span className="ff-mono" style={{fontSize:11,color:'var(--ink-3)'}}>
              {fmtCur(stmt.grossUsd - rec.priorPeriodGross)}
            </span>
          </div>
          <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:4,letterSpacing:'.04em'}}>
            prior · {fmtCur(rec.priorPeriodGross)} · current · {fmtCur(stmt.grossUsd)}
          </div>
        </div>
        <div style={{padding:'16px 18px',borderLeft:'1px solid var(--rule)'}}>
          <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em',marginBottom:6}}>VS FORECAST</div>
          <div style={{display:'flex',alignItems:'baseline',gap:8}}>
            <span className="ff-display num" style={{fontSize:28,fontWeight:600,letterSpacing:'-0.03em',color: Math.abs(rec.forecastVar)<0.12 ? 'var(--ink)' : '#c79538'}}>
              {rec.forecastVar >= 0 ? '+' : ''}{(rec.forecastVar*100).toFixed(1)}%
            </span>
            <span className="ff-mono" style={{fontSize:11,color:'var(--ink-3)'}}>
              {fmtCur(stmt.grossUsd - rec.forecastGross)}
            </span>
          </div>
          <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginTop:4,letterSpacing:'.04em'}}>
            forecast · {fmtCur(rec.forecastGross)} · actual · {fmtCur(stmt.grossUsd)}
          </div>
        </div>
      </div>

      {/* FX + withholding */}
      <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:14}}>CURRENCY · WITHHOLDING</div>
      <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:0,border:'1px solid var(--rule)',marginBottom:32}}>
        <div style={{padding:'16px 18px'}}>
          <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em',marginBottom:6}}>FX RATE · {stmt.currency}/USD</div>
          <div style={{fontSize:13,color:'var(--ink-2)',marginBottom:8}}>
            Statement converted using rate at period close. Re-base if material movement.
          </div>
          <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:14}}>
            <div>
              <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em',marginBottom:3}}>AT CLOSE</div>
              <div className="ff-mono num" style={{fontSize:14,fontWeight:600}}>{rec.fxAtClose.toFixed(4)}</div>
            </div>
            <div>
              <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em',marginBottom:3}}>AT PAYOUT (PROJ)</div>
              <div className="ff-mono num" style={{fontSize:14,fontWeight:600,color: Math.abs(rec.fxAtPay-rec.fxAtClose)/rec.fxAtClose > 0.02 ? '#c79538' : 'var(--ink)'}}>{rec.fxAtPay.toFixed(4)}</div>
            </div>
          </div>
        </div>
        <div style={{padding:'16px 18px',borderLeft:'1px solid var(--rule)'}}>
          <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.1em',marginBottom:6}}>WITHHOLDING TAX</div>
          <div style={{fontSize:13,color:'var(--ink-2)',marginBottom:8}}>
            Source-country tax already deducted before remittance.
          </div>
          <div style={{display:'flex',alignItems:'baseline',gap:8}}>
            <span className="ff-display num" style={{fontSize:24,fontWeight:600,letterSpacing:'-0.02em'}}>{fmtCur(rec.withholding, stmt.currency)}</span>
            <span className="ff-mono" style={{fontSize:11,color:'var(--ink-3)'}}>{stmt.gross > 0 ? Math.round(rec.withholding/stmt.gross*100) : 0}%</span>
          </div>
        </div>
      </div>

      {/* Sign-off */}
      <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:14}}>SIGN-OFF</div>
      <div style={{border:'1px solid var(--rule)',padding:'18px 18px'}}>
        <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:14,marginBottom:14}}>
          <KV k="PARSED BY"   v="parser v3.4.1 · auto"/>
          <KV k="MATCHED BY"  v="matcher v2.1 · auto"/>
          <KV k="REVIEWED BY" v={stmt.status === 'paid' || stmt.status === 'reconciled' ? 'a.cohen · 2 days ago' : '— pending'}/>
          <KV k="APPROVED BY" v={stmt.status === 'paid' ? 'p.tibbets · 1 day ago' : '— pending'}/>
        </div>
        <textarea placeholder="Reconciliation notes…" className="ff-mono" style={{
          width:'100%',minHeight:80,padding:10,fontSize:11,border:'1px solid var(--rule)',
          background:'var(--bg)',color:'var(--ink)',outline:'none',resize:'vertical',boxSizing:'border-box',
        }}/>
      </div>
    </div>
  );
}

function HistoryTab({ stmt, lines, liveStatus }) {
  // Build timeline based on status (use the live decision-store status if user has acted)
  const status = liveStatus || stmt.status;
  const closeDate = new Date(stmt.period.closed);
  const daysAfter = (n) => new Date(closeDate.getTime() + n*86400000).toISOString().slice(0,10) + ' ' + (10+(n%8)).toString().padStart(2,'0') + ':' + (10+(n*7%50)).toString().padStart(2,'0');

  const events = [
    { stage:'expected',  done:true,  date:daysAfter(0),                              who:'system',           msg:`Period ${stmt.period.label} closed · statement expected from ${stmt.source.name}` },
    { stage:'received',  done:!['pending','late'].includes(status),                  date:stmt.processedAt ? daysAfter(stmt.source.cadenceDays - 5) : null,  who:'system',  msg:`Raw statement file received · ${(lines.length*8).toLocaleString()} bytes` },
    { stage:'parsing',   done:!['pending','late','received'].includes(status),       date:stmt.processedAt ? daysAfter(stmt.source.cadenceDays - 4) : null,  who:'parser v3.4.1', msg:`Parsing started · template ${stmt.source.name.toLowerCase().replace(/[^a-z]/g,'_')}_v2` },
    { stage:'parsed',    done:['parsed','matched','reconciled','paid'].includes(status), date:stmt.processedAt ? daysAfter(stmt.source.cadenceDays - 4) : null, who:'parser v3.4.1', msg:`${lines.length.toLocaleString()} lines extracted · ${lines.filter(l=>l.matched).length} pre-matched on ID` },
    { stage:'matched',   done:['matched','reconciled','paid'].includes(status),      date:stmt.processedAt ? daysAfter(stmt.source.cadenceDays - 3) : null,  who:'matcher v2.1',  msg:`Fuzzy matcher resolved ${lines.filter(l=>l.matched).length}/${lines.length} lines · ${lines.filter(l=>l.anomaly).length} flagged for review` },
    { stage:'reconciled',done:['reconciled','paid'].includes(status),                date:stmt.processedAt ? daysAfter(stmt.source.cadenceDays - 1) : null,  who:'a.cohen',       msg:`Reconciled · all checks passed · variance within tolerance` },
    { stage:'paid',      done:status==='paid',                                       date:stmt.processedAt ? daysAfter(stmt.source.cadenceDays + 6) : null,  who:'p.tibbets',     msg:`Payout cycle cut · ${fmtCur(stmt.grossUsd*0.85)} distributed to recipients` },
  ];
  if (status === 'failed') {
    events.push({ stage:'failed', done:true, date:daysAfter(stmt.source.cadenceDays-3), who:'parser v3.4.1', msg:`Parse failed · column 14 schema mismatch · awaiting human review`, error:true });
  }
  if (status === 'late') {
    events.push({ stage:'late', done:true, date:'NOW', who:'system', msg:`Statement is overdue · expected ${stmt.expectedDate.toISOString().slice(0,10)}`, error:true });
  }

  return (
    <div style={{padding:'24px 28px'}}>
      <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:18}}>EVENT LOG · {events.filter(e=>e.done).length} OF {events.length} STAGES</div>
      <div style={{position:'relative',paddingLeft:24}}>
        <div style={{position:'absolute',left:7,top:8,bottom:8,width:1,background:'var(--rule)'}}/>
        {events.map((e,i) => (
          <div key={i} style={{position:'relative',marginBottom: i < events.length-1 ? 22 : 0,opacity: e.done ? 1 : 0.45}}>
            <span style={{position:'absolute',left:-21,top:5,width:13,height:13,
              background: e.error ? '#a04432' : e.done ? 'var(--ink)' : 'var(--bg)',
              border:`2px solid ${e.error ? '#a04432' : e.done ? 'var(--ink)' : 'var(--rule)'}`}}/>
            <div style={{fontSize:13,fontWeight:500,letterSpacing:'-0.005em',marginBottom:3,color: e.error ? '#a04432' : 'var(--ink)'}}>{e.msg}</div>
            <div className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',letterSpacing:'.04em'}}>
              <span className="upper" style={{letterSpacing:'.1em',fontWeight:600,color: e.done ? 'var(--ink-2)' : 'var(--ink-3)'}}>{e.stage}</span>
              {e.date && <span> · {e.date}</span>}
              {e.who && <span> · {e.who}</span>}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function RawFileTab({ stmt, lines, onDownload, onReparse, downloads, reparses }) {
  // Mock raw line preview — pretend to show the parsed source file
  const fmtName = stmt.source.kind === 'DSP' ? `${stmt.source.id}_${stmt.period.id}.tsv` :
                  stmt.source.kind === 'PRO' ? `${stmt.source.name.toLowerCase().replace(/[^a-z]/g,'_')}_${stmt.period.id}.cwr` :
                  `${stmt.source.id}_${stmt.period.id}.csv`;
  const fmt = stmt.source.kind === 'DSP' ? 'DDEX DSR (TSV)' : stmt.source.kind === 'PRO' ? 'CWR-style ASCII' : 'CSV (custom schema)';
  const sampleLines = lines.slice(0,12);
  return (
    <div style={{padding:'24px 28px'}}>
      <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:14,marginBottom:24}}>
        <KV k="FILE NAME"   v={fmtName}/>
        <KV k="FORMAT"      v={fmt}/>
        <KV k="ENCODING"    v="UTF-8 · LF"/>
        <KV k="SIZE"        v={`${(lines.length*0.18).toFixed(1)} MB · ${lines.length.toLocaleString()} rows`}/>
        <KV k="PARSER"      v="parser v3.4.1"/>
        <KV k="TEMPLATE"    v={`${stmt.source.name.toLowerCase().replace(/[^a-z]/g,'_')}_v2`}/>
      </div>

      <div style={{display:'flex',gap:8,marginBottom:14,flexWrap:'wrap',alignItems:'center'}}>
        <button onClick={()=>{ onDownload && onDownload(); }} className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',padding:'6px 12px',background:'var(--ink)',color:'var(--bg)',border:0,fontWeight:600,cursor:'pointer'}}>↓ DOWNLOAD RAW{downloads ? ` · ${downloads}` : ''}</button>
        <button onClick={()=>{ onReparse && onReparse(); }} className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',padding:'6px 12px',background:'transparent',color:'var(--ink-2)',border:'1px solid var(--rule)',cursor:'pointer'}}>↻ RE-PARSE{reparses ? ` · ${reparses}` : ''}</button>
        <button onClick={()=>toast('Parser template editor opened')} className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',padding:'6px 12px',background:'transparent',color:'var(--ink-2)',border:'1px solid var(--rule)',cursor:'pointer'}}>EDIT TEMPLATE</button>
        {(downloads > 0 || reparses > 0) && (
          <span className="ff-mono" style={{fontSize:10,color:'var(--ink-3)',marginLeft:'auto',letterSpacing:'.04em'}}>
            this session: {downloads} download{downloads===1?'':'s'} · {reparses} re-parse{reparses===1?'':'s'}
          </span>
        )}
      </div>

      <div className="ff-mono upper" style={{fontSize:9,color:'var(--ink-3)',letterSpacing:'.12em',marginBottom:8}}>FILE PREVIEW · FIRST 12 LINES</div>
      <pre className="ff-mono" style={{
        margin:0,padding:'14px 16px',background:'var(--ink)',color:'var(--bg)',fontSize:10,lineHeight:1.6,
        overflowX:'auto',border:'1px solid var(--ink)',whiteSpace:'pre',
      }}>
{`# ${fmtName}
# format: ${fmt}
# generated: ${stmt.processedAt ? stmt.processedAt.toISOString() : '—'}
# rows: ${lines.length}

`}{sampleLines.map((l,i) => stmt.source.kind === 'DSP'
  ? `${String(i+1).padStart(5,'0')}\t${(l.isrc||l.iswc||'XXXX').padEnd(15)}\t${l.territory}\t${String(l.units).padStart(8)}\t${l.rate.toFixed(5)}\t${l.gross.toFixed(2)}\t${stmt.currency}\t${l.type.short}`
  : `"${(l.iswc||l.isrc||'').padEnd(18)}","${l.title.slice(0,32).padEnd(32)}","${l.territory}",${l.units},${l.rate.toFixed(4)},${l.gross.toFixed(2)},${stmt.currency},${l.type.code}`
).join('\n')}{`\n...\n# (${(lines.length-12).toLocaleString()} more rows)`}
      </pre>
    </div>
  );
}

// ─────────────────────────── persistent per-statement decisions
// Stored on window so reopening the drawer keeps user's progress within the session.
// Shape: { [stmt.id]: { status, lineDecisions: { [lineId]: 'matched'|'accepted'|'hold' }, downloads, reparses } }
window.__STMT_DECISIONS = window.__STMT_DECISIONS || {};
function useStatementDecisions(stmtId, initialStatus) {
  const [, force] = useState(0);
  const cur = window.__STMT_DECISIONS[stmtId] || (window.__STMT_DECISIONS[stmtId] = {
    status: initialStatus, lineDecisions: {}, downloads: 0, reparses: 0,
  });
  const update = (mut) => {
    mut(cur);
    force(x=>x+1);
    // Tell other surfaces (royalties inbox, dashboard) to re-render
    window.dispatchEvent(new CustomEvent('astro-stmt-decisions-changed', { detail: { stmtId } }));
  };
  return [cur, update];
}

// ─────────────────────────── main drawer
function StatementDrawer({ stmt, onClose }) {
  const [tab, setTab] = useState('overview');
  // For real statements, pull decoded lines from __STMT_INDEX. For synthetic ones,
  // generate from the stmt seed.
  const realStmt = stmt.isReal && window.__STMT_INDEX ? window.__STMT_INDEX.byId[stmt.id] : null;
  const lineCount = realStmt
    ? realStmt.lines.length
    : Math.min(stmt.lines || 0, 800);
  const lines = useMemo(
    () => realStmt ? buildRealLines(realStmt) : buildLines(stmt, lineCount),
    [stmt.id, lineCount, !!realStmt]
  );

  // Persistent per-statement decision store
  const [dec, updateDec] = useStatementDecisions(stmt.id, stmt.status);
  const liveStatus = dec.status || stmt.status;
  const decideLine = (lineId, decision) => updateDec(d => { d.lineDecisions[lineId] = decision; });
  const setStatus = (status) => updateDec(d => { d.status = status; });
  const decisionCounts = {
    matched: Object.values(dec.lineDecisions).filter(v => v === 'matched').length,
    accepted: Object.values(dec.lineDecisions).filter(v => v === 'accepted').length,
    hold: Object.values(dec.lineDecisions).filter(v => v === 'hold').length,
  };

  const onMarkReconciled = () => {
    setStatus('reconciled');
    toast(`Statement reconciled · ${stmt.source.name} ${stmt.period.label}`,'ok');
  };
  const onDownloadRaw = () => {
    updateDec(d => { d.downloads = (d.downloads||0) + 1; });
    toast(`Raw file download started · ${stmt.source.name}`);
  };
  const onReparse = () => {
    updateDec(d => { d.reparses = (d.reparses||0) + 1; });
    toast(`Re-parse queued · ${stmt.source.name}`);
  };

  return (
    <Drawer onClose={onClose} width={780}>
      {/* Hero */}
      <div style={{position:'relative',padding:'28px 28px 24px',background:'var(--ink)',color:'var(--bg)'}}>
        <CloseBtn onClose={onClose}/>
        <div className="ff-mono upper" style={{fontSize:9,letterSpacing:'.14em',opacity:.55,marginBottom:14}}>
          ROYALTY STATEMENT · {stmt.id.toUpperCase()}
        </div>
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'flex-end',gap:24,marginBottom:18}}>
          <div style={{minWidth:0,flex:1}}>
            <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:8}}>
              <span style={{width:8,height:36,background:stmt.source.color,flexShrink:0}}/>
              <div className="ff-display" style={{fontSize:36,fontWeight:700,letterSpacing:'-0.03em',lineHeight:1}}>{stmt.source.name}</div>
              {realStmt && (
                <span title="Real statement on file" className="ff-mono upper" style={{
                  display:'inline-flex',alignItems:'center',gap:4,padding:'3px 8px',
                  fontSize:9,letterSpacing:'.12em',fontWeight:600,
                  background:'var(--accent)',color:'var(--accent-ink)',
                }}>📎 ON FILE</span>
              )}
            </div>
            <div className="ff-mono" style={{fontSize:12,opacity:.7,letterSpacing:'.04em'}}>
              {stmt.source.kind} · {stmt.period.label} · {stmt.period.start} → {stmt.period.end}
            </div>
            {realStmt && (
              <div className="ff-mono" style={{fontSize:10,opacity:.55,letterSpacing:'.04em',marginTop:6}}>
                {realStmt.legalEntity} · {realStmt.file}
              </div>
            )}
          </div>
          <div style={{textAlign:'right'}}>
            <div className="ff-display num" style={{fontSize:48,fontWeight:700,letterSpacing:'-0.04em',lineHeight:1}}>
              {fmtCur(stmt.grossUsd)}
            </div>
            <div className="ff-mono" style={{fontSize:11,opacity:.7,marginTop:4,letterSpacing:'.04em'}}>
              {fmtCur(stmt.gross, stmt.currency)} · USD equiv
            </div>
          </div>
        </div>
        <div style={{display:'flex',gap:10,alignItems:'center',justifyContent:'space-between',flexWrap:'wrap'}}>
          <div style={{display:'flex',gap:6,alignItems:'center'}}>
            <StatusChip s={liveStatus}/>
            {stmt.anomalies > 0 && (
              <span className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',padding:'4px 10px',background:'rgba(199,149,56,.15)',color:'#e8b34d',border:'1px solid #c79538'}}>{stmt.anomalies} flagged</span>
            )}
            {(decisionCounts.accepted + decisionCounts.matched + decisionCounts.hold) > 0 && (
              <span className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',padding:'4px 10px',background:'rgba(255,255,255,.08)',color:'rgba(255,255,255,.85)',border:'1px solid rgba(255,255,255,.2)'}}>
                {decisionCounts.matched ? `${decisionCounts.matched} matched · ` : ''}
                {decisionCounts.accepted ? `${decisionCounts.accepted} accepted · ` : ''}
                {decisionCounts.hold ? `${decisionCounts.hold} on hold` : ''}
              </span>
            )}
          </div>
          <div style={{display:'flex',gap:6}}>
            {liveStatus !== 'paid' && liveStatus !== 'reconciled' && liveStatus !== 'pending' && liveStatus !== 'late' && (
              <button onClick={onMarkReconciled} className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',padding:'7px 12px',background:'var(--accent)',color:'var(--accent-ink)',border:0,fontWeight:600,cursor:'pointer'}}>
                MARK RECONCILED
              </button>
            )}
            {liveStatus === 'reconciled' && (
              <span className="ff-mono upper" style={{fontSize:10,letterSpacing:'.1em',padding:'7px 12px',background:'#2d6a3f',color:'#fff',fontWeight:600}}>✓ RECONCILED</span>
            )}
            <button onClick={onDownloadRaw} className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',padding:'7px 12px',background:'transparent',color:'var(--bg)',border:'1px solid rgba(255,255,255,.3)',cursor:'pointer'}}>
              ↓ DOWNLOAD RAW{dec.downloads ? ` · ${dec.downloads}` : ''}
            </button>
            <button onClick={onReparse} className="ff-mono upper" style={{fontSize:10,letterSpacing:'.08em',padding:'7px 12px',background:'transparent',color:'var(--bg)',border:'1px solid rgba(255,255,255,.3)',cursor:'pointer'}}>
              ↻ RE-PARSE{dec.reparses ? ` · ${dec.reparses}` : ''}
            </button>
          </div>
        </div>
      </div>

      {/* Tabs */}
      <div style={{borderBottom:'1px solid var(--rule)',display:'flex',gap:0,position:'sticky',top:0,zIndex:1,background:'var(--bg)'}}>
        {[
          {k:'overview',l:'Overview'},
          {k:'lines',l:`Lines · ${lineCount.toLocaleString()}`},
          {k:'anomalies',l:`Anomalies · ${lines.filter(l=>l.anomaly).length}`},
          {k:'reconcile',l:'Reconciliation'},
          {k:'history',l:'History'},
          {k:'raw',l:'Raw file'},
        ].map(t => (
          <button key={t.k} onClick={()=>setTab(t.k)} className="ff-mono upper" style={{
            padding:'14px 16px',fontSize:10,fontWeight:600,letterSpacing:'.08em',
            border:0,background:'transparent',cursor:'pointer',
            color: tab===t.k ? 'var(--ink)' : 'var(--ink-3)',
            borderBottom: tab===t.k ? '2px solid var(--ink)' : '2px solid transparent',
            marginBottom:-1,
          }}>{t.l}</button>
        ))}
      </div>

      {tab === 'overview'  && <OverviewTab     stmt={stmt} lines={lines}/>}
      {tab === 'lines'     && <LinesTab        stmt={stmt} lines={lines}/>}
      {tab === 'anomalies' && <AnomaliesTab    stmt={stmt} lines={lines} dec={dec} decideLine={decideLine}/>}
      {tab === 'reconcile' && <ReconciliationTab stmt={stmt} lines={lines} liveStatus={liveStatus} onMarkReconciled={onMarkReconciled}/>}
      {tab === 'history'   && <HistoryTab      stmt={stmt} lines={lines} liveStatus={liveStatus}/>}
      {tab === 'raw'       && <RawFileTab      stmt={stmt} lines={lines} onDownload={onDownloadRaw} onReparse={onReparse} downloads={dec.downloads||0} reparses={dec.reparses||0}/>}
    </Drawer>
  );
}

// ─────────────────────────── global mount
function GlobalStatementDrawer() {
  const [stmt, setStmt] = useState(null);
  // Cache statements by id once on mount
  const stmts = useMemo(() => {
    if (!window.__STMT_BUILD) return [];
    return window.__STMT_BUILD();
  }, []);

  useEffect(() => {
    const onOpen = (e) => {
      const found = stmts.find(s => s.id === e.detail?.id);
      if (found) setStmt(found);
    };
    const onClose = () => setStmt(null);
    window.addEventListener('astro-open-statement', onOpen);
    window.addEventListener('astro-close-statement', onClose);
    return () => {
      window.removeEventListener('astro-open-statement', onOpen);
      window.removeEventListener('astro-close-statement', onClose);
    };
  }, [stmts]);

  if (!stmt) return null;
  return <StatementDrawer stmt={stmt} onClose={()=>setStmt(null)}/>;
}

window.GlobalStatementDrawer = GlobalStatementDrawer;
})();
