// ============================================================================
// CWR GENERATOR SCREEN  ·  picker → preview → inspector
// ----------------------------------------------------------------------------
// Loaded as window.CwrGenTab and rendered as a tab inside ScreenCwrAcks.
// Three panes:
//   1. WORK PICKER — multi-select from window.WORKS / window.songsAll, or
//      pre-selected when arriving from a Registrations row / Agreement detail.
//   2. SETTINGS    — version (2.1, 2.1r7, 2.2, 3.0, 3.1), transaction type
//      (NWR/REV/ISW), recipient society, sender details.
//   3. PREVIEW     — generated CWR transmission, line-by-line, with a side
//      inspector that decodes the clicked line into named fields.
//
// Optional: split-screen mode that generates the same works under TWO
// versions side-by-side so you can see how the format evolves.
//
// Depends on: window.CwrBuild, window.CwrDecode, window.CwrSynth
// EXPORT: window.CwrGenTab
// ============================================================================

(function () {
  'use strict';
  const { useState, useMemo, useEffect, useRef } = React;

  // ─── Catalog source ───────────────────────────────────────────────────────
  function getCatalogWorks() {
    // Pull from whatever the catalog currently exposes — ASTRO uses several
    // shapes depending on screen. We accept any of these and merge.
    const candidates = [
      window.WORKS, window.songsAll, window.SONGS, window.ALL_WORKS,
    ].filter(Array.isArray);
    const seen = new Set();
    const out = [];
    for (const arr of candidates) {
      for (const w of arr) {
        if (!w || !w.id || seen.has(w.id)) continue;
        seen.add(w.id);
        out.push(w);
      }
    }
    return out;
  }

  const VERSIONS = [
    { v: '2.1',   label: '2.1',     hint: 'most societies still parse this' },
    { v: '2.1r7', label: '2.1 r7',  hint: 'CISAC June 2010 revision' },
    { v: '2.2',   label: '2.2',     hint: 'adds REC fields, never widely deployed' },
    { v: '3.0',   label: '3.0',     hint: 'new XRF, expanded MWN' },
    { v: '3.1',   label: '3.1',     hint: 'current — XSD-validated' },
  ];
  const TXN_TYPES = [
    { v: 'NWR', label: 'NWR — New work registration' },
    { v: 'REV', label: 'REV — Revised registration' },
    { v: 'ISW', label: 'ISW — Notification of ISWC' },
    { v: 'EXC', label: 'EXC — Existing work in conflict' },
  ];
  const RECIPIENTS = [
    { code: 'ASCAP', name: 'ASCAP' }, { code: 'BMI', name: 'BMI' },
    { code: 'SESAC', name: 'SESAC' }, { code: 'PRS', name: 'PRS for Music' },
    { code: 'GEMA', name: 'GEMA' }, { code: 'SACEM', name: 'SACEM' },
    { code: 'JASRAC', name: 'JASRAC' }, { code: 'SOCAN', name: 'SOCAN' },
    { code: 'APRA', name: 'APRA AMCOS' }, { code: 'MLC', name: 'The MLC' },
    { code: 'HFA', name: 'HFA' },
  ];

  // ═══════════════════════════════════════════════════════════════════════════
  function CwrGenTab({ go, payload }) {
    const all = useMemo(getCatalogWorks, []);
    // Pre-selection from caller (e.g. Registrations row → Generate CWR)
    const initialIds = (payload && payload.workIds) || [];
    const [selected, setSelected] = useState(() => new Set(initialIds.length ? initialIds : (all[0] ? [all[0].id] : [])));
    const [search, setSearch] = useState('');
    const [version, setVersion] = useState(payload?.version || '2.1');
    const [compareVer, setCompareVer] = useState(null);
    const [txnType, setTxnType] = useState('NWR');
    const [recipient, setRecipient] = useState(payload?.recipient || 'ASCAP');
    const [senderIpi, setSenderIpi] = useState(199900001);
    const [senderName, setSenderName] = useState('PLURALIS MUSIC');
    const [activeLine, setActiveLine] = useState(0);
    const [distOpen, setDistOpen] = useState(false);

    const filtered = useMemo(() => {
      const q = search.trim().toLowerCase();
      if (!q) return all;
      return all.filter((w) => `${w.title||''} ${w.iswc||''} ${(w.writers||[]).join(' ')}`.toLowerCase().includes(q));
    }, [all, search]);

    // Generate the transmission
    const transmission = useMemo(() => {
      const works = [...selected].map((id) => all.find((w) => w.id === id)).filter(Boolean);
      const hydrated = works.map((w) => window.CwrSynth.hydrateWork(w, { senderIpi, senderName }));
      return window.CwrBuild.buildTransmission({
        version,
        senderType: 'PB',
        senderIpi,
        senderName,
        transactionType: txnType,
        works: hydrated,
        created: new Date(2026, 4, 12, 14, 22, 33),
      });
    }, [selected, all, version, txnType, senderIpi, senderName]);

    const transmissionCompare = useMemo(() => {
      if (!compareVer) return null;
      const works = [...selected].map((id) => all.find((w) => w.id === id)).filter(Boolean);
      const hydrated = works.map((w) => window.CwrSynth.hydrateWork(w, { senderIpi, senderName }));
      return window.CwrBuild.buildTransmission({
        version: compareVer,
        senderType: 'PB',
        senderIpi,
        senderName,
        transactionType: txnType,
        works: hydrated,
        created: new Date(2026, 4, 12, 14, 22, 33),
      });
    }, [compareVer, selected, all, txnType, senderIpi, senderName]);

    const filename = window.CwrBuild.buildFilename({
      version, senderCode: 'PLU', recipientCode: recipient, sequence: 501,
      created: new Date(2026, 4, 12),
    });

    const decoded = useMemo(() => {
      const line = transmission[activeLine];
      if (!line) return null;
      return window.CwrDecode.decodeLine(line, version);
    }, [transmission, activeLine, version]);

    // ── Validation report (CISAC compliance gauntlet)
    const validation = useMemo(() => {
      if (!window.CwrValidate) return { issues: [], summary: { errors: 0, warnings: 0, total: 0, ok: true } };
      const works = [...selected].map((id) => all.find((w) => w.id === id)).filter(Boolean);
      const hydrated = works.map((w) => window.CwrSynth.hydrateWork(w, { senderIpi, senderName }));
      return window.CwrValidate.validate({ version, lines: transmission, works: hydrated });
    }, [transmission, version, selected, all, senderIpi, senderName]);
    const [validationOpen, setValidationOpen] = useState(false);

    // Toggle a work's selection
    const toggle = (id) => setSelected((s) => {
      const next = new Set(s);
      if (next.has(id)) next.delete(id); else next.add(id);
      return next;
    });

    // Download
    const download = () => {
      const blob = new Blob([transmission.join('\r\n') + '\r\n'], { type: 'text/plain' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url; a.download = filename;
      document.body.appendChild(a); a.click(); a.remove();
      URL.revokeObjectURL(url);
      window.toast && window.toast(`Wrote ${filename} (${transmission.length} records)`);
    };
    const copy = () => {
      navigator.clipboard.writeText(transmission.join('\r\n'));
      window.toast && window.toast('CWR transmission copied to clipboard');
    };

    return (
      <div style={{ borderTop: '1px solid var(--rule)', display: 'grid', gridTemplateColumns: '320px 1fr', minHeight: 'calc(100vh - 200px)' }}>
        {/* ── LEFT: pickers ────────────────────────────────────── */}
        <aside style={{ borderRight: '1px solid var(--rule)', background: 'var(--bg-2)', display: 'flex', flexDirection: 'column' }}>
          {/* Settings */}
          <div style={{ padding: '14px 14px 12px', borderBottom: '1px solid var(--rule)' }}>
            <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 8 }}>CWR VERSION</div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 4, marginBottom: 12 }}>
              {VERSIONS.map((vv) => (
                <button key={vv.v} onClick={() => setVersion(vv.v)} title={vv.hint} className="ff-mono"
                  style={{
                    padding: '7px 0', fontSize: 11, fontWeight: 600,
                    background: version === vv.v ? 'var(--ink)' : 'var(--paper)',
                    color: version === vv.v ? 'var(--paper)' : 'var(--ink)',
                    border: '1px solid var(--rule)', cursor: 'pointer',
                  }}>{vv.label}</button>
              ))}
            </div>

            <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 8 }}>TRANSACTION</div>
            <select value={txnType} onChange={(e) => setTxnType(e.target.value)} className="ff-mono"
              style={{ width: '100%', padding: '7px 8px', fontSize: 11, background: 'var(--paper)', border: '1px solid var(--rule)', marginBottom: 12 }}>
              {TXN_TYPES.map((t) => <option key={t.v} value={t.v}>{t.label}</option>)}
            </select>

            <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 8 }}>RECIPIENT SOCIETY</div>
            <select value={recipient} onChange={(e) => setRecipient(e.target.value)} className="ff-mono"
              style={{ width: '100%', padding: '7px 8px', fontSize: 11, background: 'var(--paper)', border: '1px solid var(--rule)', marginBottom: 12 }}>
              {RECIPIENTS.map((r) => <option key={r.code} value={r.code}>{r.code} — {r.name}</option>)}
            </select>

            <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 8 }}>COMPARE WITH</div>
            <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
              <button onClick={() => setCompareVer(null)} className="ff-mono"
                style={{ padding: '6px 9px', fontSize: 10, background: !compareVer ? 'var(--ink)' : 'var(--paper)',
                  color: !compareVer ? 'var(--paper)' : 'var(--ink)', border: '1px solid var(--rule)', cursor: 'pointer' }}>off</button>
              {VERSIONS.filter((vv) => vv.v !== version).map((vv) => (
                <button key={vv.v} onClick={() => setCompareVer(vv.v)} className="ff-mono"
                  style={{ padding: '6px 9px', fontSize: 10, background: compareVer === vv.v ? 'var(--ink)' : 'var(--paper)',
                    color: compareVer === vv.v ? 'var(--paper)' : 'var(--ink)', border: '1px solid var(--rule)', cursor: 'pointer' }}>{vv.label}</button>
              ))}
            </div>
          </div>

          {/* Work picker */}
          <div style={{ padding: '12px 14px 8px' }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 8 }}>
              <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)' }}>WORKS · {selected.size} of {all.length}</div>
              <button onClick={() => setSelected(new Set(filtered.map((w) => w.id)))} className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.08em', background: 'transparent', border: 0, color: 'var(--ink-3)', cursor: 'pointer' }}>SELECT ALL</button>
            </div>
            <input value={search} onChange={(e) => setSearch(e.target.value)} placeholder="Search title, ISWC, writer…" className="ff-mono"
              style={{ width: '100%', padding: '7px 8px', fontSize: 11, background: 'var(--paper)', border: '1px solid var(--rule)', marginBottom: 8, boxSizing: 'border-box' }} />
          </div>
          <div style={{ flex: 1, overflowY: 'auto', borderTop: '1px solid var(--rule)' }}>
            {filtered.map((w) => (
              <button key={w.id} onClick={() => toggle(w.id)}
                style={{
                  display: 'flex', alignItems: 'flex-start', gap: 8, width: '100%', textAlign: 'left',
                  padding: '9px 14px', cursor: 'pointer',
                  background: selected.has(w.id) ? 'var(--paper)' : 'transparent',
                  border: 0, borderBottom: '1px solid var(--rule)',
                }}>
                <span style={{
                  width: 14, height: 14, flex: '0 0 14px', marginTop: 2,
                  border: '1px solid var(--ink-2)',
                  background: selected.has(w.id) ? 'var(--ink)' : 'transparent',
                  display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                  color: 'var(--paper)', fontSize: 10, fontWeight: 700,
                }}>{selected.has(w.id) ? '✓' : ''}</span>
                <span style={{ flex: 1, minWidth: 0 }}>
                  <span style={{ fontSize: 12, fontWeight: 500, display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{w.title}</span>
                  <span className="ff-mono" style={{ fontSize: 9, color: 'var(--ink-3)', display: 'block' }}>{w.iswc || '—'} · {(w.writers || []).slice(0, 2).join(', ')}</span>
                </span>
              </button>
            ))}
          </div>
        </aside>

        {/* ── RIGHT: preview + inspector ─────────────────────── */}
        <main style={{ display: 'flex', flexDirection: 'column', minWidth: 0 }}>
          {/* file header bar */}
          <div style={{ borderBottom: '1px solid var(--rule)', padding: '10px 16px', display: 'flex', alignItems: 'center', gap: 14, background: 'var(--paper)' }}>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div className="ff-mono" style={{ fontSize: 13, fontWeight: 600 }}>{filename}</div>
              <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 1 }}>
                {transmission.length} records · {transmission.reduce((s, l) => s + l.length, 0).toLocaleString()} chars · CWR v{version} · → {recipient}
              </div>
            </div>
            <button onClick={copy} className="ff-mono upper" style={{ padding: '8px 12px', fontSize: 10, fontWeight: 600, letterSpacing: '.08em', background: 'var(--paper)', border: '1px solid var(--rule)', cursor: 'pointer' }}>copy</button>
            <button onClick={() => setValidationOpen((v) => !v)} className="ff-mono upper" title={`${validation.summary.errors} errors · ${validation.summary.warnings} warnings`}
              style={{
                padding: '8px 12px', fontSize: 10, fontWeight: 600, letterSpacing: '.08em',
                background: validationOpen ? 'var(--ink)' : 'var(--paper)',
                color: validationOpen ? 'var(--paper)' : 'var(--ink)',
                border: '1px solid var(--rule)', cursor: 'pointer',
                display: 'inline-flex', alignItems: 'center', gap: 6,
              }}>
              <span style={{ width: 7, height: 7, borderRadius: 99,
                background: validation.summary.errors ? '#c0392b' : (validation.summary.warnings ? '#d68910' : '#2e7d4f') }} />
              validate
              {validation.summary.total > 0 && <span style={{ fontWeight: 500, color: validationOpen ? 'var(--paper)' : 'var(--ink-3)' }}>· {validation.summary.errors}E / {validation.summary.warnings}W</span>}
            </button>
            <button onClick={download} className="ff-mono upper" style={{ padding: '8px 12px', fontSize: 10, fontWeight: 600, letterSpacing: '.08em', background: 'var(--paper)', border: '1px solid var(--rule)', cursor: 'pointer' }}>download</button>
            <button onClick={() => setDistOpen(true)} className="ff-mono upper" style={{ padding: '8px 14px', fontSize: 10, fontWeight: 600, letterSpacing: '.08em', background: 'var(--ink)', color: 'var(--paper)', border: 0, cursor: 'pointer' }}>distribute →</button>
          </div>

          {validationOpen && <ValidationPanel report={validation} version={version} onJump={(line) => setActiveLine(line)} />}

          {/* preview body — split if compareVer set, otherwise lines+inspector */}
          {compareVer ? (
            <CompareView lines={transmission} compareLines={transmissionCompare} version={version} compareVer={compareVer} />
          ) : (
            <div style={{ flex: 1, display: 'grid', gridTemplateColumns: '1fr 360px', minHeight: 0 }}>
              <LinesView lines={transmission} active={activeLine} onPick={setActiveLine} version={version} />
              <Inspector decoded={decoded} line={transmission[activeLine]} />
            </div>
          )}
        </main>

        {distOpen && (
          <DistributeModal
            onClose={() => setDistOpen(false)}
            works={[...selected].map((id) => all.find((w) => w.id === id)).filter(Boolean)}
            version={version}
            txnType={txnType}
            senderIpi={senderIpi}
            senderName={senderName}
          />
        )}
      </div>
    );
  }

  // ─── Distribute modal: one file per recipient society ─────────────────────
  function DistributeModal({ onClose, works, version, txnType, senderIpi, senderName }) {
    const [picked, setPicked] = useState(() => new Set(['ASCAP','BMI','MLC']));
    const [stage, setStage] = useState('config'); // config | review | sent
    const [transport, setTransport] = useState('cwr-portal'); // cwr-portal | sftp | email
    const [delivery, setDelivery] = useState('immediate'); // immediate | scheduled
    const [scheduleAt, setScheduleAt] = useState('2026-05-13T09:00');
    const [groupBy, setGroupBy] = useState('one-file'); // one-file | per-territory
    const togglePick = (code) => setPicked((s) => {
      const n = new Set(s); if (n.has(code)) n.delete(code); else n.add(code); return n;
    });

    // Build one transmission per recipient
    const manifest = useMemo(() => {
      const hydrated = works.map((w) => window.CwrSynth.hydrateWork(w, { senderIpi, senderName }));
      return [...picked].map((code) => {
        const recv = RECIPIENTS.find((r) => r.code === code);
        const lines = window.CwrBuild.buildTransmission({
          version, senderType: 'PB', senderIpi, senderName,
          transactionType: txnType, works: hydrated, recipient: code,
          created: new Date(2026, 4, 12, 14, 22, 33),
        });
        const filename = window.CwrBuild.buildFilename({
          version, senderCode: 'PLU', recipientCode: code,
          sequence: 501 + [...picked].indexOf(code), created: new Date(2026, 4, 12),
        });
        return {
          code, name: recv?.name || code, filename,
          lines: lines.length, bytes: lines.reduce((s, l) => s + l.length + 2, 0),
          works: works.length, transport, eta: transport === 'cwr-portal' ? '< 5 min' : transport === 'sftp' ? '< 30 min' : '< 1 hr',
        };
      });
    }, [picked, works, version, txnType, senderIpi, senderName, transport]);

    const totalBytes = manifest.reduce((s, m) => s + m.bytes, 0);
    const totalRecords = manifest.reduce((s, m) => s + m.lines, 0);

    return (
      <div onClick={onClose} style={{ position: 'fixed', inset: 0, background: 'rgba(10,10,10,.55)', backdropFilter: 'blur(4px)', zIndex: 1200, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
        <div onClick={(e) => e.stopPropagation()} style={{ width: 'min(900px, 96vw)', maxHeight: '88vh', background: 'var(--bg)', border: '1px solid var(--rule)', boxShadow: '0 24px 60px rgba(0,0,0,.35)', display: 'flex', flexDirection: 'column' }}>
          {/* Header */}
          <div style={{ padding: '18px 24px', borderBottom: '1px solid var(--rule)', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <div>
              <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)' }}>BATCH DISTRIBUTION · CWR v{version}</div>
              <div className="ff-display" style={{ fontSize: 24, fontWeight: 700, letterSpacing: '-0.02em', marginTop: 4 }}>
                {stage === 'config' ? 'Distribute to societies' : stage === 'review' ? 'Review manifest' : 'Queued for delivery'}
              </div>
            </div>
            <button onClick={onClose} style={{ background: 'transparent', border: 'none', fontSize: 22, cursor: 'pointer', color: 'var(--ink-3)', padding: 4 }}>×</button>
          </div>

          {/* Body */}
          <div style={{ flex: 1, overflow: 'auto', padding: '20px 24px' }}>
            {stage === 'config' && (
              <div style={{ display: 'grid', gap: 18 }}>
                <div>
                  <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 8 }}>RECIPIENT SOCIETIES · {picked.size} selected</div>
                  <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(190px, 1fr))', gap: 6 }}>
                    {RECIPIENTS.map((r) => (
                      <button key={r.code} onClick={() => togglePick(r.code)} className="ff-mono"
                        style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '9px 11px', fontSize: 11, textAlign: 'left',
                          background: picked.has(r.code) ? 'var(--paper)' : 'transparent',
                          border: '1px solid ' + (picked.has(r.code) ? 'var(--ink)' : 'var(--rule)'),
                          cursor: 'pointer' }}>
                        <span style={{ width: 12, height: 12, border: '1px solid var(--ink-2)', background: picked.has(r.code) ? 'var(--ink)' : 'transparent', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', color: 'var(--paper)', fontSize: 9, fontWeight: 700, flex: '0 0 12px' }}>{picked.has(r.code) ? '✓' : ''}</span>
                        <span style={{ fontWeight: 600 }}>{r.code}</span>
                        <span style={{ color: 'var(--ink-3)', fontSize: 10, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{r.name}</span>
                      </button>
                    ))}
                  </div>
                </div>

                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18 }}>
                  <div>
                    <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 8 }}>TRANSPORT</div>
                    <div style={{ display: 'grid', gap: 5 }}>
                      {[
                        { v: 'cwr-portal', l: 'CWR Portal (CISAC EBU/CIS-Net)', sub: 'Direct society API · TLS 1.3' },
                        { v: 'sftp', l: 'SFTP drop', sub: 'Per-society credential vault' },
                        { v: 'email', l: 'Email attachment', sub: 'PGP-signed · for legacy CMOs' },
                      ].map((t) => (
                        <button key={t.v} onClick={() => setTransport(t.v)} style={{ textAlign: 'left', padding: '8px 11px', background: transport === t.v ? 'var(--paper)' : 'transparent', border: '1px solid ' + (transport === t.v ? 'var(--ink)' : 'var(--rule)'), cursor: 'pointer' }}>
                          <div className="ff-mono" style={{ fontSize: 11, fontWeight: 600 }}>{t.l}</div>
                          <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2 }}>{t.sub}</div>
                        </button>
                      ))}
                    </div>
                  </div>
                  <div>
                    <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 8 }}>DELIVERY</div>
                    <div style={{ display: 'grid', gap: 5, marginBottom: 14 }}>
                      {[
                        { v: 'immediate', l: 'Send immediately', sub: 'Queued on confirm' },
                        { v: 'scheduled', l: 'Schedule for…', sub: 'Hold until time' },
                      ].map((t) => (
                        <button key={t.v} onClick={() => setDelivery(t.v)} style={{ textAlign: 'left', padding: '8px 11px', background: delivery === t.v ? 'var(--paper)' : 'transparent', border: '1px solid ' + (delivery === t.v ? 'var(--ink)' : 'var(--rule)'), cursor: 'pointer' }}>
                          <div className="ff-mono" style={{ fontSize: 11, fontWeight: 600 }}>{t.l}</div>
                          <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2 }}>{t.sub}</div>
                        </button>
                      ))}
                    </div>
                    {delivery === 'scheduled' && (
                      <input type="datetime-local" value={scheduleAt} onChange={(e) => setScheduleAt(e.target.value)} className="ff-mono" style={{ width: '100%', padding: '7px 8px', fontSize: 11, background: 'var(--paper)', border: '1px solid var(--rule)' }} />
                    )}

                    <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginTop: 14, marginBottom: 8 }}>FILE GROUPING</div>
                    <div style={{ display: 'grid', gap: 5 }}>
                      {[
                        { v: 'one-file', l: 'One file per society', sub: 'Standard CWR' },
                        { v: 'per-territory', l: 'Per-society × per-territory', sub: 'For multi-jurisdiction CMOs' },
                      ].map((t) => (
                        <button key={t.v} onClick={() => setGroupBy(t.v)} style={{ textAlign: 'left', padding: '8px 11px', background: groupBy === t.v ? 'var(--paper)' : 'transparent', border: '1px solid ' + (groupBy === t.v ? 'var(--ink)' : 'var(--rule)'), cursor: 'pointer' }}>
                          <div className="ff-mono" style={{ fontSize: 11, fontWeight: 600 }}>{t.l}</div>
                          <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2 }}>{t.sub}</div>
                        </button>
                      ))}
                    </div>
                  </div>
                </div>
              </div>
            )}

            {stage === 'review' && (
              <div>
                <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', color: 'var(--ink-3)', marginBottom: 12 }}>FILES TO TRANSMIT · {manifest.length} · {(totalBytes/1024).toFixed(1)} KB · {totalRecords.toLocaleString()} records</div>
                <div style={{ border: '1px solid var(--rule)' }}>
                  <div className="ff-mono upper" style={{ display: 'grid', gridTemplateColumns: '60px 1fr 90px 80px 90px', padding: '8px 12px', background: 'var(--bg-2)', borderBottom: '1px solid var(--rule)', fontSize: 9, letterSpacing: '.1em', color: 'var(--ink-3)' }}>
                    <span>SOCIETY</span><span>FILENAME</span><span style={{ textAlign: 'right' }}>WORKS</span><span style={{ textAlign: 'right' }}>RECORDS</span><span style={{ textAlign: 'right' }}>ETA</span>
                  </div>
                  {manifest.map((m) => (
                    <div key={m.code} className="ff-mono" style={{ display: 'grid', gridTemplateColumns: '60px 1fr 90px 80px 90px', padding: '9px 12px', borderBottom: '1px solid var(--rule)', fontSize: 11 }}>
                      <span style={{ fontWeight: 700 }}>{m.code}</span>
                      <span style={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>{m.filename}</span>
                      <span style={{ textAlign: 'right' }}>{m.works}</span>
                      <span style={{ textAlign: 'right' }}>{m.lines}</span>
                      <span style={{ textAlign: 'right', color: 'var(--ink-3)' }}>{m.eta}</span>
                    </div>
                  ))}
                </div>
                <div className="ff-mono" style={{ marginTop: 14, padding: '12px 14px', background: 'var(--bg-2)', border: '1px solid var(--rule)', fontSize: 11, color: 'var(--ink-2)', lineHeight: 1.55 }}>
                  Transport: <strong>{transport === 'cwr-portal' ? 'CWR Portal' : transport.toUpperCase()}</strong>
                  {' · '}Delivery: <strong>{delivery === 'immediate' ? 'Immediate' : `Scheduled · ${scheduleAt.replace('T', ' ')}`}</strong>
                  {' · '}Audit log will record submitter, recipient codes, file SHA-256, and ack channel for each file.
                </div>
              </div>
            )}

            {stage === 'sent' && (
              <div style={{ textAlign: 'center', padding: '32px 24px' }}>
                <div className="ff-mono" style={{ fontSize: 56, marginBottom: 14 }}>✓</div>
                <div className="ff-display" style={{ fontSize: 24, fontWeight: 700, letterSpacing: '-0.02em' }}>{manifest.length} {manifest.length === 1 ? 'file' : 'files'} queued</div>
                <div className="ff-mono" style={{ fontSize: 12, color: 'var(--ink-3)', marginTop: 8 }}>Track delivery & acks on the Acks tab. Audit log entries created for each file.</div>
                <div style={{ marginTop: 24, display: 'flex', flexDirection: 'column', gap: 4, maxWidth: 520, margin: '24px auto 0', textAlign: 'left' }}>
                  {manifest.map((m) => (
                    <div key={m.code} className="ff-mono" style={{ display: 'flex', justifyContent: 'space-between', padding: '7px 11px', background: 'var(--bg-2)', border: '1px solid var(--rule)', fontSize: 11 }}>
                      <span><strong>{m.code}</strong> · {m.filename}</span>
                      <span style={{ color: 'oklch(0.55 0.14 145)' }}>QUEUED</span>
                    </div>
                  ))}
                </div>
              </div>
            )}
          </div>

          {/* Footer */}
          <div style={{ padding: '14px 24px', borderTop: '1px solid var(--rule)', display: 'flex', justifyContent: 'space-between', alignItems: 'center', background: 'var(--bg-2)' }}>
            <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>
              {stage === 'config' && `${picked.size} ${picked.size === 1 ? 'recipient' : 'recipients'} · ${works.length} ${works.length === 1 ? 'work' : 'works'}`}
              {stage === 'review' && `Confirms send · ${manifest.length} files`}
              {stage === 'sent' && 'Done'}
            </div>
            <div style={{ display: 'flex', gap: 8 }}>
              {stage === 'config' && (
                <>
                  <button onClick={onClose} className="ff-mono upper" style={{ padding: '9px 14px', fontSize: 10, fontWeight: 600, letterSpacing: '.08em', background: 'transparent', border: '1px solid var(--rule)', cursor: 'pointer' }}>cancel</button>
                  <button onClick={() => setStage('review')} disabled={picked.size === 0} className="ff-mono upper" style={{ padding: '9px 16px', fontSize: 10, fontWeight: 600, letterSpacing: '.08em', background: picked.size ? 'var(--ink)' : 'var(--ink-4)', color: 'var(--paper)', border: 0, cursor: picked.size ? 'pointer' : 'not-allowed' }}>review →</button>
                </>
              )}
              {stage === 'review' && (
                <>
                  <button onClick={() => setStage('config')} className="ff-mono upper" style={{ padding: '9px 14px', fontSize: 10, fontWeight: 600, letterSpacing: '.08em', background: 'transparent', border: '1px solid var(--rule)', cursor: 'pointer' }}>← back</button>
                  <button onClick={() => { setStage('sent'); window.toast && window.toast(`Queued ${manifest.length} CWR files for distribution`); }} className="ff-mono upper" style={{ padding: '9px 16px', fontSize: 10, fontWeight: 600, letterSpacing: '.08em', background: 'var(--ink)', color: 'var(--paper)', border: 0, cursor: 'pointer' }}>{delivery === 'immediate' ? 'send now' : 'schedule send'}</button>
                </>
              )}
              {stage === 'sent' && (
                <button onClick={onClose} className="ff-mono upper" style={{ padding: '9px 16px', fontSize: 10, fontWeight: 600, letterSpacing: '.08em', background: 'var(--ink)', color: 'var(--paper)', border: 0, cursor: 'pointer' }}>close</button>
              )}
            </div>
          </div>
        </div>
      </div>
    );
  }

  // ─── Lines view ───────────────────────────────────────────────────────────
  function LinesView({ lines, active, onPick, version }) {
    if (window.CwrHighlight && window.CwrHighlight.Viewer) {
      return React.createElement(window.CwrHighlight.Viewer, {
        lines, version, active, onPick,
      });
    }
    return (
      <div style={{ overflow: 'auto', background: 'var(--paper)', fontFamily: 'ui-monospace, "SF Mono", Menlo, monospace', fontSize: 11, lineHeight: 1.6 }}>
        <div style={{ minWidth: 'max-content' }}>
          {lines.map((line, i) => {
            const recType = line.slice(0, 3);
            const isControl = ['HDR', 'GRH', 'GRT', 'TRL'].includes(recType);
            const color = colorForRec(recType);
            return (
              <div key={i} onClick={() => onPick(i)}
                style={{
                  display: 'flex', cursor: 'pointer', padding: '0 16px',
                  background: active === i ? 'var(--bg-2)' : (isControl ? 'rgba(0,0,0,0.025)' : 'transparent'),
                  borderLeft: active === i ? '3px solid var(--ink)' : '3px solid transparent',
                }}>
                <span style={{ width: 50, color: 'var(--ink-4)', flex: '0 0 50px' }}>{String(i + 1).padStart(4, '0')}</span>
                <span style={{ width: 38, fontWeight: 700, color, flex: '0 0 38px' }}>{recType}</span>
                <span style={{ whiteSpace: 'pre', color: 'var(--ink)' }}>{line.slice(3)}</span>
              </div>
            );
          })}
        </div>
      </div>
    );
  }

  // ─── Validation panel ─────────────────────────────────────────────────────
  function ValidationPanel({ report, version, onJump }) {
    const [filter, setFilter] = useState('all'); // all | error | warn
    const issues = report.issues || [];
    const filtered = issues.filter((i) => filter === 'all' || i.severity === filter);
    const grouped = useMemo(() => {
      const g = {};
      filtered.forEach((iss) => {
        const cat = iss.rule.split('.')[0];
        (g[cat] = g[cat] || []).push(iss);
      });
      return g;
    }, [filtered]);

    const summary = report.summary || { errors: 0, warnings: 0, ok: true };
    const ruleDescriptions = {
      'file': 'Transmission structure',
      'length': 'Record length',
      'charset': 'Character set',
      'iswc': 'ISWC check digit (ISO 15707)',
      'ipi': 'IPI check digit (CISAC IPI Manual)',
      'isrc': 'ISRC format (ISO 3901)',
      'society': 'CISAC society code',
      'tis': 'TIS territory code',
      'shares': 'Share reconciliation',
      'pwr': 'Publisher-Writer chain',
      'trl': 'TRL counts',
    };

    return (
      <div style={{ borderBottom: '1px solid var(--rule)', background: 'var(--bg-2)' }}>
        {/* Compact summary band */}
        <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--rule)', display: 'flex', alignItems: 'center', gap: 16, flexWrap: 'wrap' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <span style={{ width: 11, height: 11, borderRadius: 99,
              background: summary.errors ? '#c0392b' : (summary.warnings ? '#d68910' : '#2e7d4f') }} />
            <span className="ff-mono upper" style={{ fontSize: 10, letterSpacing: '.12em', fontWeight: 600 }}>
              {summary.errors === 0 && summary.warnings === 0 ? 'CISAC compliance · pass' :
                summary.errors === 0 ? `Pass with ${summary.warnings} warning${summary.warnings === 1 ? '' : 's'}` :
                `${summary.errors} error${summary.errors === 1 ? '' : 's'} · ${summary.warnings} warning${summary.warnings === 1 ? '' : 's'}`}
            </span>
          </div>
          <div style={{ flex: 1 }} />
          <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)' }}>
            CWR v{version} · {Object.keys(ruleDescriptions).length} rule families · IPI mod-101 · ISWC mod-10 · TIS · CISAC societies
          </div>
          <div style={{ display: 'flex', gap: 4 }}>
            {[
              { v: 'all', l: `all (${issues.length})` },
              { v: 'error', l: `errors (${summary.errors})` },
              { v: 'warn', l: `warnings (${summary.warnings})` },
            ].map((t) => (
              <button key={t.v} onClick={() => setFilter(t.v)} className="ff-mono"
                style={{
                  padding: '5px 10px', fontSize: 10, fontWeight: 600,
                  background: filter === t.v ? 'var(--ink)' : 'var(--paper)',
                  color: filter === t.v ? 'var(--paper)' : 'var(--ink)',
                  border: '1px solid var(--rule)', cursor: 'pointer',
                }}>{t.l}</button>
            ))}
          </div>
        </div>

        {/* Issues list */}
        <div style={{ maxHeight: 240, overflow: 'auto' }}>
          {filtered.length === 0 ? (
            <div style={{ padding: '28px 16px', textAlign: 'center', color: 'var(--ink-3)', fontSize: 12 }}>
              {issues.length === 0
                ? '✓ All structural and semantic rules pass — file is ready for society submission.'
                : 'No issues match this filter.'}
            </div>
          ) : (
            Object.keys(grouped).map((cat) => (
              <div key={cat} style={{ borderBottom: '1px solid var(--rule)' }}>
                <div className="ff-mono upper" style={{
                  padding: '8px 16px', fontSize: 9, letterSpacing: '.14em', color: 'var(--ink-3)',
                  background: 'var(--paper)', borderBottom: '1px solid var(--rule)',
                  display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                }}>
                  <span>{ruleDescriptions[cat] || cat}</span>
                  <span style={{ color: 'var(--ink-4)', letterSpacing: '.08em' }}>{grouped[cat].length} item{grouped[cat].length === 1 ? '' : 's'}</span>
                </div>
                {grouped[cat].map((iss, i) => (
                  <div key={i}
                    onClick={() => iss.line != null && onJump && onJump(iss.line)}
                    style={{
                      padding: '9px 16px 9px 14px',
                      borderBottom: '1px solid var(--rule)',
                      cursor: iss.line != null ? 'pointer' : 'default',
                      borderLeft: '3px solid ' + (iss.severity === 'error' ? '#c0392b' : '#d68910'),
                      display: 'flex', alignItems: 'flex-start', gap: 12,
                    }}>
                    <span className="ff-mono upper" style={{
                      flex: '0 0 60px', fontSize: 9, fontWeight: 700, letterSpacing: '.08em',
                      color: iss.severity === 'error' ? '#c0392b' : '#d68910', marginTop: 2,
                    }}>{iss.severity === 'error' ? 'error' : 'warn'}</span>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 12, color: 'var(--ink)', lineHeight: 1.4 }}>{iss.msg}</div>
                      <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 3, display: 'flex', gap: 12 }}>
                        <span>rule: <span style={{ color: 'var(--ink-2)' }}>{iss.rule}</span></span>
                        {iss.line != null && <span>line: <span style={{ color: 'var(--ink-2)' }}>{iss.line + 1}</span> →</span>}
                        {iss.work != null && <span>work: <span style={{ color: 'var(--ink-2)' }}>#{iss.work + 1}</span></span>}
                      </div>
                    </div>
                  </div>
                ))}
              </div>
            ))
          )}
        </div>
      </div>
    );
  }

  // ─── Compare view ─────────────────────────────────────────────────────────
  function CompareView({ lines, compareLines, version, compareVer }) {
    const max = Math.max(lines.length, compareLines.length);
    const rows = [];
    for (let i = 0; i < max; i++) rows.push([lines[i] || '', compareLines[i] || '']);
    return (
      <div style={{ flex: 1, display: 'grid', gridTemplateColumns: '1fr 1fr', minHeight: 0, fontFamily: 'ui-monospace, "SF Mono", Menlo, monospace', fontSize: 10 }}>
        {[[lines, version], [compareLines, compareVer]].map(([ls, vv], side) => (
          <div key={side} style={{ overflow: 'auto', borderRight: side === 0 ? '1px solid var(--rule)' : 0 }}>
            <div className="ff-mono upper" style={{ position: 'sticky', top: 0, padding: '8px 12px', background: 'var(--ink)', color: 'var(--paper)', fontSize: 10, fontWeight: 600, letterSpacing: '.08em', zIndex: 2 }}>
              CWR {vv} — {ls.length} records
            </div>
            <div style={{ minWidth: 'max-content' }}>
              {ls.map((line, i) => {
                const rt = line.slice(0, 3);
                const other = side === 0 ? compareLines[i] : lines[i];
                const diff = line !== other;
                return (
                  <div key={i} style={{ display: 'flex', padding: '0 12px', background: diff ? 'rgba(217, 119, 87, 0.08)' : 'transparent' }}>
                    <span style={{ width: 40, color: 'var(--ink-4)', flex: '0 0 40px' }}>{String(i + 1).padStart(4, '0')}</span>
                    <span style={{ width: 36, fontWeight: 700, color: colorForRec(rt), flex: '0 0 36px' }}>{rt}</span>
                    <span style={{ whiteSpace: 'pre', color: 'var(--ink)' }}>{line.slice(3)}</span>
                  </div>
                );
              })}
            </div>
          </div>
        ))}
      </div>
    );
  }

  // ─── Inspector ────────────────────────────────────────────────────────────
  function Inspector({ decoded, line }) {
    if (!decoded) return null;
    return (
      <div style={{ borderLeft: '1px solid var(--rule)', background: 'var(--bg-2)', overflow: 'auto', padding: '0 0 24px' }}>
        <div style={{ position: 'sticky', top: 0, padding: '12px 16px', background: 'var(--ink)', color: 'var(--paper)', borderBottom: '1px solid var(--rule)', zIndex: 2 }}>
          <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.12em', opacity: 0.6 }}>RECORD</div>
          <div className="ff-mono" style={{ fontSize: 18, fontWeight: 700, marginTop: 2 }}>{decoded.recType}</div>
          <div className="ff-mono" style={{ fontSize: 10, opacity: 0.7, marginTop: 2 }}>{recDescription(decoded.recType)} · {line.length} chars</div>
        </div>
        <div>
          {decoded.fields.map((f, i) => {
            const lookup = f.lookup ? window.CwrDecode.lookupCode(f.lookup, f.value) : null;
            const trimmed = String(f.value || '').trim();
            const isEmpty = !trimmed;
            return (
              <div key={i} style={{ padding: '9px 16px', borderBottom: '1px solid var(--rule)' }}>
                <div className="ff-mono upper" style={{ fontSize: 9, letterSpacing: '.1em', color: 'var(--ink-3)', marginBottom: 3 }}>{f.name}</div>
                <div className="ff-mono" style={{ fontSize: 12, color: isEmpty ? 'var(--ink-4)' : 'var(--ink)', wordBreak: 'break-all', whiteSpace: 'pre-wrap' }}>
                  {isEmpty ? '— blank —' : (f.kind === 'date' ? formatDate(trimmed) : f.kind === 'time' ? formatTime(trimmed) : trimmed)}
                </div>
                {lookup && (
                  <div style={{ marginTop: 4, fontSize: 11, color: 'var(--ink-2)' }}>
                    <span style={{ color: 'var(--ink-3)' }}>↳ </span>{lookup.label}{lookup.description ? ' — ' + lookup.description : ''}
                  </div>
                )}
              </div>
            );
          })}
        </div>
      </div>
    );
  }

  function colorForRec(rt) {
    const map = {
      HDR: 'oklch(0.55 0.18 30)', GRH: 'oklch(0.55 0.18 30)', GRT: 'oklch(0.55 0.18 30)', TRL: 'oklch(0.55 0.18 30)',
      NWR: 'oklch(0.45 0.16 260)', REV: 'oklch(0.45 0.16 260)', ISW: 'oklch(0.45 0.16 260)', EXC: 'oklch(0.45 0.16 260)',
      SPU: 'oklch(0.5 0.14 160)', SPT: 'oklch(0.5 0.10 160)', OPU: 'oklch(0.5 0.14 200)', OPT: 'oklch(0.5 0.10 200)',
      SWR: 'oklch(0.5 0.16 50)',  SWT: 'oklch(0.5 0.10 50)',  OWR: 'oklch(0.5 0.16 320)', OWT: 'oklch(0.5 0.10 320)',
      PWR: 'oklch(0.5 0.16 90)', ALT: 'oklch(0.5 0.10 280)', EWT: 'oklch(0.45 0.13 280)', VER: 'oklch(0.45 0.13 280)',
      PER: 'oklch(0.5 0.14 0)', REC: 'oklch(0.5 0.14 220)', ORN: 'oklch(0.5 0.12 130)',
      INS: 'oklch(0.5 0.10 60)', IND: 'oklch(0.5 0.10 60)', COM: 'oklch(0.5 0.12 180)', MSG: 'oklch(0.55 0.18 30)',
      ARI: 'oklch(0.5 0.10 100)', XRF: 'oklch(0.5 0.14 240)', ACK: 'oklch(0.55 0.18 30)',
    };
    return map[rt] || 'var(--ink-2)';
  }
  function recDescription(rt) {
    const REF = window.REF;
    if (REF && REF.ready && REF.raw && REF.raw.cwr_record_types) {
      const m = REF.raw.cwr_record_types.find((r) => r.code === rt);
      if (m) return m.label;
    }
    const fallback = {
      HDR: 'File Header', GRH: 'Group Header', GRT: 'Group Trailer', TRL: 'File Trailer',
      NWR: 'New Work Registration', REV: 'Revised Registration', ISW: 'ISWC Notification', EXC: 'Existing Work in Conflict',
      SPU: 'Submitter Publisher (Controlled)', SPT: 'Submitter Publisher Territory',
      OPU: 'Other Publisher', OPT: 'Other Publisher Territory',
      SWR: 'Submitter Writer (Controlled)', SWT: 'Submitter Writer Territory',
      OWR: 'Other Writer', OWT: 'Other Writer Territory',
      PWR: 'Publisher-for-Writer Link', ALT: 'Alternate Title', EWT: 'Entire Work Title',
      VER: 'Original Work for Version', PER: 'Performer', REC: 'Recording Detail', ORN: 'Work Origin',
      INS: 'Instrumentation Summary', IND: 'Instrumentation Detail', COM: 'Component Title',
      MSG: 'Message', ARI: 'Additional Related Info', XRF: 'Cross-Reference (v3)', ACK: 'Acknowledgement',
    };
    return fallback[rt] || rt;
  }
  function formatDate(s) {
    if (!s || !/^\d{8}$/.test(s)) return s;
    return s.slice(0, 4) + '-' + s.slice(4, 6) + '-' + s.slice(6, 8);
  }
  function formatTime(s) {
    if (!s || !/^\d{6}$/.test(s)) return s;
    return s.slice(0, 2) + ':' + s.slice(2, 4) + ':' + s.slice(4, 6);
  }

  window.CwrGenTab = CwrGenTab;
})();
