// fee-engine.jsx — Admin / sub-pub fee cascade
// ─────────────────────────────────────────────────────────────────
// Computes the fee that an administrator or sub-publisher takes off
// the top of incoming royalties before paying writers. The "cascade"
// supports source-specific rates (mech vs perf vs sync), territory
// overrides, deal-type overrides, writer-class overrides, and
// minimum/maximum bounds.
//
// API:
//   FEE_ENGINE.compute({ grossAmount, productType, territory, writerId, dealType })
//     → { gross, fee, net, feePct, rule, breakdown[] }
//   FEE_ENGINE.rules / setRules / addRule / removeRule
//   FEE_ENGINE.testCase(scenario) — preview
//
// Tabs:
//   01 RULES      — cascade editor (priority list, drag-reorder)
//   02 SIMULATOR  — try a scenario, see which rule fires + the math
//   03 BREAKDOWN  — apply rules to current __STMT_INDEX, report by writer
//
// Persists via localStorage (astro.fee.rules.v1).
// ─────────────────────────────────────────────────────────────────
(function () {
  if (typeof window === 'undefined' || !window.React) return;
  const _S = React.useState, _E = React.useEffect, _M = React.useMemo;

  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>;
  }

  // ─── Default cascade ─────────────────────────────────────────
  const DEFAULT_RULES = [
    { id: 'r-sync-priority',    name: 'Sync — premium rate',          priority: 10, match: { productType: 'sync' },         feePct: 25, active: true, desc: 'Sync licenses earn 25% admin fee due to negotiation work.' },
    { id: 'r-neighbour',        name: 'Neighbouring rights',           priority: 20, match: { productType: 'neighbour' },    feePct: 20, active: true },
    { id: 'r-mech-us',          name: 'Mechanical · US',                priority: 30, match: { productType: 'mechanical', territory: 'US' }, feePct: 12, active: true },
    { id: 'r-mech-default',     name: 'Mechanical · default',           priority: 40, match: { productType: 'mechanical' },  feePct: 15, active: true },
    { id: 'r-perf-default',     name: 'Performance · default',          priority: 50, match: { productType: 'performance' }, feePct: 10, active: true },
    { id: 'r-fallback',         name: 'Fallback',                       priority: 99, match: {},                              feePct: 10, active: true, desc: 'Catch-all when no other rule matches.' },
  ];

  const STORAGE_KEY = 'astro.fee.rules.v1';

  function loadRules() {
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (raw) {
        const parsed = JSON.parse(raw);
        if (Array.isArray(parsed) && parsed.length) return parsed;
      }
    } catch (e) {}
    return DEFAULT_RULES.map(r => ({ ...r }));
  }
  function saveRules(rules) {
    try { localStorage.setItem(STORAGE_KEY, JSON.stringify(rules)); } catch (e) {}
  }

  let RULES = loadRules();

  function ruleMatches(rule, ctx) {
    if (!rule.active) return false;
    const m = rule.match || {};
    if (m.productType && m.productType !== ctx.productType) return false;
    if (m.usageType && m.usageType !== ctx.usageType) return false;
    if (m.territory && m.territory !== ctx.territory) return false;
    if (m.dealType && m.dealType !== ctx.dealType) return false;
    if (m.writerId && m.writerId !== ctx.writerId) return false;
    if (m.dsp && m.dsp !== ctx.dsp) return false;
    return true;
  }

  function compute(ctx) {
    const gross = +ctx.grossAmount || 0;
    if (gross <= 0) return { gross: 0, fee: 0, net: 0, feePct: 0, rule: null, breakdown: [] };
    const sorted = [...RULES].sort((a, b) => (a.priority || 99) - (b.priority || 99));
    const breakdown = [];
    let matched = null;
    for (const r of sorted) {
      const ok = ruleMatches(r, ctx);
      breakdown.push({ ruleId: r.id, name: r.name, matched: ok, feePct: r.feePct });
      if (ok && !matched) matched = r;
    }
    const feePct = matched ? matched.feePct : 10;
    let fee = gross * (feePct / 100);
    if (matched?.minFee != null) fee = Math.max(fee, matched.minFee);
    if (matched?.maxFee != null) fee = Math.min(fee, matched.maxFee);
    const net = gross - fee;
    return { gross, fee, net, feePct, rule: matched, breakdown };
  }

  function setRules(next) {
    RULES = Array.isArray(next) ? next : RULES;
    saveRules(RULES);
    window.dispatchEvent(new CustomEvent('astro-fee-rules-updated'));
  }
  function addRule(r) {
    const id = r.id || ('r-' + Math.random().toString(36).slice(2, 9));
    setRules([...RULES, { ...r, id, active: r.active !== false }]);
  }
  function updateRule(id, patch) {
    setRules(RULES.map(r => r.id === id ? { ...r, ...patch } : r));
  }
  function removeRule(id) { setRules(RULES.filter(r => r.id !== id)); }
  function getRules() { return [...RULES]; }
  function resetRules() { setRules(DEFAULT_RULES.map(r => ({ ...r }))); }

  window.FEE_ENGINE = { compute, getRules, setRules, addRule, updateRule, removeRule, resetRules, DEFAULT_RULES };

  // ─── SCREEN ──────────────────────────────────────────────────
  function ScreenFeeEngine({ go, payload }) {
    const PageHeader = window.PageHeader;
    const [tab, setTab] = _S(payload?.tab || 'rules');
    const [rules, setLocalRules] = _S(getRules());
    const [_v, bump] = _S(0);
    _E(() => {
      const onUpd = () => { setLocalRules(getRules()); bump(x => x + 1); };
      window.addEventListener('astro-fee-rules-updated', onUpd);
      return () => window.removeEventListener('astro-fee-rules-updated', onUpd);
    }, []);

    return (
      <div>
        {PageHeader && (
          <PageHeader
            eyebrow={['ROYALTIES', 'FEE ENGINE', `${rules.filter(r => r.active).length} ACTIVE RULES`]}
            title="set the cascade."
            highlight="cascade"
            sub="Admin & sub-publishing fee logic — product type × territory × deal × writer. The first matching rule (by priority) wins."
          />
        )}

        <div style={{ borderBottom: '1px solid var(--rule)', display: 'flex', gap: 0, marginBottom: 24 }}>
          {[{ k: 'rules', l: 'Rules' }, { k: 'simulator', l: 'Simulator' }, { k: 'breakdown', l: 'Apply to inbox' }].map(t => (
            <button key={t.k} onClick={() => setTab(t.k)} style={{
              padding: '14px 22px', background: 'transparent',
              borderBottom: '2px solid ' + (tab === t.k ? 'var(--ink)' : 'transparent'),
              color: tab === t.k ? 'var(--ink)' : 'var(--ink-3)',
              fontSize: 13, fontWeight: tab === t.k ? 600 : 400, cursor: 'pointer',
            }}>{t.l}</button>
          ))}
        </div>

        {tab === 'rules' && <RulesTab rules={rules}/>}
        {tab === 'simulator' && <SimulatorTab/>}
        {tab === 'breakdown' && <BreakdownTab/>}
      </div>
    );
  }

  // ─── 01 RULES ────────────────────────────────────────────────
  function RulesTab({ rules }) {
    const [editing, setEditing] = _S(null);

    function startNew() {
      setEditing({ id: '', name: 'New rule', priority: 35, match: {}, feePct: 12, active: true, isNew: true });
    }

    return (
      <div>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 14 }}>
          <Mono upper size={9} color="var(--ink-3)">CASCADE · LOWER PRIORITY # WINS FIRST</Mono>
          <div style={{ display: 'flex', gap: 8 }}>
            <button onClick={startNew} style={{ padding: '7px 14px', border: '1px solid var(--ink)', background: 'var(--ink)', color: 'var(--bg)', fontSize: 11, cursor: 'pointer' }}>+ Add rule</button>
            <button onClick={() => { if (confirm('Reset all fee rules to defaults?')) resetRules(); }} style={{ padding: '7px 14px', border: '1px solid var(--rule)', background: 'transparent', fontSize: 11, cursor: 'pointer' }}>Reset to defaults</button>
          </div>
        </div>

        <div style={{ border: '1px solid var(--rule)' }}>
          <div style={{ display: 'grid', gridTemplateColumns: '60px 1fr 200px 80px 70px 100px', gap: 12, padding: '10px 16px', background: 'var(--bg-2)', borderBottom: '1px solid var(--rule)' }}>
            <Mono upper size={9} color="var(--ink-3)">PRI</Mono>
            <Mono upper size={9} color="var(--ink-3)">RULE</Mono>
            <Mono upper size={9} color="var(--ink-3)">MATCH</Mono>
            <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'right' }}>FEE %</Mono>
            <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'center' }}>ACTIVE</Mono>
            <Mono upper size={9} color="var(--ink-3)" style={{ textAlign: 'right' }}>—</Mono>
          </div>
          {[...rules].sort((a, b) => (a.priority || 99) - (b.priority || 99)).map((r, i, arr) => (
            <div key={r.id} style={{ display: 'grid', gridTemplateColumns: '60px 1fr 200px 80px 70px 100px', gap: 12, padding: '11px 16px', borderBottom: i < arr.length - 1 ? '1px solid var(--rule-soft)' : 0, alignItems: 'center', opacity: r.active ? 1 : 0.5 }}>
              <Mono size={11}>{r.priority}</Mono>
              <div>
                <div style={{ fontSize: 13, fontWeight: 500 }}>{r.name}</div>
                {r.desc && <div style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 2 }}>{r.desc}</div>}
              </div>
              <Mono size={10} color="var(--ink-3)">{matchToString(r.match) || 'any'}</Mono>
              <Mono size={12} style={{ textAlign: 'right', fontWeight: 600 }}>{r.feePct}%</Mono>
              <div style={{ textAlign: 'center' }}>
                <input type="checkbox" checked={r.active} onChange={(e) => updateRule(r.id, { active: e.target.checked })}/>
              </div>
              <div style={{ textAlign: 'right', display: 'flex', gap: 6, justifyContent: 'flex-end' }}>
                <button onClick={() => setEditing({ ...r })} style={{ padding: '4px 8px', border: '1px solid var(--rule)', background: 'transparent', fontSize: 10, cursor: 'pointer' }}>Edit</button>
                <button onClick={() => { if (confirm('Delete this rule?')) removeRule(r.id); }} style={{ padding: '4px 8px', border: '1px solid var(--rule)', background: 'transparent', fontSize: 10, cursor: 'pointer', color: '#a32a18' }}>×</button>
              </div>
            </div>
          ))}
        </div>

        {editing && <RuleEditor rule={editing} onClose={() => setEditing(null)} onSave={(r) => {
          if (editing.isNew) addRule(r); else updateRule(editing.id, r);
          setEditing(null);
        }}/>}
      </div>
    );
  }

  function matchToString(m) {
    if (!m) return '';
    return Object.entries(m).filter(([_, v]) => v).map(([k, v]) => `${k}=${v}`).join(' · ');
  }

  function RuleEditor({ rule, onClose, onSave }) {
    const [draft, setDraft] = _S({ ...rule });
    function setM(k, v) {
      const m = { ...(draft.match || {}) };
      if (!v) delete m[k]; else m[k] = v;
      setDraft({ ...draft, match: m });
    }
    return (
      <div role="dialog" style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.4)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 100 }}>
        <div style={{ background: 'var(--bg)', border: '1px solid var(--rule)', width: 520, padding: 24 }}>
          <Mono upper size={9} color="var(--ink-3)">{draft.isNew ? 'NEW RULE' : 'EDIT RULE'}</Mono>
          <div className="ff-display" style={{ fontSize: 22, fontWeight: 600, marginTop: 6, marginBottom: 18 }}>Cascade rule</div>

          <div style={{ display: 'grid', gridTemplateColumns: '110px 1fr', gap: 12, alignItems: 'center', fontSize: 12 }}>
            <label>Name</label>
            <input value={draft.name || ''} onChange={(e) => setDraft({ ...draft, name: e.target.value })} style={inp}/>
            <label>Priority</label>
            <input type="number" value={draft.priority || 50} onChange={(e) => setDraft({ ...draft, priority: +e.target.value })} style={inp}/>
            <label>Fee %</label>
            <input type="number" step="0.5" value={draft.feePct || 0} onChange={(e) => setDraft({ ...draft, feePct: +e.target.value })} style={inp}/>
            <label style={{ alignSelf: 'flex-start', marginTop: 8 }}>Match</label>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
              <select value={draft.match?.productType || ''} onChange={(e) => setM('productType', e.target.value)} style={inp}>
                <option value="">any product type</option>
                <option value="mechanical">mechanical</option>
                <option value="performance">performance</option>
                <option value="sync">sync</option>
                <option value="neighbour">neighbour</option>
                <option value="print">print</option>
              </select>
              <input placeholder="territory (e.g. US)" value={draft.match?.territory || ''} onChange={(e) => setM('territory', e.target.value.toUpperCase())} style={inp}/>
              <input placeholder="dsp (e.g. Spotify)" value={draft.match?.dsp || ''} onChange={(e) => setM('dsp', e.target.value)} style={inp}/>
              <input placeholder="writerId (specific)" value={draft.match?.writerId || ''} onChange={(e) => setM('writerId', e.target.value)} style={inp}/>
            </div>
            <label>Description</label>
            <textarea value={draft.desc || ''} onChange={(e) => setDraft({ ...draft, desc: e.target.value })} style={{ ...inp, minHeight: 50 }}/>
          </div>

          <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 22 }}>
            <button onClick={onClose} style={{ padding: '7px 14px', border: '1px solid var(--rule)', background: 'transparent', fontSize: 12, cursor: 'pointer' }}>Cancel</button>
            <button onClick={() => onSave(draft)} style={{ padding: '7px 14px', border: '1px solid var(--ink)', background: 'var(--ink)', color: 'var(--bg)', fontSize: 12, cursor: 'pointer' }}>Save</button>
          </div>
        </div>
      </div>
    );
  }

  const inp = { padding: '7px 10px', border: '1px solid var(--rule)', background: 'var(--bg)', color: 'var(--ink)', fontSize: 12 };

  // ─── 02 SIMULATOR ────────────────────────────────────────────
  function SimulatorTab() {
    const [scenario, setScenario] = _S({ grossAmount: 1000, productType: 'mechanical', territory: 'US', dsp: 'Spotify', writerId: '' });
    const result = _M(() => compute(scenario), [scenario]);

    return (
      <div style={{ display: 'grid', gridTemplateColumns: '320px 1fr', gap: 22 }}>
        <div style={{ border: '1px solid var(--rule)', padding: 18, background: 'var(--paper)' }}>
          <Mono upper size={9} color="var(--ink-3)">SCENARIO</Mono>
          <div style={{ display: 'grid', gridTemplateColumns: '90px 1fr', gap: 10, alignItems: 'center', fontSize: 12, marginTop: 14 }}>
            <label>Gross USD</label>
            <input type="number" value={scenario.grossAmount} onChange={(e) => setScenario({ ...scenario, grossAmount: +e.target.value })} style={inp}/>
            <label>Product</label>
            <select value={scenario.productType} onChange={(e) => setScenario({ ...scenario, productType: e.target.value })} style={inp}>
              <option value="mechanical">mechanical</option>
              <option value="performance">performance</option>
              <option value="sync">sync</option>
              <option value="neighbour">neighbour</option>
              <option value="print">print</option>
            </select>
            <label>Territory</label>
            <input value={scenario.territory} onChange={(e) => setScenario({ ...scenario, territory: e.target.value.toUpperCase() })} style={inp}/>
            <label>DSP</label>
            <input value={scenario.dsp} onChange={(e) => setScenario({ ...scenario, dsp: e.target.value })} style={inp}/>
          </div>
        </div>

        <div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', borderTop: '1px solid var(--rule)', borderBottom: '1px solid var(--rule)', marginBottom: 18 }}>
            <Stat l="GROSS" v={'$' + result.gross.toFixed(2)}/>
            <Stat l={`FEE · ${result.feePct}%`} v={'−$' + result.fee.toFixed(2)} tone="#a32a18" border/>
            <Stat l="NET" v={'$' + result.net.toFixed(2)} tone="#0a8754"/>
          </div>

          <Mono upper size={9} color="var(--ink-3)">RULE TRACE · WHICH RULE FIRED</Mono>
          <div style={{ border: '1px solid var(--rule)', marginTop: 10 }}>
            {result.breakdown.map((b, i) => (
              <div key={b.ruleId} style={{ display: 'grid', gridTemplateColumns: '40px 1fr 80px 70px', gap: 12, padding: '10px 16px', borderBottom: i < result.breakdown.length - 1 ? '1px solid var(--rule-soft)' : 0, fontSize: 12, opacity: b.matched ? 1 : 0.55 }}>
                <Mono size={11}>{b.matched ? (result.rule?.id === b.ruleId ? '★' : '✓') : '·'}</Mono>
                <div>{b.name}</div>
                <Mono size={11} style={{ textAlign: 'right' }}>{b.feePct}%</Mono>
                <Mono upper size={9} color={b.matched ? (result.rule?.id === b.ruleId ? '#0a8754' : 'var(--ink-2)') : 'var(--ink-4)'} style={{ textAlign: 'right' }}>{result.rule?.id === b.ruleId ? 'APPLIED' : (b.matched ? 'matched' : 'skipped')}</Mono>
              </div>
            ))}
          </div>
        </div>
      </div>
    );
  }

  function Stat({ l, v, tone, border }) {
    return (
      <div style={{ padding: '18px 22px', borderRight: border ? '1px solid var(--rule)' : 0, borderLeft: border ? '1px solid var(--rule)' : 0 }}>
        <Mono upper size={9} color="var(--ink-3)">{l}</Mono>
        <div className="ff-display" style={{ fontSize: 28, fontWeight: 600, letterSpacing: '-0.02em', marginTop: 4, color: tone || 'var(--ink)' }}>{v}</div>
      </div>
    );
  }

  // ─── 03 BREAKDOWN ────────────────────────────────────────────
  function BreakdownTab() {
    const idx = window.__STMT_INDEX;
    const summary = _M(() => {
      if (!idx?.statements) return null;
      let gross = 0, fee = 0, lines = 0;
      const byRule = {};
      idx.statements.forEach(stmt => {
        (stmt.lines || []).forEach(line => {
          const g = +line.grossUsd || +line.grossAmount || 0;
          if (g <= 0) return;
          const productType = line.productType ||
            (stmt.parser?.includes('mech') ? 'mechanical' :
             stmt.parser?.includes('soundex') ? 'neighbour' : 'performance');
          const r = compute({ grossAmount: g, productType, territory: line.territory, dsp: line.dsp });
          gross += r.gross; fee += r.fee; lines++;
          const k = r.rule?.name || 'unmatched';
          byRule[k] = byRule[k] || { gross: 0, fee: 0, lines: 0 };
          byRule[k].gross += r.gross; byRule[k].fee += r.fee; byRule[k].lines++;
        });
      });
      return { gross, fee, net: gross - fee, lines, byRule };
    }, [idx]);

    if (!summary) {
      return <div style={{ padding: 40, textAlign: 'center', border: '1px solid var(--rule)' }}><Mono color="var(--ink-3)">No statement inbox loaded yet</Mono></div>;
    }

    return (
      <div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', borderTop: '1px solid var(--rule)', borderBottom: '1px solid var(--rule)', marginBottom: 22 }}>
          <Stat l="LINES" v={summary.lines.toLocaleString()}/>
          <Stat l="GROSS" v={'$' + Math.round(summary.gross).toLocaleString()} border/>
          <Stat l="FEE TAKEN" v={'−$' + Math.round(summary.fee).toLocaleString()} tone="#a32a18"/>
          <Stat l="NET" v={'$' + Math.round(summary.net).toLocaleString()} tone="#0a8754" border/>
        </div>

        <Mono upper size={9} color="var(--ink-3)">FEE BY RULE</Mono>
        <div style={{ border: '1px solid var(--rule)', marginTop: 10 }}>
          {Object.entries(summary.byRule).sort((a, b) => b[1].fee - a[1].fee).map(([name, agg], i, arr) => (
            <div key={name} style={{ display: 'grid', gridTemplateColumns: '1fr 100px 130px 130px', gap: 12, padding: '11px 16px', borderBottom: i < arr.length - 1 ? '1px solid var(--rule-soft)' : 0, alignItems: 'center' }}>
              <div style={{ fontSize: 13, fontWeight: 500 }}>{name}</div>
              <Mono size={11} style={{ textAlign: 'right' }}>{agg.lines.toLocaleString()} lines</Mono>
              <Mono size={11} style={{ textAlign: 'right' }}>${agg.gross.toFixed(2)}</Mono>
              <Mono size={12} style={{ textAlign: 'right', color: '#a32a18', fontWeight: 600 }}>−${agg.fee.toFixed(2)}</Mono>
            </div>
          ))}
        </div>
      </div>
    );
  }

  window.ScreenFeeEngine = ScreenFeeEngine;
  console.log('[FeeEngine] loaded');
})();
