// dsp-import.jsx — DSP File-Drop Import screen
// ─────────────────────────────────────────────────────────────────
// 5-tab pipeline UI for vendor-native analytics exports:
//
//   01 UPLOAD   — drop zone · vendor sniff · recent runs
//   02 ADAPTER  — vendor + adapter selection · field mapping
//   03 VALIDATE — row-level errors / warnings · match diagnostics
//   04 PREVIEW  — sample rows + aggregates (top territories / dates)
//   05 APPLY    — manifest + apply to RS_DSP_IMPORTS
//
// Sits alongside Bulk Catalog Import (catalog assets) and Statement
// Parser (royalty $) — this is for analytics/insight exports.
//
// Exports: window.ScreenDspImport
// ─────────────────────────────────────────────────────────────────
(function () {
  if (typeof window === 'undefined' || !window.React) return;
  const _S = React.useState, _E = React.useEffect, _M = React.useMemo;
  const E = window.DSP_IMPORT_ENGINE;
  if (!E) { console.warn('[dsp-import] DSP_IMPORT_ENGINE missing'); return; }

  function Mono({ children, upper, size, color, style, ...rest }) {
    return <span className={'ff-mono' + (upper?' upper':'')} style={{ fontSize: size||11, color: color||'var(--ink)', letterSpacing: upper?'.08em':0, ...style }} {...rest}>{children}</span>;
  }
  function Cell({ label, value, sub, tone }) {
    return (
      <div style={{ padding:'18px 22px', borderRight:'1px solid var(--rule)' }}>
        <Mono upper size={9} color="var(--ink-3)">{label}</Mono>
        <div className="ff-display" style={{ fontSize:24, fontWeight:600, letterSpacing:'-0.02em', marginTop:4, color: tone || 'var(--ink)' }}>{value}</div>
        <div style={{ fontSize:11, color:'var(--ink-2)', marginTop:3 }}>{sub}</div>
      </div>
    );
  }
  function Pill({ tone, children }) {
    const tones = { ok:{bg:'#0a8754',fg:'#fff'}, warn:{bg:'#d4881f',fg:'#fff'}, err:{bg:'#a32a18',fg:'#fff'}, info:{bg:'#1a4ed8',fg:'#fff'}, mono:{bg:'var(--ink)',fg:'var(--bg)'}, ghost:{bg:'transparent',fg:'var(--ink-2)',bd:'1px solid var(--rule)'} };
    const t = tones[tone] || tones.mono;
    return <span className="ff-mono upper" style={{ fontSize:9, padding:'2px 7px', background:t.bg, color:t.fg, border:t.bd||0, letterSpacing:'.08em' }}>{children}</span>;
  }
  function VendorChip({ vendor, label }) {
    const s = E.VENDOR_STYLE[vendor] || { color:'var(--ink-2)', bg:'var(--rule-soft)' };
    return <span style={{ display:'inline-flex', alignItems:'center', gap:6, fontSize:10.5, padding:'3px 8px', background:s.bg, color:s.color, fontFamily:'IBM Plex Mono, monospace' }}><span style={{ width:6,height:6,background:s.color,borderRadius:'50%' }}/>{label || vendor.toUpperCase()}</span>;
  }
  function fmtBig(n) { if (n == null) return '—'; if (n>=1e6) return (n/1e6).toFixed(1)+'M'; if (n>=1e3) return (n/1e3).toFixed(1)+'K'; return Number(n).toLocaleString(); }
  function fmtDate(t) { return new Date(t).toLocaleString(); }
  function Empty({ msg }) { return <div style={{ padding:48, textAlign:'center', color:'var(--ink-3)', border:'1px dashed var(--rule)' }}>{msg}</div>; }

  // ════════════════════════════════════════════════════════════════
  // UPLOAD
  // ════════════════════════════════════════════════════════════════
  function UploadTab({ onResult }) {
    const [over, setOver] = _S(false);
    const [paste, setPaste] = _S('');
    const [busy, setBusy] = _S(false);

    function ingest(name, text) {
      setBusy(true);
      setTimeout(() => { try { const r = E.runPipeline({ name, text }); onResult(name, text, r); } finally { setBusy(false); } }, 50);
    }
    function onFile(e) { const f = e.target.files?.[0]; if (!f) return; const r = new FileReader(); r.onload = () => ingest(f.name, String(r.result||'')); r.readAsText(f); }
    function onDrop(e) { e.preventDefault(); setOver(false); const f = e.dataTransfer.files?.[0]; if (!f) return; const r = new FileReader(); r.onload = () => ingest(f.name, String(r.result||'')); r.readAsText(f); }
    function onPaste() { if (!paste.trim()) return; ingest('pasted.csv', paste); }

    function onSample(vendor) {
      const samples = {
        spotify: 'Date,Track,ISRC,Streams,Listeners,Country\n2025-10-01,"Echoes In Glass",USPLU2400001,8124,4218,US\n2025-10-02,"Echoes In Glass",USPLU2400001,8841,4612,US\n2025-10-01,"Half Light",USPLU2400002,3120,2018,US\n2025-10-02,"Half Light",USPLU2400002,2912,1944,US',
        apple: 'Date,Song,ISRC,Plays,Shazams,Listeners,Country\n2025-10-01,"Echoes In Glass",USPLU2400001,2418,142,1881,US\n2025-10-02,"Echoes In Glass",USPLU2400001,2614,158,2014,US',
        youtube: 'Date,Video,Video Title,Views,Watch time (hours),Estimated revenue (USD),Geography\n2025-10-01,abc123,"Echoes In Glass — Lyric",18241,612.4,28.41,US\n2025-10-02,abc123,"Echoes In Glass — Lyric",19014,648.1,29.88,US',
        tidal: 'Date,Track,ISRC,Streams,Listeners,Country\n2025-10-01,"Echoes In Glass",USPLU2400001,412,318,US',
      };
      ingest(`sample_${vendor}.csv`, samples[vendor] || samples.spotify);
    }

    const recent = E.seedRunHistory().slice(0, 5);

    return (
      <div>
        <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:18, marginBottom:24 }}>
          <div onDragOver={(e)=>{e.preventDefault();setOver(true);}} onDragLeave={()=>setOver(false)} onDrop={onDrop}
               style={{ border:'2px dashed '+(over?'var(--ink)':'var(--rule)'), padding:'40px 22px', textAlign:'center', background: over?'var(--bg-2)':'var(--paper)', transition:'all .15s' }}>
            <div style={{ fontSize:40, marginBottom:8, opacity:.5 }}>⤓</div>
            <div style={{ fontSize:16, fontWeight:600, marginBottom:8 }}>Drop a DSP analytics export</div>
            <div style={{ fontSize:12, color:'var(--ink-2)', marginBottom:18, maxWidth:420, margin:'0 auto 18px' }}>
              CSV / TSV / XLSX from Spotify-for-Artists, Apple Music for Artists, YouTube Studio, Tidal Stage, Amazon Music for Artists, SoundCloud, Pandora AMP, Deezer, JioSaavn, Anghami, and more. Vendor + schema are auto-detected.
            </div>
            <label style={{ display:'inline-block', padding:'9px 18px', border:'1px solid var(--ink)', cursor:'pointer', fontSize:12, background:'var(--ink)', color:'var(--bg)' }}>
              Browse files
              <input type="file" accept=".csv,.tsv,.txt,.xlsx,.json" onChange={onFile} style={{ display:'none' }}/>
            </label>
            {busy && <div style={{ marginTop:16, fontSize:11, color:'var(--ink-3)' }}>Sniffing & mapping…</div>}
          </div>

          <div style={{ border:'1px solid var(--rule)', padding:18, background:'var(--paper)' }}>
            <Mono upper size={9} color="var(--ink-3)" style={{ marginBottom:8, display:'block' }}>OR PASTE CSV / TSV</Mono>
            <textarea value={paste} onChange={(e)=>setPaste(e.target.value)} placeholder="Paste rows — first row should be headers" style={{ width:'100%', height:140, padding:10, border:'1px solid var(--rule)', background:'var(--bg)', fontFamily:'IBM Plex Mono, monospace', fontSize:11, color:'var(--ink)', boxSizing:'border-box', resize:'vertical' }}/>
            <div style={{ display:'flex', gap:8, marginTop:10, alignItems:'center' }}>
              <button onClick={onPaste} disabled={!paste.trim()} style={{ padding:'8px 16px', border:'1px solid var(--ink)', background: paste.trim()?'var(--ink)':'var(--rule-soft)', color: paste.trim()?'var(--bg)':'var(--ink-3)', fontSize:12, cursor: paste.trim()?'pointer':'not-allowed' }}>Run pipeline</button>
              <span style={{ flex:1 }}/>
              <Mono size={10} color="var(--ink-3)">Try a sample:</Mono>
              {['spotify','apple','youtube','tidal'].map(k => (
                <button key={k} onClick={()=>onSample(k)} style={{ padding:'4px 9px', border:'1px solid var(--rule)', background:'transparent', fontSize:10.5, cursor:'pointer' }}>{k}</button>
              ))}
            </div>
          </div>
        </div>

        {/* Adapter library */}
        <div style={{ marginBottom:24 }}>
          <Mono upper size={9} color="var(--ink-3)" style={{ marginBottom:10, display:'block' }}>SUPPORTED ADAPTERS · {E.ADAPTERS.length}</Mono>
          <div style={{ display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:12 }}>
            {E.ADAPTERS.map(a => (
              <div key={a.id} style={{ padding:14, border:'1px solid var(--rule)', background:'var(--paper)' }}>
                <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:6 }}>
                  <VendorChip vendor={a.vendor} label={a.vendorLabel}/>
                  <Mono upper size={9} color="var(--ink-3)">{a.dataset}</Mono>
                </div>
                <div style={{ fontSize:13, fontWeight:500, marginBottom:4 }}>{a.label}</div>
                <Mono size={10} color="var(--ink-3)">period · {a.period}</Mono>
                <div style={{ fontSize:10.5, color:'var(--ink-2)', marginTop:8, lineHeight:1.4 }}>{a.docHint}</div>
              </div>
            ))}
          </div>
        </div>

        {/* Recent runs */}
        <div>
          <Mono upper size={9} color="var(--ink-3)" style={{ marginBottom:10, display:'block' }}>RECENT IMPORTS</Mono>
          <div style={{ border:'1px solid var(--rule)' }}>
            {recent.map(r => {
              const adapter = E.ADAPTERS.find(a => a.id === r.adapter);
              const tone = r.status === 'ok' ? '#0a8754' : r.status === 'partial' ? '#d4881f' : '#a32a18';
              return (
                <div key={r.id} style={{ display:'grid', gridTemplateColumns:'10px 1fr 180px 100px 100px 80px', gap:14, padding:'12px 16px', borderBottom:'1px solid var(--rule-soft)', alignItems:'center' }}>
                  <span style={{ width:8, height:8, borderRadius:'50%', background:tone, display:'inline-block' }}/>
                  <div>
                    <div style={{ fontSize:12.5, fontWeight:500 }}>{r.fileName}</div>
                    <Mono size={10} color="var(--ink-3)" style={{ marginTop:2 }}>{fmtDate(r.startedAt)} · {r.user}</Mono>
                  </div>
                  {adapter ? <VendorChip vendor={adapter.vendor} label={adapter.vendorLabel}/> : <Mono size={11} color="var(--ink-3)">—</Mono>}
                  <Mono size={11} style={{ textAlign:'right' }}>{r.rowCount.toLocaleString()} rows</Mono>
                  <Mono size={11} style={{ textAlign:'right', color: r.matchRate < 0.8 ? '#d4881f' : 'var(--ink-3)' }}>{Math.round(r.matchRate*100)}% matched</Mono>
                  <Mono upper size={9} style={{ textAlign:'right', color:tone, fontWeight:600 }}>{r.status}</Mono>
                </div>
              );
            })}
          </div>
        </div>
      </div>
    );
  }

  // ════════════════════════════════════════════════════════════════
  // ADAPTER & MAPPING TAB
  // ════════════════════════════════════════════════════════════════
  function AdapterTab({ session, onUpdate }) {
    if (!session.result) return <Empty msg="Upload a file first to configure the adapter."/>;
    const r = session.result;
    const adapter = session.adapter || r.adapter;
    const mapping = session.mapping || r.mapping;
    const headers = r.headers;

    function setAdapter(id) {
      const a = E.ADAPTERS.find(x => x.id === id) || adapter;
      const m = E.autoMap(a, headers);
      onUpdate({ adapter: a, mapping: m });
    }
    function setMapping(field, header) {
      const next = { ...mapping };
      if (header === '') delete next[field]; else next[field] = header;
      onUpdate({ mapping: next });
    }

    const usedHeaders = new Set(Object.values(mapping));
    const unusedHeaders = headers.filter(h => !usedHeaders.has(h));
    const score = r.detection?.scored?.find(x => x.adapter.id === adapter.id)?.score || 0;

    return (
      <div>
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:16, padding:'14px 18px', background:'var(--bg-2)', border:'1px solid var(--rule)' }}>
          <div>
            <Mono upper size={9} color="var(--ink-3)">DETECTED · {r.format.toUpperCase()} · CONFIDENCE {Math.round(score*100)}%</Mono>
            <div style={{ fontSize:14, fontWeight:600, marginTop:2, display:'flex', alignItems:'center', gap:10 }}>
              <VendorChip vendor={adapter.vendor} label={adapter.vendorLabel}/>
              <span>{adapter.label}</span>
              <span style={{ color:'var(--ink-3)' }}>· {r.rowCount.toLocaleString()} rows · {headers.length} cols</span>
            </div>
          </div>
          <div style={{ display:'flex', alignItems:'center', gap:10 }}>
            <Mono upper size={9} color="var(--ink-3)">CHANGE ADAPTER</Mono>
            <select value={adapter.id} onChange={(e)=>setAdapter(e.target.value)} style={{ border:'1px solid var(--rule)', background:'var(--bg)', fontSize:12, padding:'0 24px 0 10px', minWidth:280 }}>
              {E.ADAPTERS.map(a => <option key={a.id} value={a.id}>{a.vendorLabel} — {a.label}</option>)}
            </select>
          </div>
        </div>

        {/* Schema-mapping table */}
        <div style={{ border:'1px solid var(--rule)' }}>
          <div style={{ display:'grid', gridTemplateColumns:'24px 200px 1fr 220px', gap:14, padding:'10px 16px', borderBottom:'1px solid var(--rule)', background:'var(--bg-2)' }}>
            <Mono upper size={9} color="var(--ink-3)"></Mono>
            <Mono upper size={9} color="var(--ink-3)">CANONICAL FIELD</Mono>
            <Mono upper size={9} color="var(--ink-3)">SOURCE COLUMN</Mono>
            <Mono upper size={9} color="var(--ink-3)">EXAMPLE</Mono>
          </div>
          {Object.entries(adapter.schema).map(([field, syns], i, arr) => {
            const src = mapping[field];
            const sample = src && r.sample[0] ? r.sample[0].row[src] : null;
            return (
              <div key={field} style={{ display:'grid', gridTemplateColumns:'24px 200px 1fr 220px', gap:14, padding:'10px 16px', borderBottom: i < arr.length-1 ? '1px solid var(--rule-soft)' : 0, alignItems:'center' }}>
                <span style={{ width:8, height:8, borderRadius:'50%', background: src ? '#0a8754' : 'var(--rule)', display:'inline-block' }}/>
                <div>
                  <div style={{ fontSize:12.5, fontWeight:500 }}>{field}</div>
                  <Mono size={10} color="var(--ink-3)">syn: {syns.slice(0,3).join(', ')}{syns.length > 3 ? '…' : ''}</Mono>
                </div>
                <select value={src||''} onChange={(e)=>setMapping(field, e.target.value)} style={{ border:'1px solid var(--rule)', background:'var(--bg)', fontSize:12, padding:'0 24px 0 10px', width:'100%' }}>
                  <option value="">— unmapped —</option>
                  {headers.map(h => <option key={h} value={h}>{h}</option>)}
                </select>
                <Mono size={10.5} color="var(--ink-3)" style={{ overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{sample != null ? String(sample).slice(0,40) : '—'}</Mono>
              </div>
            );
          })}
        </div>

        {unusedHeaders.length > 0 && (
          <div style={{ marginTop:14, padding:'12px 16px', border:'1px solid var(--rule)', background:'var(--paper)' }}>
            <Mono upper size={9} color="var(--ink-3)" style={{ marginBottom:6, display:'block' }}>UNMAPPED COLUMNS · {unusedHeaders.length} · IGNORED ON IMPORT</Mono>
            <div style={{ display:'flex', flexWrap:'wrap', gap:6 }}>
              {unusedHeaders.map(h => <span key={h} className="ff-mono" style={{ fontSize:10.5, padding:'3px 8px', border:'1px solid var(--rule)', background:'var(--bg-2)' }}>{h}</span>)}
            </div>
          </div>
        )}

        <div style={{ marginTop:16, display:'flex', justifyContent:'space-between', alignItems:'center' }}>
          <Mono size={11} color="var(--ink-3)">{Object.keys(mapping).length} of {Object.keys(adapter.schema).length} canonical fields mapped</Mono>
          <button onClick={() => onUpdate({ tab:'validate', revalidate:true })} style={{ padding:'9px 20px', border:'1px solid var(--ink)', background:'var(--ink)', color:'var(--bg)', fontSize:12, cursor:'pointer' }}>Validate →</button>
        </div>
      </div>
    );
  }

  // ════════════════════════════════════════════════════════════════
  // VALIDATE TAB
  // ════════════════════════════════════════════════════════════════
  function ValidateTab({ session, onUpdate }) {
    if (!session.result) return <Empty msg="Upload a file first."/>;
    const result = session.revalidatedResult || session.result;

    _E(() => {
      if (session.revalidate) {
        const r = E.runPipeline({ name: session.fileName, text: session.text, adapterOverride: session.adapter, mappingOverride: session.mapping });
        onUpdate({ revalidatedResult: r, revalidate: false });
      }
    }, [session.revalidate]);

    const [filter, setFilter] = _S('all');
    const sample = result.sample || [];
    const errs = sample.filter(s => s.errors.length);
    const warns = sample.filter(s => s.warnings.length);
    const filtered = filter === 'err' ? errs : filter === 'warn' ? warns : (filter === 'unmatched' ? sample.filter(s => !s.matchType) : sample);

    return (
      <div>
        <div style={{ display:'grid', gridTemplateColumns:'repeat(5, 1fr)', border:'1px solid var(--rule)', marginBottom:18 }}>
          <Cell label="ROWS" value={result.rowCount.toLocaleString()} sub={`${result.format} · ${result.adapter.vendorLabel}`}/>
          <Cell label="MATCHED · ISRC" value={result.stats.matchedISRC.toLocaleString()} sub={`${Math.round(result.stats.matchRate*100)}% overall match`} tone="#0a8754"/>
          <Cell label="MATCHED · TITLE" value={result.stats.matchedTitle.toLocaleString()} sub="fuzzy fallback"/>
          <Cell label="UNMATCHED" value={result.stats.unmatched.toLocaleString()} sub="no RS recording" tone={result.stats.unmatched > 0 ? '#d4881f' : 'var(--ink)'}/>
          <Cell label="ERRORS" value={result.stats.errors.toLocaleString()} sub={`${result.stats.warnings} warnings`} tone={result.stats.errors > 0 ? '#a32a18' : 'var(--ink)'}/>
        </div>

        <div style={{ display:'flex', gap:8, marginBottom:14, alignItems:'center' }}>
          <Mono upper size={9} color="var(--ink-3)">FILTER</Mono>
          {[['all','All rows'],['err','Errors only'],['warn','Warnings only'],['unmatched','Unmatched']].map(([k,l]) => (
            <button key={k} onClick={()=>setFilter(k)} style={{ padding:'5px 11px', border:'1px solid '+(filter===k?'var(--ink)':'var(--rule)'), background: filter===k?'var(--ink)':'transparent', color: filter===k?'var(--bg)':'var(--ink)', fontSize:11, cursor:'pointer' }}>{l}</button>
          ))}
          <span style={{ flex:1 }}/>
          <Mono size={11} color="var(--ink-3)">{filtered.length.toLocaleString()} shown</Mono>
        </div>

        <div style={{ border:'1px solid var(--rule)', maxHeight:520, overflow:'auto' }}>
          <div style={{ display:'grid', gridTemplateColumns:'48px 1fr 200px 100px 1fr', gap:14, padding:'10px 16px', borderBottom:'1px solid var(--rule)', background:'var(--bg-2)', position:'sticky', top:0, zIndex:1 }}>
            <Mono upper size={9} color="var(--ink-3)">ROW</Mono>
            <Mono upper size={9} color="var(--ink-3)">RECORDING / TITLE</Mono>
            <Mono upper size={9} color="var(--ink-3)">MATCH</Mono>
            <Mono upper size={9} color="var(--ink-3)" style={{ textAlign:'right' }}>METRIC</Mono>
            <Mono upper size={9} color="var(--ink-3)">DIAGNOSTIC</Mono>
          </div>
          {filtered.length === 0 && <div style={{ padding:36, textAlign:'center', color:'var(--ink-3)', fontSize:12 }}>No rows match this filter.</div>}
          {filtered.map(s => (
            <div key={s.rowIndex} style={{ display:'grid', gridTemplateColumns:'48px 1fr 200px 100px 1fr', gap:14, padding:'8px 16px', borderBottom:'1px solid var(--rule-soft)', alignItems:'center' }}>
              <Mono size={10.5} color="var(--ink-3)">{s.rowIndex+1}</Mono>
              <div style={{ overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
                <span style={{ fontSize:12, fontWeight: s.match ? 500 : 400, color: s.match ? 'var(--ink)' : 'var(--ink-3)' }}>{s.match ? (s.match['Track Name'] || s.match.title) : (Object.values(s.row)[0] || '—')}</span>
              </div>
              <div>
                {s.matchType === 'isrc' && <Pill tone="ok">ISRC</Pill>}
                {s.matchType === 'title' && <Pill tone="info">TITLE</Pill>}
                {!s.matchType && <Pill tone="ghost">UNMATCHED</Pill>}
              </div>
              <Mono size={11} style={{ textAlign:'right' }}>{s.streams ? fmtBig(s.streams) : '—'}</Mono>
              <div style={{ display:'flex', gap:6, flexWrap:'wrap' }}>
                {s.errors.map((e,i) => <Pill key={'e'+i} tone="err">{e.field}·{e.kind}</Pill>)}
                {s.warnings.map((w,i) => <Pill key={'w'+i} tone="warn">{w.field}·{w.kind}</Pill>)}
                {s.errors.length === 0 && s.warnings.length === 0 && <Mono size={10.5} color="var(--ink-3)">clean</Mono>}
              </div>
            </div>
          ))}
        </div>

        <div style={{ marginTop:16, display:'flex', justifyContent:'space-between', alignItems:'center' }}>
          <Mono size={11} color="var(--ink-3)">Showing first {sample.length.toLocaleString()} of {result.rowCount.toLocaleString()} rows · validation runs over the full set on apply</Mono>
          <button onClick={() => onUpdate({ tab:'preview' })} style={{ padding:'9px 20px', border:'1px solid var(--ink)', background:'var(--ink)', color:'var(--bg)', fontSize:12, cursor:'pointer' }}>Preview →</button>
        </div>
      </div>
    );
  }

  // ════════════════════════════════════════════════════════════════
  // PREVIEW TAB — aggregates
  // ════════════════════════════════════════════════════════════════
  function PreviewTab({ session, onUpdate }) {
    if (!session.result) return <Empty msg="Upload a file first."/>;
    const result = session.revalidatedResult || session.result;
    const stats = result.stats;

    return (
      <div>
        <div style={{ display:'grid', gridTemplateColumns:'repeat(4, 1fr)', border:'1px solid var(--rule)', marginBottom:18 }}>
          <Cell label="TOTAL STREAMS" value={fmtBig(stats.totalStreams)} sub={result.adapter.dataset}/>
          <Cell label="LISTENERS" value={fmtBig(stats.totalListeners)} sub={stats.uniqueRecordings + ' recordings'}/>
          <Cell label="TERRITORIES" value={stats.uniqueTerritories} sub="distinct"/>
          <div style={{ padding:'18px 22px' }}>
            <Mono upper size={9} color="var(--ink-3)">DATE RANGE</Mono>
            <div className="ff-display" style={{ fontSize:18, fontWeight:600, letterSpacing:'-0.02em', marginTop:4 }}>{stats.dateRange ? `${stats.dateRange.from} → ${stats.dateRange.to}` : '—'}</div>
            <div style={{ fontSize:11, color:'var(--ink-2)', marginTop:3 }}>{stats.dateRange?.days || 0} days</div>
          </div>
        </div>

        <div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:18 }}>
          {/* Top territories */}
          <div style={{ border:'1px solid var(--rule)' }}>
            <div style={{ padding:'12px 16px', borderBottom:'1px solid var(--rule)', background:'var(--bg-2)' }}>
              <Mono upper size={9} color="var(--ink-3)">TOP TERRITORIES · BY {result.adapter.dataset.toUpperCase()}</Mono>
            </div>
            {result.topTerritories.length === 0 && <div style={{ padding:24, textAlign:'center', color:'var(--ink-3)', fontSize:12 }}>No territory data on this export.</div>}
            {result.topTerritories.map(([t, v], i) => {
              const max = result.topTerritories[0][1];
              const pct = max > 0 ? v / max : 0;
              return (
                <div key={t} style={{ display:'grid', gridTemplateColumns:'180px 1fr 80px', gap:10, padding:'8px 16px', borderBottom: i < result.topTerritories.length-1 ? '1px solid var(--rule-soft)' : 0, alignItems:'center' }}>
                  <span style={{ fontSize:12 }}>{t}</span>
                  <div style={{ height:6, background:'var(--rule-soft)', position:'relative' }}>
                    <div style={{ position:'absolute', inset:0, width: (pct*100)+'%', background:'var(--ink)' }}/>
                  </div>
                  <Mono size={11} style={{ textAlign:'right' }}>{fmtBig(v)}</Mono>
                </div>
              );
            })}
          </div>

          {/* Time series */}
          <div style={{ border:'1px solid var(--rule)' }}>
            <div style={{ padding:'12px 16px', borderBottom:'1px solid var(--rule)', background:'var(--bg-2)' }}>
              <Mono upper size={9} color="var(--ink-3)">DAILY TIME SERIES</Mono>
            </div>
            {result.timeSeries.length === 0 && <div style={{ padding:24, textAlign:'center', color:'var(--ink-3)', fontSize:12 }}>No date column detected.</div>}
            {result.timeSeries.length > 0 && (() => {
              const max = Math.max(...result.timeSeries.map(([_,v])=>v));
              return (
                <div style={{ padding:'18px 16px', display:'flex', alignItems:'flex-end', gap:1, height:200 }}>
                  {result.timeSeries.slice(-90).map(([d, v], i) => (
                    <div key={d} title={`${d}: ${fmtBig(v)}`} style={{ flex:1, height: max > 0 ? `${(v/max)*100}%` : '1px', background:'var(--ink)', minHeight:1 }}/>
                  ))}
                </div>
              );
            })()}
            {result.timeSeries.length > 0 && (
              <div style={{ display:'flex', justifyContent:'space-between', padding:'6px 16px 12px' }}>
                <Mono size={10} color="var(--ink-3)">{result.timeSeries[Math.max(0, result.timeSeries.length - 90)][0]}</Mono>
                <Mono size={10} color="var(--ink-3)">{result.timeSeries[result.timeSeries.length-1][0]}</Mono>
              </div>
            )}
          </div>
        </div>

        <div style={{ marginTop:16, display:'flex', justifyContent:'space-between', alignItems:'center' }}>
          <Mono size={11} color="var(--ink-3)">Aggregates computed live from the parsed file. Apply pushes to RS_DSP_IMPORTS.</Mono>
          <button onClick={() => onUpdate({ tab:'apply' })} style={{ padding:'9px 20px', border:'1px solid var(--ink)', background:'var(--ink)', color:'var(--bg)', fontSize:12, cursor:'pointer' }}>Apply →</button>
        </div>
      </div>
    );
  }

  // ════════════════════════════════════════════════════════════════
  // APPLY TAB
  // ════════════════════════════════════════════════════════════════
  function ApplyTab({ session, onUpdate }) {
    if (!session.result) return <Empty msg="Upload a file first."/>;
    const result = session.revalidatedResult || session.result;
    const [committed, setCommitted] = _S(false);

    function onApply() {
      const out = E.apply(result);
      if (out.ok) setCommitted(out);
    }

    return (
      <div>
        <div style={{ display:'grid', gridTemplateColumns:'repeat(4, 1fr)', border:'1px solid var(--rule)', marginBottom:18 }}>
          <Cell label="ADAPTER" value={result.adapter.vendorLabel} sub={result.adapter.label}/>
          <Cell label="ROWS" value={result.rowCount.toLocaleString()} sub={`${Math.round(result.stats.matchRate*100)}% matched`}/>
          <Cell label="MATCHED RECORDINGS" value={result.stats.uniqueRecordings} sub="distinct on RS"/>
          <Cell label="ERRORS" value={result.stats.errors} sub="will be skipped" tone={result.stats.errors > 0 ? '#a32a18' : 'var(--ink)'}/>
        </div>

        <div style={{ border:'1px solid var(--rule)', padding:18, background:'var(--paper)', marginBottom:18 }}>
          <Mono upper size={9} color="var(--ink-3)" style={{ marginBottom:10, display:'block' }}>MANIFEST</Mono>
          <div style={{ display:'grid', gridTemplateColumns:'160px 1fr', gap:'8px 18px', fontSize:12 }}>
            <span style={{ color:'var(--ink-3)' }}>File</span><span className="ff-mono">{session.fileName}</span>
            <span style={{ color:'var(--ink-3)' }}>Vendor</span><span><VendorChip vendor={result.adapter.vendor} label={result.adapter.vendorLabel}/></span>
            <span style={{ color:'var(--ink-3)' }}>Adapter</span><span className="ff-mono">{result.adapter.id}</span>
            <span style={{ color:'var(--ink-3)' }}>Dataset</span><span>{result.adapter.dataset}</span>
            <span style={{ color:'var(--ink-3)' }}>Period</span><span>{result.adapter.period}</span>
            <span style={{ color:'var(--ink-3)' }}>Mapped fields</span><span>{Object.keys(result.mapping).length} of {Object.keys(result.adapter.schema).length}</span>
            <span style={{ color:'var(--ink-3)' }}>Date range</span><span>{result.stats.dateRange ? `${result.stats.dateRange.from} → ${result.stats.dateRange.to}` : '—'}</span>
            <span style={{ color:'var(--ink-3)' }}>Total streams</span><span>{fmtBig(result.stats.totalStreams)}</span>
            <span style={{ color:'var(--ink-3)' }}>Target</span><span className="ff-mono">window.RS_DSP_IMPORTS</span>
          </div>
        </div>

        {!committed ? (
          <div style={{ display:'flex', justifyContent:'flex-end', alignItems:'center', gap:12 }}>
            <Mono size={11} color="var(--ink-3)">Apply pushes the parsed dataset onto the in-session DSP store. Audience &amp; Recording analytics will pick it up via the cross-filter store.</Mono>
            <button onClick={onApply} style={{ padding:'10px 24px', border:'1px solid var(--ink)', background:'var(--ink)', color:'var(--bg)', fontSize:13, fontWeight:600, cursor:'pointer' }}>Apply import</button>
          </div>
        ) : (
          <div style={{ padding:'18px 22px', border:'1px solid #0a8754', background:'#e8f5ed', display:'flex', alignItems:'center', justifyContent:'space-between' }}>
            <div>
              <Mono upper size={9} color="#0a8754" style={{ fontWeight:600 }}>APPLIED</Mono>
              <div style={{ fontSize:14, fontWeight:600, marginTop:4 }}>{committed.applied.toLocaleString()} rows pushed onto window.RS_DSP_IMPORTS</div>
              <div style={{ fontSize:11, color:'var(--ink-2)', marginTop:3 }}>Visible on Audience, Recording → Platforms, and Analytics for this session.</div>
            </div>
          </div>
        )}
      </div>
    );
  }

  // ════════════════════════════════════════════════════════════════
  // SCREEN
  // ════════════════════════════════════════════════════════════════
  function ScreenDspImport({ go, payload }) {
    const PageHeader = window.PageHeader;
    const [tab, setTab] = _S(payload?.tab || 'upload');
    const [session, setSession] = _S({ fileName:null, text:null, result:null, adapter:null, mapping:null, revalidatedResult:null, revalidate:false });

    const onUpdate = (patch) => {
      setSession(s => ({ ...s, ...patch }));
      if (patch.tab) setTab(patch.tab);
    };

    const onResult = (fileName, text, result) => {
      setSession({ fileName, text, result, adapter: result.adapter, mapping: result.mapping, revalidatedResult:null, revalidate:false });
      if (result.ok) setTab('adapter');
    };

    const TABS = [
      { k:'upload',   l:'01 · Upload' },
      { k:'adapter',  l:'02 · Adapter & schema' },
      { k:'validate', l:'03 · Validate' },
      { k:'preview',  l:'04 · Preview' },
      { k:'apply',    l:'05 · Apply' },
    ];

    const eyebrow = ['INTEGRATIONS', 'DSP IMPORT', `${E.ADAPTERS.length} ADAPTERS`];

    return (
      <div data-screen-label="DSP Import">
        {PageHeader && (
          <PageHeader eyebrow={eyebrow} title="dsp import." highlight="dsp import."
            sub="Drop the CSV / TSV / XLSX exports your team pulls from Spotify-for-Artists, Apple Music for Artists, YouTube Studio, Tidal Stage and friends. Vendor and schema are auto-detected, rows are matched against catalog by ISRC, and aggregates flow into Audience and Analytics."
            actions={<button onClick={() => go && go('integrations')} style={{ padding:'8px 14px', border:'1px solid var(--rule)', background:'transparent', fontSize:11, cursor:'pointer' }}>Live API console →</button>}/>
        )}

        <div style={{ borderBottom:'1px solid var(--rule)', display:'flex', gap:0, marginBottom:24 }}>
          {TABS.map(t => {
            const enabled = t.k === 'upload' || !!session.result;
            return (
              <button key={t.k} onClick={() => enabled && setTab(t.k)} style={{
                padding:'14px 22px', background:'transparent', border:0,
                borderBottom:'2px solid '+(tab===t.k?'var(--ink)':'transparent'),
                color: enabled ? (tab===t.k?'var(--ink)':'var(--ink-2)') : 'var(--ink-4)',
                fontSize:12, fontWeight: tab===t.k?600:400, cursor: enabled?'pointer':'not-allowed', letterSpacing:'.02em',
              }}>{t.l}</button>
            );
          })}
        </div>

        {tab === 'upload'   && <UploadTab onResult={onResult}/>}
        {tab === 'adapter'  && <AdapterTab session={session} onUpdate={onUpdate}/>}
        {tab === 'validate' && <ValidateTab session={session} onUpdate={onUpdate}/>}
        {tab === 'preview'  && <PreviewTab session={session} onUpdate={onUpdate}/>}
        {tab === 'apply'    && <ApplyTab session={session} onUpdate={onUpdate}/>}
      </div>
    );
  }

  window.ScreenDspImport = ScreenDspImport;
})();
