// match-audit.jsx — diagnostic view: every quote line, what it matched to,
// the match type, and the confidence, so pairings can be eyeballed for accuracy.
// Reads the same job data as the budget tracker / variance view and runs the
// shared matchLineItems so what you see here is exactly what those screens use.

// How "trustworthy" a match type is — drives color + the "needs review" filter.
const MATCH_TIER = {
  manual:               { tier: 'high',   label: 'Manual' },
  exact_sku:            { tier: 'high',   label: 'SKU' },
  exact_desc:           { tier: 'high',   label: 'Exact desc' },
  cost_code:            { tier: 'review', label: 'Cost code' },
  missing_from_invoice: { tier: 'none',   label: 'Not invoiced' },
  new_charge:           { tier: 'none',   label: 'New charge' },
};

// fuzzy_desc covers three sub-cases, distinguished by score:
//   score === 0   → normalized-exact (abbreviations/packaging differed only)
//   score integer → Levenshtein edit distance
//   score decimal → token-subset (stored as 1 − tokenScore)
function describeMatch(matchType, score) {
  if (matchType !== 'fuzzy_desc') {
    const m = MATCH_TIER[matchType] || { tier: 'review', label: matchType };
    return { ...m, detail: null };
  }
  if (score === 0)              return { tier: 'good',   label: 'Normalized', detail: 'abbrev/packaging' };
  if (Number.isInteger(score))  return { tier: 'review', label: 'Fuzzy',      detail: `edit dist ${score}` };
  return { tier: 'review', label: 'Token', detail: `${Math.round((1 - score) * 100)}% tokens` };
}

const TIER_TONE = { high: 'ok', good: 'info', review: 'warn', none: 'dim' };

function MatchAuditRow({ pair }) {
  const { quote: qi, iis, matchType, matchScore } = pair;
  const m = describeMatch(matchType, matchScore);
  const tone = TIER_TONE[m.tier] || 'dim';

  const qUnit = qi && qi.unit != null ? fmtUSD(qi.unit) : '—';
  const qUom  = qi ? normalizeUOM(qi.uom) : null;

  return (
    <div className={`mar mar-${tone}`}>
      {/* Quote line */}
      <div className="mar-c mar-quote">
        {qi ? (
          <>
            <div className="mar-desc" title={qi.desc}>{qi.desc || <span className="mar-dash">(no description)</span>}</div>
            <div className="mar-sub">
              {qi.sku && !/^LINE-\d+$/i.test(qi.sku) && <span className="mar-sku mono">{qi.sku}</span>}
              <span className="mar-meta num">{(qi.qty ?? 0).toLocaleString()}{qUom ? ' ' + qUom : ''} @ {qUnit}</span>
            </div>
          </>
        ) : <span className="mar-dash">— no quote line —</span>}
      </div>

      {/* Arrow */}
      <div className="mar-c mar-arrow">→</div>

      {/* Matched invoice line(s) */}
      <div className="mar-c mar-inv">
        {iis.length === 0
          ? <span className="mar-dash">— not on any invoice —</span>
          : iis.map((ii, k) => (
              <div key={k} className="mar-inv-line">
                <span className="mar-inv-id mono">{ii._invoiceId}</span>
                <span className="mar-desc" title={ii.desc}>{ii.desc}</span>
                <span className="mar-meta num">{(ii.qty ?? 0).toLocaleString()}{ii.uom ? ' ' + normalizeUOM(ii.uom) : ''} @ {ii.unit != null ? fmtUSD(ii.unit) : '—'}</span>
              </div>
            ))}
      </div>

      {/* Match type + confidence */}
      <div className="mar-c mar-type">
        <span className={`mar-badge mar-badge-${tone}`}>{m.label}</span>
        {m.detail && <span className="mar-detail">{m.detail}</span>}
        {iis.length > 1 && <span className="mar-detail">{iis.length} invoice lines</span>}
      </div>
    </div>
  );
}

function MatchAuditView({ job, userId }) {
  const [quote, setQuote]       = React.useState(null);
  const [invoices, setInvoices] = React.useState([]);
  const [synced, setSynced]     = React.useState(false);
  const [filter, setFilter]     = React.useState('all'); // all | review | matched | unmatched

  const isSample = job.id === '_sample';
  const jobRef = React.useMemo(
    () => !isSample ? fbDb.collection('jobs').doc(job.id) : null,
    [job.id]
  );

  React.useEffect(() => {
    if (isSample) {
      setQuote(INVOICE_A);
      setInvoices([INVOICE_B]);
      setSynced(true);
      return;
    }
    setSynced(false);
    jobRef.get().then((doc) => {
      if (doc.exists) {
        const d = doc.data();
        setQuote(d.quote ?? null);
        setInvoices(d.invoices ?? []);
      }
      setSynced(true);
    }).catch(() => setSynced(true));
  }, [job.id]);

  const pairs = React.useMemo(
    () => quote && invoices.length > 0 ? matchLineItems(quote, invoices) : [],
    [quote, invoices]
  );

  const stats = React.useMemo(() => {
    const s = { matched: 0, review: 0, missing: 0, newCharge: 0 };
    for (const p of pairs) {
      if (p.matchType === 'missing_from_invoice') s.missing++;
      else if (p.matchType === 'new_charge') s.newCharge++;
      else {
        s.matched++;
        if (describeMatch(p.matchType, p.matchScore).tier === 'review') s.review++;
      }
    }
    return s;
  }, [pairs]);

  const isReview = (p) =>
    p.matchType !== 'missing_from_invoice' &&
    p.matchType !== 'new_charge' &&
    describeMatch(p.matchType, p.matchScore).tier === 'review';

  const shown = pairs.filter((p) => {
    if (filter === 'all') return true;
    if (filter === 'review') return isReview(p);
    if (filter === 'matched') return p.matchType !== 'missing_from_invoice' && p.matchType !== 'new_charge';
    if (filter === 'unmatched') return p.matchType === 'missing_from_invoice' || p.matchType === 'new_charge';
    return true;
  });

  if (!synced) {
    return <div className="ma-wrap"><div className="ma-empty">Loading…</div><MatchAuditStyles /></div>;
  }
  if (!quote || invoices.length === 0) {
    return (
      <div className="ma-wrap">
        <div className="ma-empty">
          Add a quote and at least one invoice to audit line matching.
        </div>
        <MatchAuditStyles />
      </div>
    );
  }

  const FILTERS = [
    ['all', `All (${pairs.length})`],
    ['review', `Needs review (${stats.review})`],
    ['matched', `Matched (${stats.matched})`],
    ['unmatched', `Unmatched (${stats.missing + stats.newCharge})`],
  ];

  return (
    <div className="ma-wrap">
      <div className="ma-head">
        <div className="ma-stats">
          <div className="ma-stat"><span className="ma-v num">{stats.matched}</span><span className="ma-l">matched</span></div>
          <div className="ma-stat ma-stat-warn"><span className="ma-v num">{stats.review}</span><span className="ma-l">need review</span></div>
          <div className="ma-stat"><span className="ma-v num">{stats.missing}</span><span className="ma-l">not invoiced</span></div>
          <div className="ma-stat"><span className="ma-v num">{stats.newCharge}</span><span className="ma-l">new charges</span></div>
        </div>
        <div className="ma-note">
          Quote lines matched strongest→weakest: SKU → exact desc → normalized → fuzzy → token → cost code.
          Amber rows used a fuzzy/token match — verify these against the source documents.
        </div>
      </div>

      <div className="ma-filters">
        {FILTERS.map(([key, label]) => (
          <button key={key} className={`ma-filter ${filter === key ? 'is-on' : ''}`} onClick={() => setFilter(key)}>
            {label}
          </button>
        ))}
      </div>

      <div className="ma-table">
        <div className="ma-thead">
          <div className="mar-c">Quote line (RFQ)</div>
          <div className="mar-c mar-arrow"></div>
          <div className="mar-c">Matched invoice line(s)</div>
          <div className="mar-c">Match</div>
        </div>
        {shown.map((p, i) => <MatchAuditRow key={i} pair={p} />)}
        {shown.length === 0 && <div className="ma-empty">No rows for this filter.</div>}
      </div>

      <MatchAuditStyles />
    </div>
  );
}

const MatchAuditStyles = () => (
  <style>{`
    .ma-wrap { max-width: 1400px; margin: 0 auto; padding: 20px 24px 48px; }
    .ma-empty { padding: 40px; text-align: center; color: var(--fg-dim); font-size: 13px; }
    .ma-head { display: flex; flex-direction: column; gap: 10px; margin-bottom: 14px; }
    .ma-stats { display: flex; gap: 10px; flex-wrap: wrap; }
    .ma-stat { background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r); padding: 8px 14px; min-width: 92px; }
    .ma-stat-warn { border-color: oklch(from var(--warn, #c90) l c h / 0.5); }
    .ma-v { display: block; font-size: 20px; font-weight: 650; }
    .ma-l { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--fg-dim); }
    .ma-note { font-size: 11.5px; color: var(--fg-muted); line-height: 1.5; max-width: 820px; }
    .ma-filters { display: flex; gap: 6px; margin-bottom: 12px; flex-wrap: wrap; }
    .ma-filter { appearance: none; border: 1px solid var(--border); background: var(--surface-2); color: var(--fg-muted);
      padding: 5px 12px; border-radius: 999px; font: inherit; font-size: 12px; cursor: pointer; }
    .ma-filter:hover:not(.is-on) { color: var(--fg); }
    .ma-filter.is-on { background: var(--accent); color: #fff; border-color: var(--accent); font-weight: 600; }

    .ma-table { border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; }
    .ma-thead, .mar { display: grid; grid-template-columns: minmax(220px,1fr) 26px minmax(260px,1.3fr) 150px; align-items: start; }
    .ma-thead { background: var(--surface-2); border-bottom: 1px solid var(--border); font-size: 10.5px;
      text-transform: uppercase; letter-spacing: 0.06em; color: var(--fg-dim); font-weight: 600; }
    .ma-thead .mar-c { padding: 8px 12px; }
    .mar { border-bottom: 1px solid var(--border); font-size: 12.5px; }
    .mar:last-child { border-bottom: 0; }
    .mar-ok    { background: oklch(from var(--ok, #2a7) l c h / 0.05); }
    .mar-warn  { background: oklch(from var(--warn, #c90) l c h / 0.07); }
    .mar-dim   { background: var(--surface-1); }
    .mar-c { padding: 9px 12px; min-width: 0; }
    .mar-arrow { color: var(--fg-dim); text-align: center; padding-left: 0; padding-right: 0; }
    .mar-desc { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .mar-sub { display: flex; gap: 8px; align-items: center; margin-top: 2px; }
    .mar-sku { font-size: 10.5px; background: var(--surface-3); padding: 1px 5px; border-radius: 3px; color: var(--fg-muted); }
    .mar-meta { font-size: 11px; color: var(--fg-dim); }
    .mar-inv-line { display: flex; flex-direction: column; gap: 1px; padding: 2px 0; }
    .mar-inv-line + .mar-inv-line { border-top: 1px dashed var(--border); margin-top: 3px; padding-top: 4px; }
    .mar-inv-id { font-size: 10px; color: var(--fg-dim); }
    .mar-badge { display: inline-block; font-size: 10.5px; font-weight: 600; padding: 2px 7px; border-radius: 4px; }
    .mar-badge-ok   { background: oklch(from var(--ok, #2a7) l c h / 0.18);   color: var(--ok, #2a7); }
    .mar-badge-info { background: oklch(from var(--accent) l c h / 0.16);     color: var(--accent); }
    .mar-badge-warn { background: oklch(from var(--warn, #c90) l c h / 0.20); color: var(--warn, #c90); }
    .mar-badge-dim  { background: var(--surface-3); color: var(--fg-dim); }
    .mar-detail { display: block; font-size: 10px; color: var(--fg-dim); margin-top: 3px; }
    .mar-dash { color: var(--fg-dim); font-style: italic; font-size: 11.5px; }
  `}</style>
);

Object.assign(window, { MatchAuditView });
