// stmt-mapper.jsx — Custom column-mapping UI for unrecognized statement formats
// ─────────────────────────────────────────────────────────────────
// Extends stmt-parser.jsx. When sniff confidence < 0.4, the user can
// hand-map columns to canonical fields and save the mapping as a
// custom adapter. Stored in localStorage under astro.stmt.custom-adapters
// and re-applied automatically on next file with matching headers.
//
// Exports: window.StmtMapperModal, window.STMT_CUSTOM_ADAPTERS_API
// ─────────────────────────────────────────────────────────────────
(function () {
  if (typeof window === 'undefined' || !window.React) return;
  const _S = React.useState, _E = React.useEffect, _M = React.useMemo;
  const LS_KEY = 'astro.stmt.custom-adapters';

  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 loadCustom() {
    try {
      const raw = localStorage.getItem(LS_KEY);
      return raw ? JSON.parse(raw) : [];
    } catch { return []; }
  }
  function saveCustom(arr) {
    try { localStorage.setItem(LS_KEY, JSON.stringify(arr)); } catch {}
    window.__STMT_CUSTOM_ADAPTERS = arr;
    syncIntoEngine(arr);
  }

  function syncIntoEngine(arr) {
    const eng = window.STMT_PARSER_ENGINE;
    if (!eng || !eng.ADAPTERS) return;
    eng.ADAPTERS = eng.ADAPTERS.filter(a => !a._custom);
    arr.forEach(spec => {
      const ad = {
        _custom: true,
        id: spec.id,
        label: spec.label + ' · custom',
        vendor: spec.vendor || 'Custom',
        format: spec.format || 'CSV',
        cols: spec.requiredCols || [],
        mapping: spec.mapping || {},
        sniff: (h) => {
          if (!h || !spec.sigCols?.length) return 0;
          const set = h.map(s => (s||'').toLowerCase());
          let hits = 0;
          for (const sig of spec.sigCols) {
            if (set.some(s => s === sig.toLowerCase())) hits++;
          }
          return Math.min(1, hits / spec.sigCols.length);
        },
      };
      eng.ADAPTERS.push(ad);
    });
  }

  const initial = loadCustom();
  window.__STMT_CUSTOM_ADAPTERS = initial;
  setTimeout(() => syncIntoEngine(initial), 50);

  window.STMT_CUSTOM_ADAPTERS_API = {
    list: () => loadCustom(),
    add: (spec) => {
      const arr = loadCustom();
      const idx = arr.findIndex(a => a.id === spec.id);
      if (idx >= 0) arr[idx] = spec; else arr.push(spec);
      saveCustom(arr);
    },
    remove: (id) => {
      saveCustom(loadCustom().filter(a => a.id !== id));
    },
  };

  function StmtMapperModal() {
    const [open, setOpen] = _S(false);
    const [ctx, setCtx] = _S(null);
    const [label, setLabel] = _S('');
    const [vendor, setVendor] = _S('');
    const [mapping, setMapping] = _S({});
    const [staticVals, setStaticVals] = _S({});

    _E(() => {
      function onOpen(e) {
        const d = e.detail || {};
        setCtx({ headers: d.headers || [], sample: d.sample || [], fileName: d.fileName || 'unknown.csv' });
        const auto = {};
        (d.headers || []).forEach(h => {
          const lo = h.toLowerCase();
          if (lo.includes('iswc')) auto.iswc = h;
          else if (lo.includes('isrc')) auto.isrc = h;
          else if (lo.includes('title') || lo === 'work' || lo === 'song') auto.workTitle = h;
          else if (lo.includes('writer') || lo.includes('composer')) auto.writers = h;
          else if (lo.includes('amount') || lo.includes('royalty') || lo.includes('payment')) auto.grossAmount = h;
          else if (lo.includes('currency')) auto.currency = h;
          else if (lo.includes('country') || lo.includes('territory')) auto.territory = h;
          else if (lo.includes('streams') || lo.includes('plays') || lo.includes('units') || lo.includes('performances') || lo.includes('views')) auto.units = h;
          else if (lo.includes('dsp') || lo.includes('service') || lo.includes('platform') || lo.includes('store')) auto.dsp = h;
          else if (lo.includes('period') && lo.includes('start')) auto.periodStart = h;
          else if (lo.includes('period') && lo.includes('end')) auto.periodEnd = h;
          else if (lo === 'period' || lo.includes('quarter') || lo.includes('month')) auto.periodStart = h;
        });
        setMapping(auto);
        setStaticVals({});
        setLabel('Custom · ' + (d.fileName || 'unknown'));
        setVendor('');
        setOpen(true);
      }
      window.addEventListener('astro-open-stmt-mapper', onOpen);
      return () => window.removeEventListener('astro-open-stmt-mapper', onOpen);
    }, []);

    if (!open || !ctx) return null;

    const eng = window.STMT_PARSER_ENGINE;
    const FIELDS = eng?.CANONICAL_FIELDS || [];
    const reqFields = FIELDS.filter(f => f.req);
    const optFields = FIELDS.filter(f => !f.req);

    function setMap(k, val) { setMapping(m => ({ ...m, [k]: val })); }
    function setStatic(k, val) { setStaticVals(s => ({ ...s, [k]: val })); }

    function handleSave() {
      const finalMapping = {};
      Object.entries(mapping).forEach(([k, v]) => { if (v) finalMapping[k] = v; });
      Object.entries(staticVals).forEach(([k, v]) => { if (v && v.trim()) finalMapping[k] = { static: v.trim() }; });

      const id = 'custom_' + (vendor || 'usr').toLowerCase().replace(/[^a-z0-9]+/g, '_') + '_' + Date.now().toString(36);
      const spec = {
        id, label,
        vendor: vendor || 'Custom',
        format: 'CSV',
        sigCols: ctx.headers.slice(0, 6),
        requiredCols: ctx.headers,
        mapping: finalMapping,
        createdAt: Date.now(),
        sourceFile: ctx.fileName,
      };
      window.STMT_CUSTOM_ADAPTERS_API.add(spec);
      window.dispatchEvent(new CustomEvent('astro-stmt-mapper-saved', { detail: { spec } }));
      if (window.toast) window.toast('Custom adapter saved · ' + label);
      setOpen(false);
    }

    function FieldRow({ f }) {
      const cur = mapping[f.k];
      const stat = staticVals[f.k];
      return (
        <div style={{ display: 'grid', gridTemplateColumns: '180px 1fr 200px', gap: 12, padding: '10px 14px', borderBottom: '1px solid var(--rule-soft)', alignItems: 'center' }}>
          <div>
            <Mono size={11} style={{ fontWeight: 600 }}>{f.k}</Mono>
            <div style={{ fontSize: 10, color: f.req ? '#a32a18' : 'var(--ink-3)', marginTop: 2 }}>{f.req ? 'REQUIRED · ' : ''}{f.t}</div>
          </div>
          <select value={typeof cur === 'string' ? cur : ''} onChange={(e) => setMap(f.k, e.target.value || null)}
            style={{ width: '100%', padding: '6px 8px', border: '1px solid var(--rule)', fontSize: 12, background: 'var(--bg)', color: 'var(--ink)', fontFamily: 'IBM Plex Mono, monospace' }}>
            <option value="">— map to column —</option>
            {ctx.headers.map(h => <option key={h} value={h}>{h}</option>)}
          </select>
          <input type="text" value={stat || ''} onChange={(e) => setStatic(f.k, e.target.value)} placeholder="or static value"
            style={{ width: '100%', padding: '6px 8px', border: '1px solid var(--rule)', fontSize: 11, background: 'var(--bg)', color: 'var(--ink)', fontFamily: 'IBM Plex Mono, monospace' }}/>
        </div>
      );
    }

    const reqMapped = reqFields.filter(f => mapping[f.k] || staticVals[f.k]).length;
    const reqMissing = reqFields.length - reqMapped;

    return (
      <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', zIndex: 9999, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20 }}>
        <div style={{ background: 'var(--paper)', width: '95%', maxWidth: 1100, maxHeight: '92vh', overflow: 'hidden', display: 'flex', flexDirection: 'column', border: '1px solid var(--rule)' }}>
          <div style={{ padding: '18px 22px', borderBottom: '1px solid var(--rule)', display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
            <div>
              <Mono upper size={9} color="var(--ink-3)">CUSTOM ADAPTER · MAP COLUMNS</Mono>
              <div style={{ fontSize: 18, fontWeight: 600, marginTop: 4, fontFamily: 'Space Grotesk' }}>{ctx.fileName}</div>
              <div style={{ fontSize: 12, color: 'var(--ink-2)', marginTop: 4 }}>{ctx.headers.length} columns detected · {ctx.sample.length} sample rows</div>
            </div>
            <button onClick={() => setOpen(false)} style={{ padding: '6px 12px', border: '1px solid var(--rule)', fontSize: 11, background: 'transparent', cursor: 'pointer' }}>Close</button>
          </div>

          <div style={{ flex: 1, overflow: 'auto', padding: '20px 22px' }}>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14, marginBottom: 22 }}>
              <div>
                <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 6 }}>ADAPTER LABEL</Mono>
                <input type="text" value={label} onChange={(e) => setLabel(e.target.value)} style={{ width: '100%', padding: '8px 10px', border: '1px solid var(--rule)', fontSize: 13, background: 'var(--bg)', color: 'var(--ink)', boxSizing: 'border-box' }}/>
              </div>
              <div>
                <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 6 }}>VENDOR / SOURCE</Mono>
                <input type="text" value={vendor} onChange={(e) => setVendor(e.target.value)} placeholder="e.g. SACEM, MROC, AdRev" style={{ width: '100%', padding: '8px 10px', border: '1px solid var(--rule)', fontSize: 13, background: 'var(--bg)', color: 'var(--ink)', boxSizing: 'border-box' }}/>
              </div>
            </div>

            <div style={{ marginBottom: 22 }}>
              <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 8 }}>FIRST 3 ROWS · PREVIEW</Mono>
              <div style={{ overflow: 'auto', border: '1px solid var(--rule)' }}>
                <table style={{ borderCollapse: 'collapse', minWidth: '100%', fontSize: 11 }}>
                  <thead><tr style={{ background: 'var(--bg-2)' }}>
                    {ctx.headers.map(h => <th key={h} style={{ padding: '7px 10px', textAlign: 'left', borderBottom: '1px solid var(--rule)', fontWeight: 600, whiteSpace: 'nowrap' }}><Mono size={10}>{h}</Mono></th>)}
                  </tr></thead>
                  <tbody>
                    {ctx.sample.slice(0, 3).map((row, i) => (
                      <tr key={i}>
                        {ctx.headers.map(h => <td key={h} style={{ padding: '7px 10px', borderBottom: '1px solid var(--rule-soft)', whiteSpace: 'nowrap', maxWidth: 200, overflow: 'hidden', textOverflow: 'ellipsis' }}>{row[h] || '—'}</td>)}
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            </div>

            <div style={{ marginBottom: 18 }}>
              <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 8 }}>REQUIRED FIELDS · {reqMapped}/{reqFields.length} mapped</Mono>
              <div style={{ border: '1px solid var(--rule)' }}>
                <div style={{ display: 'grid', gridTemplateColumns: '180px 1fr 200px', gap: 12, padding: '8px 14px', background: 'var(--bg-2)', borderBottom: '1px solid var(--rule)' }}>
                  <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)">OR STATIC VALUE</Mono>
                </div>
                {reqFields.map((f) => <FieldRow key={f.k} f={f}/>)}
              </div>
            </div>

            <div>
              <Mono upper size={9} color="var(--ink-3)" style={{ display: 'block', marginBottom: 8 }}>OPTIONAL FIELDS</Mono>
              <div style={{ border: '1px solid var(--rule)' }}>
                {optFields.map((f) => <FieldRow key={f.k} f={f}/>)}
              </div>
            </div>
          </div>

          <div style={{ padding: '14px 22px', borderTop: '1px solid var(--rule)', display: 'flex', justifyContent: 'space-between', alignItems: 'center', background: 'var(--bg-2)' }}>
            <div style={{ fontSize: 12, color: reqMissing ? '#a32a18' : '#0a8754' }}>
              {reqMissing ? `${reqMissing} required field${reqMissing>1?'s':''} unmapped` : 'All required fields mapped'}
            </div>
            <div style={{ display: 'flex', gap: 10 }}>
              <button onClick={() => setOpen(false)} style={{ padding: '8px 14px', border: '1px solid var(--rule)', fontSize: 12, background: 'transparent', cursor: 'pointer' }}>Cancel</button>
              <button onClick={handleSave} disabled={reqMissing > 0 || !label.trim()}
                style={{ padding: '8px 16px', border: '1px solid var(--ink)', fontSize: 12, background: reqMissing > 0 ? 'var(--rule)' : 'var(--ink)', color: reqMissing > 0 ? 'var(--ink-3)' : 'var(--bg)', cursor: reqMissing > 0 ? 'not-allowed' : 'pointer', fontWeight: 600 }}>
                Save adapter
              </button>
            </div>
          </div>
        </div>
      </div>
    );
  }

  window.StmtMapperModal = StmtMapperModal;
  console.log('[StmtMapper] loaded · ' + initial.length + ' custom adapters');
})();
