// ============================================================================
// AGREEMENT-DIFF — version-to-version redline viewer
//
// Renders a side-by-side or unified diff between two agreement versions.
// Diff happens on TWO levels:
//
//   • FIELD diff — top, structured: shows changed scalar fields (term,
//     advance, share, autoRenew, retentionYears, jurisdiction, …) as
//     before/after rows.
//
//   • CLAUSE diff — bottom, prose: word-level Myers-style diff over the
//     reconstructed body of each version (or, when only metadata changes
//     are recorded in ag.versions[], synthesized clause text).
//
// Versions come from ag.versions (the schema already in screens3.jsx mocks)
// — but actual clause bodies aren't stored on those records, so we build a
// synthetic body from the agreement's snapshot fields, mutate it per-version
// using the version `note` field as a hint (e.g. note containing "advance"
// → re-run the advance schedule with a perturbed amount). It's fine for
// design-fidelity; production swaps in real version snapshots.
//
// EXPORT: window.AgDiffPanel — embeddable for agreement-detail
//          window.AgDiff      — { diffWords, diffLines, fieldDiff }
// ============================================================================
(function () {
  const { useState, useMemo } = React;

  // ── Word-level Myers diff (LCS-based, good enough for clause text) ─────
  function tokenize(s) {
    return String(s || '').split(/(\s+|[,.;:!?()"'])/).filter(Boolean);
  }
  function lcs(a, b) {
    const n = a.length, m = b.length;
    const dp = Array.from({ length: n + 1 }, () => new Uint16Array(m + 1));
    for (let i = 1; i <= n; i++) {
      for (let j = 1; j <= m; j++) {
        dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] + 1 : Math.max(dp[i - 1][j], dp[i][j - 1]);
      }
    }
    const ops = [];
    let i = n, j = m;
    while (i > 0 && j > 0) {
      if (a[i - 1] === b[j - 1]) { ops.unshift({ op: 'eq', t: a[i - 1] }); i--; j--; }
      else if (dp[i - 1][j] >= dp[i][j - 1]) { ops.unshift({ op: 'del', t: a[i - 1] }); i--; }
      else { ops.unshift({ op: 'ins', t: b[j - 1] }); j--; }
    }
    while (i > 0) { ops.unshift({ op: 'del', t: a[--i] }); }
    while (j > 0) { ops.unshift({ op: 'ins', t: b[--j] }); }
    return ops;
  }
  function diffWords(beforeText, afterText) {
    const a = tokenize(beforeText), b = tokenize(afterText);
    return lcs(a, b);
  }

  // ── Field diff ─────────────────────────────────────────────────────────
  const TRACKED_FIELDS = [
    ['kind', 'Kind'],
    ['typeCwr', 'CWR Type'],
    ['start', 'Start'],
    ['end', 'End'],
    ['term', 'Term'],
    ['territory', 'Territory'],
    ['share', 'Share'],
    ['status', 'Status'],
    ['value', 'Advance'],
    ['autoRenew', 'Auto-renew'],
    ['renewNoticeMonths', 'Notice (mo)'],
    ['retentionYears', 'Retention (y)'],
    ['retentionEndDate', 'Retention end'],
    ['jurisdiction', 'Jurisdiction'],
    ['disputeResolution', 'Dispute resolution'],
    ['priorRoyaltyStatus', 'Prior royalties'],
    ['sharesCanChange', 'Shares can change'],
    ['advanceGiven', 'Advance given'],
    ['salesOrManufacture', 'S/M'],
  ];

  // Synthesize a snapshot per version. Idea: the "current" version IS the
  // ag fields as recorded; older versions get plausible perturbations based
  // on the version note hint.
  function snapshotForVersion(ag, ver) {
    const base = {};
    TRACKED_FIELDS.forEach(([k]) => { base[k] = ag[k]; });
    if (ver.current) return base;
    const note = String(ver.note || '').toLowerCase();
    const out = { ...base };
    // Heuristics: roll back fields the note implies were changed
    if (note.includes('advance')) {
      // Rough: previous advance was 60% of current — pull a number from value if present
      if (ag.value && /\$/.test(ag.value)) {
        const m = ag.value.match(/\$([\d.]+)\s*([KkMm]?)/);
        if (m) {
          const num = parseFloat(m[1]);
          const mult = m[2].toLowerCase() === 'm' ? 1 : 1; // keep unit
          out.value = `$${(num * 0.6).toFixed(0)}${m[2]}` + (note.includes('60/40') ? ' adv. (50/50)' : ' adv.');
        } else {
          out.value = ag.value + ' (prev)';
        }
      }
    }
    if (note.includes('redline') || note.includes('counter')) {
      // Pretend territory or term tightened
      if (ag.term) out.term = ag.term.replace(/(\d+)y/, (_, n) => `${parseInt(n, 10) + 1}y`);
    }
    if (note.includes('share') || note.includes('split')) {
      if (ag.share) {
        const swapped = ag.share.split('/').reverse().join('/');
        out.share = swapped;
      }
    }
    if (note.includes('renewal') || note.includes('renew')) {
      out.autoRenew = !ag.autoRenew;
    }
    if (note.includes('jurisdict')) {
      out.jurisdiction = (ag.jurisdiction || '').includes('England') ? 'New York' : 'England & Wales';
    }
    if (note.includes('reten')) {
      out.retentionYears = (ag.retentionYears || 0) - 1;
    }
    if (note.includes('draft')) {
      // First draft: assume no advance, no auto-renew
      out.value = '$0';
      out.autoRenew = false;
      out.advanceGiven = false;
    }
    return out;
  }

  function fieldDiff(beforeSnap, afterSnap) {
    const out = [];
    TRACKED_FIELDS.forEach(([k, l]) => {
      const a = beforeSnap[k], b = afterSnap[k];
      if (String(a == null ? '' : a) !== String(b == null ? '' : b)) {
        out.push({ key: k, label: l, before: a, after: b });
      }
    });
    return out;
  }

  // ── Synthesize clause body from ag + version note ──────────────────────
  function synthesizeBody(ag, snap, ver) {
    const lines = [];
    lines.push(`§1 PARTIES. ${ag.a || '[Party A]'} ("Assignor") and ${ag.b || '[Party B]'} ("Assignee") enter this ${snap.kind || 'agreement'}.`);
    lines.push(`§2 TERM. The term of this Agreement begins ${snap.start || '[date]'} and ends ${snap.end || '[date]'} (the "Term"), being a period of ${snap.term || '[term]'}. ${snap.autoRenew ? `This Agreement shall automatically renew unless either party provides written notice of non-renewal at least ${snap.renewNoticeMonths || 0} months prior to the end of the then-current Term.` : 'This Agreement shall not automatically renew.'}`);
    lines.push(`§3 TERRITORY. The Territory shall be ${snap.territory || 'as specified in Schedule A'}.`);
    lines.push(`§4 SHARES. The parties agree to a split of ${snap.share || '50/50'} of all collected revenues, after deduction of any agreed administrative fees. ${snap.sharesCanChange ? 'Shares may be modified by mutual written agreement.' : 'Shares are fixed for the duration of the Term.'}`);
    if (snap.advanceGiven) {
      lines.push(`§5 ADVANCE. Assignee shall pay Assignor a recoupable advance of ${snap.value || '[amount]'} pursuant to the schedule attached as Schedule B. The advance is recoupable solely from royalties earned hereunder and not from any other source.`);
    } else {
      lines.push(`§5 ADVANCE. No advance is payable under this Agreement.`);
    }
    lines.push(`§6 RETENTION. Following the expiration or termination of this Agreement, Assignee shall continue to administer rights for an additional ${snap.retentionYears || 0} years (the "Retention Period"), ending ${snap.retentionEndDate || '[reversion date]'}, with all royalties ${snap.priorRoyaltyStatus === 'A' ? 'retained by Assignee' : 'reverting to Assignor'} during such period.`);
    lines.push(`§7 GOVERNING LAW. This Agreement shall be governed by the laws of ${snap.jurisdiction || '[jurisdiction]'}. ${snap.disputeResolution ? 'Disputes shall be resolved by ' + snap.disputeResolution + '.' : ''}`);
    return lines.join('\n\n');
  }

  // ── Render helpers ─────────────────────────────────────────────────────
  function DiffWords({ ops }) {
    return (
      <div style={{ whiteSpace: 'pre-wrap', fontFamily: 'Georgia, serif', fontSize: 13, lineHeight: 1.7, padding: 14, border: '1px solid var(--rule)', background: 'var(--bg)', maxHeight: 540, overflow: 'auto' }}>
        {ops.map((o, i) => (
          o.op === 'eq' ? <span key={i}>{o.t}</span>
          : o.op === 'del' ? <span key={i} style={{ background: 'oklch(94% 0.06 30)', color: 'var(--danger)', textDecoration: 'line-through' }}>{o.t}</span>
          : <span key={i} style={{ background: 'oklch(94% 0.08 145)', color: '#0a8a3a' }}>{o.t}</span>
        ))}
      </div>
    );
  }

  function SideBySide({ aText, bText }) {
    return (
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        <div>
          <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--danger)', letterSpacing: '.12em', marginBottom: 6 }}>BEFORE</div>
          <pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'Georgia, serif', fontSize: 13, lineHeight: 1.7, padding: 14, border: '1px solid var(--rule)', background: 'oklch(99% 0.005 30)', maxHeight: 540, overflow: 'auto', margin: 0 }}>{aText}</pre>
        </div>
        <div>
          <div className="ff-mono upper" style={{ fontSize: 9, color: '#0a8a3a', letterSpacing: '.12em', marginBottom: 6 }}>AFTER</div>
          <pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'Georgia, serif', fontSize: 13, lineHeight: 1.7, padding: 14, border: '1px solid var(--rule)', background: 'oklch(99% 0.005 145)', maxHeight: 540, overflow: 'auto', margin: 0 }}>{bText}</pre>
        </div>
      </div>
    );
  }

  // ── Embeddable panel ───────────────────────────────────────────────────
  function AgDiffPanel({ ag }) {
    const versions = (ag.versions || []).slice().sort((a, b) => (b.n || 0) - (a.n || 0));
    if (versions.length < 2) {
      return <div style={{ padding: 32, textAlign: 'center', color: 'var(--ink-3)' }}>Need at least two versions to compare. This agreement has {versions.length}.</div>;
    }
    const [bIdx, setBIdx] = useState(1);  // before = older
    const [aIdx, setAIdx] = useState(0);  // after  = newer
    const [view, setView] = useState('redline'); // 'redline' | 'sideBySide' | 'fields'

    const before = versions[bIdx];
    const after = versions[aIdx];

    const beforeSnap = useMemo(() => snapshotForVersion(ag, before), [ag.id, before]);
    const afterSnap = useMemo(() => snapshotForVersion(ag, after), [ag.id, after]);
    const fields = useMemo(() => fieldDiff(beforeSnap, afterSnap), [beforeSnap, afterSnap]);

    const beforeText = useMemo(() => synthesizeBody(ag, beforeSnap, before), [ag.id, beforeSnap]);
    const afterText = useMemo(() => synthesizeBody(ag, afterSnap, after), [ag.id, afterSnap]);
    const ops = useMemo(() => diffWords(beforeText, afterText), [beforeText, afterText]);

    const additions = ops.filter(o => o.op === 'ins').length;
    const deletions = ops.filter(o => o.op === 'del').length;

    return (
      <div>
        {/* Selectors + view toggle */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, marginBottom: 18, padding: 14, border: '1px solid var(--rule)', background: 'var(--bg-2)' }}>
          <div>
            <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-3)', letterSpacing: '.12em', marginBottom: 6 }}>COMPARE FROM</div>
            <select value={bIdx} onChange={e => setBIdx(parseInt(e.target.value, 10))}
              style={{ width: '100%', padding: '6px 8px', fontSize: 12, background: 'var(--bg)', border: '1px solid var(--rule)' }}>
              {versions.map((v, i) => <option key={i} value={i}>v{v.n} · {v.date} · {v.author}{v.current ? ' (current)' : ''}</option>)}
            </select>
            {before.note && <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 6 }}>“{before.note}”</div>}
          </div>
          <div>
            <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-3)', letterSpacing: '.12em', marginBottom: 6 }}>TO</div>
            <select value={aIdx} onChange={e => setAIdx(parseInt(e.target.value, 10))}
              style={{ width: '100%', padding: '6px 8px', fontSize: 12, background: 'var(--bg)', border: '1px solid var(--rule)' }}>
              {versions.map((v, i) => <option key={i} value={i}>v{v.n} · {v.date} · {v.author}{v.current ? ' (current)' : ''}</option>)}
            </select>
            {after.note && <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 6 }}>“{after.note}”</div>}
          </div>
          <div>
            <div className="ff-mono upper" style={{ fontSize: 9, color: 'var(--ink-3)', letterSpacing: '.12em', marginBottom: 6 }}>VIEW</div>
            <div style={{ display: 'flex', gap: 0, border: '1px solid var(--rule)' }}>
              {['redline', 'sideBySide', 'fields'].map(v => (
                <button key={v} onClick={() => setView(v)} className="ff-mono upper"
                  style={{ flex: 1, padding: '6px 8px', fontSize: 9, letterSpacing: '.1em', background: view === v ? 'var(--ink)' : 'transparent', color: view === v ? 'var(--bg)' : 'var(--ink)', border: 0, cursor: 'pointer' }}>
                  {v === 'redline' ? 'REDLINE' : v === 'sideBySide' ? 'SIDE-BY-SIDE' : 'FIELDS'}
                </button>
              ))}
            </div>
            <div className="ff-mono" style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 8 }}>
              <span style={{ color: '#0a8a3a' }}>+{additions}</span> · <span style={{ color: 'var(--danger)' }}>−{deletions}</span> · <span>{fields.length} field{fields.length === 1 ? '' : 's'} changed</span>
            </div>
          </div>
        </div>

        {/* Field diff (always visible) */}
        {fields.length > 0 && (
          <div style={{ marginBottom: 24 }}>
            <div className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '.12em', marginBottom: 8 }}>STRUCTURED FIELDS · {fields.length}</div>
            <table style={{ width: '100%', borderCollapse: 'collapse', border: '1px solid var(--rule)' }}>
              <thead style={{ background: 'var(--bg-2)' }}>
                <tr>
                  {['FIELD', 'BEFORE', 'AFTER'].map(h => (
                    <th key={h} className="ff-mono upper" style={{ padding: '8px 12px', textAlign: 'left', fontSize: 9, color: 'var(--ink-3)', letterSpacing: '.12em', borderBottom: '1px solid var(--rule)' }}>{h}</th>
                  ))}
                </tr>
              </thead>
              <tbody>
                {fields.map(f => (
                  <tr key={f.key} style={{ borderBottom: '1px solid var(--rule-soft)' }}>
                    <td style={{ padding: '8px 12px', fontSize: 12, fontWeight: 500 }}>{f.label}</td>
                    <td style={{ padding: '8px 12px', fontFamily: 'IBM Plex Mono, monospace', fontSize: 11, background: 'oklch(99% 0.005 30)', color: 'var(--danger)', textDecoration: 'line-through' }}>{String(f.before == null ? '—' : f.before)}</td>
                    <td style={{ padding: '8px 12px', fontFamily: 'IBM Plex Mono, monospace', fontSize: 11, background: 'oklch(99% 0.005 145)', color: '#0a8a3a' }}>{String(f.after == null ? '—' : f.after)}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}

        {view === 'redline' && (
          <div>
            <div className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '.12em', marginBottom: 8 }}>CLAUSE REDLINE</div>
            <DiffWords ops={ops} />
          </div>
        )}
        {view === 'sideBySide' && (
          <div>
            <div className="ff-mono upper" style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '.12em', marginBottom: 8 }}>SIDE-BY-SIDE</div>
            <SideBySide aText={beforeText} bText={afterText} />
          </div>
        )}
      </div>
    );
  }

  window.AgDiffPanel = AgDiffPanel;
  window.AgDiff = { diffWords, fieldDiff, snapshotForVersion, synthesizeBody };
})();
