/* screens-enrichment.jsx - provider-agnostic data enrichment (simulated).
   The model is TWO layers: a BASE data provider (person/company/firmographics/hiring signals off a bulk
   dataset) feeding a CONTACT WATERFALL (email/phone, tried in order, first hit wins), plus a region-specific
   COMPLIANCE path that stays off until its lawful basis is verified. The UI names the LAYER, not the vendor -
   the seeded lineup in D.enrichProviders is a configurable default, not a commitment.

   Enriching candidate PII is a first-order compliance problem, so a run checks consent/suppression per
   candidate (window.candidateEnrichConsent) and SKIPS anyone suppressed or opted-out, saying so out loud.

   R108 T3 (real write path + provenance precedence): confirming a suggestion (hub review queue, per-panel
   suggested updates, bulk) now dispatches the SHARED store.js apply-field executor via window.enrichApplyField
   - the value lands on the record row through E8Store.set, persists across reload, and is undoable (a toast
   with Undo; the bulk confirm composes ONE undo). A candidate confirm also stamps candidateMeta.fieldProv
   (source = provider) WITHOUT touching R106 freshness. Precedence (window.enrichPrecedence): enrichment fills
   blanks / re-enriches its own guesses freely, but a candidate-verified or recruiter-entered field is HELD as
   a conflict needing an explicit "Override with vendor value" vs "Keep current". useEnrichActed now only
   tracks which rows have left the queue (confirmed/dismissed dedupe) - the value write is a real store op. */
const DSenr = window.Stand8DesignSystem_b5c975;

/* Layer presentation: label, one-line role, an icon + a semantic tone. Data lives in D.enrichProviders. */
const ENR_LAYER_META = {
  base:       { n: 'Layer 1', label: 'Base data provider', tone: 'info',    icon: 'dataset',
                desc: 'Resolves the person, company, firmographics and hiring signals off a bulk dataset.' },
  waterfall:  { n: 'Layer 2', label: 'Contact waterfall',  tone: 'accent',  icon: 'call_split',
                desc: 'Finds email and phone - tried in order, first hit wins across sub-providers.' },
  compliance: { n: '',        label: 'Compliance / EMEA path', tone: 'warning', icon: 'gavel',
                desc: 'A region-specific path, off by default until its lawful basis is verified.' },
};

/* The demo cohort for the candidate run - two allowed, two blocked (one opted-out, one suppressed) so the
   consent gate is visible. Names are display labels; consent is resolved live from candidateMeta. */
const ENR_CAND_COHORT = [
  { id: 'c-okafor', name: 'Daniel Okafor', role: 'Sr. Software Engineer' },
  { id: 'c-patel',  name: 'Aisha Patel',   role: 'Backend Engineer' },
  { id: 'c-silva',  name: 'Beatriz Silva', role: 'Data Engineer' },
  { id: 'c-marsh',  name: 'Felix Marsh',   role: 'Business Analyst' },
];

/* Which suggestions have left the queue - persisted so a confirmed/dismissed row does not reappear on
   reload. The VALUE write is now a real store op (window.enrichApplyField); this tracks only queue dedupe:
   markConfirmed/unmarkConfirmed (paired with the apply/undo) and dismiss. */
function enrichReadActed() {
  try { return JSON.parse(localStorage.getItem('e8-enrich-acted-v1')) || { confirmed: [], dismissed: [] }; }
  catch (e) { return { confirmed: [], dismissed: [] }; }
}
function useEnrichActed() {
  const [acted, setActed] = React.useState(enrichReadActed);
  const write = (next) => { try { localStorage.setItem('e8-enrich-acted-v1', JSON.stringify(next)); } catch (e) {} return next; };
  const markConfirmed = (ids) => setActed((prev) => {
    const set = prev.confirmed.slice();
    ids.forEach((id) => { if (set.indexOf(id) < 0) set.push(id); });
    return write({ confirmed: set, dismissed: prev.dismissed });
  });
  const unmarkConfirmed = (ids) => setActed((prev) => write({ confirmed: prev.confirmed.filter((id) => ids.indexOf(id) < 0), dismissed: prev.dismissed }));
  const dismiss = (id) => setActed((prev) => (prev.dismissed.indexOf(id) >= 0 ? prev : write({ confirmed: prev.confirmed, dismissed: prev.dismissed.concat([id]) })));
  return { acted, markConfirmed, unmarkConfirmed, dismiss };
}

/* Relative "verified 3 days ago" for the conflict provenance line (screens-core's e8AgoLabel is module-
   local, so a small local one keeps this file self-contained). */
function enrAgo(ts) {
  if (ts == null) return 'earlier';
  const d = Math.max(0, Math.round((Date.now() - ts) / 864e5));
  if (d <= 0) return 'today';
  if (d === 1) return 'yesterday';
  if (d < 30) return d + ' days ago';
  const mo = Math.round(d / 30);
  return mo + (mo === 1 ? ' month ago' : ' months ago');
}

/* One suggestion object -> the apply-field target (candidate rows carry candId so the write also stamps
   fieldProv). Queue rows use newVal; the client dossier's suggested entries use value. */
function enrichTarget(s) {
  return { coll: s.coll, targetId: s.targetId, field: s.fieldKey, value: (s.newVal != null ? s.newVal : s.value), provider: s.provider, candId: s.candId };
}
/* A candidate suggestion whose field is candidate-verified or recruiter-entered is HELD - never applied on
   a plain confirm; it needs an explicit override. Blank / enrichment-owned fields (and every non-candidate
   row) apply freely. */
function enrichIsHeld(s) {
  if (!s.candId || !window.enrichPrecedence) return false;
  const p = window.enrichPrecedence(s.candId, s.fieldKey);
  return p === 'candidate' || p === 'human';
}
/* A candidate-targeted suggestion whose candidate is suppressed or opted-out must NEVER receive vendor
   data - the record-level consent gate (window.candidateEnrichConsent, T2) applies to the review queue and
   the confirm path too, not just the hub bulk RUN. Non-candidate rows (client / contact firmographic) are
   unaffected. */
function enrichConsentBlocked(s) {
  if (!s.candId || !window.candidateEnrichConsent) return false;
  return !window.candidateEnrichConsent(s.candId).allowed;
}
/* Confirm one suggestion for real: dispatch apply-field (persist + undo), hide the row, raise an Undo toast
   that composes the value revert with un-hiding the row. ctx = { markConfirmed, unmarkConfirmed, showToast }.
   A suppressed / opted-out candidate row is blocked before any write. */
function enrichConfirmOne(s, label, ctx) {
  if (enrichConsentBlocked(s)) return;
  const res = window.enrichApplyField(enrichTarget(s));
  ctx.markConfirmed([s.id]);
  if (ctx.showToast) ctx.showToast(label, 'Undo', () => { res.undo(); ctx.unmarkConfirmed([s.id]); });
}
/* Bulk confirm: apply each, hide all, compose ONE Undo that reverts every write and un-hides every row.
   Suppressed / opted-out candidate rows are dropped from the set so no vendor value lands on them. */
function enrichConfirmMany(list, ctx) {
  const eligible = list.filter((s) => !enrichConsentBlocked(s));
  if (!eligible.length) return;
  const undos = eligible.map((s) => window.enrichApplyField(enrichTarget(s)));
  const ids = eligible.map((s) => s.id);
  ctx.markConfirmed(ids);
  if (ctx.showToast) ctx.showToast(eligible.length + ' updates applied to records', 'Undo', () => { undos.forEach((u) => u.undo()); ctx.unmarkConfirmed(ids); });
}

/* A small provider-agnostic mark for the panel headers (replaces the old vendor letter-mark). */
function EnrichMark() {
  return <span className="e8-enr-mark"><span className="material-symbols-outlined" style={{ fontSize: 14 }}>auto_awesome</span></span>;
}

/* The contact-waterfall chain: providers tried in order, stops at the first hit. */
function EnrichWaterfall({ label, waterfall, value }) {
  return (
    <div className="e8-enr-wf">
      {label ? <span className="e8-enr-wf-label">{label}</span> : null}
      {waterfall.map((s, i) => (
        <React.Fragment key={i}>
          {i > 0 ? <span className="e8-enr-wf-arrow">{'->'}</span> : null}
          <span className={'e8-enr-wf-step' + (s.hit ? ' hit' : ' miss')}>{s.provider}{s.hit ? ' ' + (value || 'found') : ''}</span>
        </React.Fragment>
      ))}
    </div>
  );
}

function EnrichProvChip({ provider }) { return <span className="e8-enr-prov">via {provider}</span>; }

function EnrichFieldRow({ f }) {
  return (
    <div className="e8-enr-field">
      <span className="e8-enr-field-k">{f.label}</span>
      <span className="e8-enr-field-v">{f.value}</span>
      <EnrichProvChip provider={f.provider} />
      <DSenr.ConfidenceChip state={f.confidence}>{f.confidence}</DSenr.ConfidenceChip>
    </div>
  );
}

/* Compact per-record readout: the enabled providers backing this record + an estimated cost + the honest,
   always-visible vendor-reported caveat. `layers` picks which layers apply (a client dossier is base-only;
   a contact/candidate uses base + the contact waterfall). Reads the global enabled set (window.enrichLayer). */
function EnrichCostReadout({ layers }) {
  const names = (layer) => window.enrichLayer(layer).map((p) => p.name);
  const cost = (layer) => window.enrichLayer(layer).reduce((a, p) => a + (p.costPerRecord || 0), 0);
  const base = names('base');
  const wf = layers.indexOf('waterfall') >= 0 ? names('waterfall') : [];
  // First-hit-wins: a typical record pays the base scan + the first waterfall step, not every step.
  const wfFirst = window.enrichLayer('waterfall')[0];
  const est = cost('base') + (layers.indexOf('waterfall') >= 0 && wfFirst ? (wfFirst.costPerRecord || 0) : 0);
  return (
    <div className="e8-enr-readout">
      <div className="e8-enr-readout-row">
        <span className="e8-enr-readout-k">Base data</span>
        <span className="e8-enr-readout-v">{base.join(', ') || 'none enabled'}</span>
      </div>
      {layers.indexOf('waterfall') >= 0 ? (
        <div className="e8-enr-readout-row">
          <span className="e8-enr-readout-k">Contact waterfall</span>
          <span className="e8-enr-readout-v">{wf.join(' -> ') || 'none enabled'}</span>
        </div>
      ) : null}
      <div className="e8-enr-readout-row">
        <span className="e8-enr-readout-k">Est. cost</span>
        <span className="e8-enr-readout-v">~{est} credits/record</span>
      </div>
      <div className="e8-enr-caveat">Coverage and cost figures are vendor-reported, not independently audited.</div>
    </div>
  );
}

/* ---- A: company / account intelligence on the client dossier (base layer) ---- */
function EnrichDossierCard({ clientId }) {
  const D = window.E8DATA;
  const data = (D.enrichClients || {})[clientId];
  const { showToast } = React.useContext(E8Ctx);
  const { acted, markConfirmed, unmarkConfirmed, dismiss } = useEnrichActed();
  const ctx = { markConfirmed, unmarkConfirmed, showToast };
  const [running, setRunning] = React.useState(false);
  if (!data) {
    return (
      <div className="e8-card" style={{ padding: '14px 16px' }}>
        <div className="e8-enr-hd"><EnrichMark /><span className="e8-enr-title">Enrichment</span></div>
        <DSenr.EmptyState icon="auto_awesome" title="Not enriched yet" body="Run enrichment to pull firmographics, tech stack and buying-intent signals for this account." />
      </div>
    );
  }
  const enrich = () => { setRunning(true); setTimeout(() => { setRunning(false); if (showToast) showToast('Enrichment run - ' + data.firmographics.length + ' fields refreshed'); }, 1100); };
  const intentTone = (t) => t === 'surging' ? 'var(--ui-success-text)' : t === 'rising' ? 'var(--ui-warning-text)' : 'var(--ui-text-tertiary)';
  const suggested = (data.suggested || []).filter((s) => acted.confirmed.indexOf(s.id) < 0 && acted.dismissed.indexOf(s.id) < 0);
  return (
    <div className="e8-card" style={{ padding: 0, overflow: 'hidden' }}>
      <div className="e8-enr-hd">
        <EnrichMark />
        <span className="e8-enr-title">Enrichment</span>
        <DSenr.ProvenanceBadge kind="advised" />
        <span style={{ marginLeft: 'auto', fontSize: 'var(--ui-text-xs)', color: 'var(--ui-text-tertiary)' }}>Last run {data.lastRun} · {(D.enrichCredits.total - D.enrichCredits.used).toLocaleString()} credits left</span>
        <DSenr.Button variant="primary" size="sm" icon="auto_awesome" onClick={enrich}>{running ? 'Enriching…' : 'Run enrichment'}</DSenr.Button>
      </div>
      <div style={{ padding: '14px 16px' }}>
        <EnrichCostReadout layers={['base']} />
        <div className="e8-sectionlabel" style={{ margin: '14px 0 10px' }}>Firmographics <span style={{ fontWeight: 400, textTransform: 'none', color: 'var(--ui-text-tertiary)' }}>· base data provider</span></div>
        <div className="e8-enr-grid">{data.firmographics.map((f) => <EnrichFieldRow key={f.key} f={f} />)}</div>

        <div className="e8-sectionlabel" style={{ margin: '16px 0 8px' }}>Tech stack</div>
        <div><DSenr.SkillChips skills={data.techStack} max={data.techStack.length} style={{ flexWrap: 'wrap' }} /></div>

        <div className="e8-sectionlabel" style={{ margin: '16px 0 8px' }}>Buying-intent signals</div>
        {data.intent.map((s) => (
          <div key={s.topic} className="e8-enr-intent">
            <span style={{ flex: 1 }}>{s.topic}</span>
            <span className="e8-enr-bar"><span style={{ width: s.score + '%' }} /></span>
            <span style={{ color: intentTone(s.trend), fontWeight: 600, fontSize: 'var(--ui-text-xs)', width: 56, textAlign: 'right' }}>{s.trend}</span>
          </div>
        ))}

        {suggested.length ? (
          <React.Fragment>
            <div className="e8-sectionlabel" style={{ margin: '16px 0 8px' }}>Suggested updates · confirm to save</div>
            {suggested.map((s) => (
              <div key={s.id} className="e8-enr-sug">
                <span style={{ flex: 1 }}>{s.field}: <b>{s.value}</b> · via {s.provider} · {s.confidence}% confidence</span>
                <DSenr.Button variant="ghost" size="sm" onClick={() => dismiss(s.id)}>Dismiss</DSenr.Button>
                <DSenr.Button variant="primary" size="sm" onClick={() => enrichConfirmOne(s, s.field + ' saved to the record', ctx)}>Confirm</DSenr.Button>
              </div>
            ))}
          </React.Fragment>
        ) : null}
      </div>
    </div>
  );
}

/* ---- B: contact / buyer enrichment on the contact record (base + contact waterfall) ---- */
function EnrichContactPanel({ contactId }) {
  const D = window.E8DATA;
  const c = (D.enrichContacts || {})[contactId];
  const { showToast } = React.useContext(E8Ctx);
  const [running, setRunning] = React.useState(false);
  if (!c) {
    return (
      <div className="e8-card" style={{ padding: '14px 16px' }}>
        <div className="e8-enr-hd"><EnrichMark /><span className="e8-enr-title">Enrichment</span></div>
        <DSenr.EmptyState icon="auto_awesome" title="Not enriched yet" body="Run enrichment to find a work email, a direct dial, seniority and buying role." />
      </div>
    );
  }
  const enrich = () => { setRunning(true); setTimeout(() => { setRunning(false); if (showToast) showToast('Found a direct dial + work email'); }, 1100); };
  return (
    <div className="e8-card" style={{ padding: '14px 16px' }}>
      <div className="e8-enr-hd">
        <EnrichMark /><span className="e8-enr-title">Enrichment</span>
        <DSenr.ProvenanceBadge kind="advised" />
        <DSenr.Button variant="secondary" size="sm" icon="auto_awesome" onClick={enrich} style={{ marginLeft: 'auto' }}>{running ? 'Enriching…' : 'Enrich'}</DSenr.Button>
      </div>
      <div style={{ marginTop: 10 }}>
        <EnrichCostReadout layers={['base', 'waterfall']} />
        <div style={{ marginTop: 10 }}>
          <EnrichFieldRow f={{ key: 'em', label: 'Work email', value: c.workEmail.value, provider: c.workEmail.provider, confidence: c.workEmail.confidence }} />
          <div className="e8-enr-field"><span className="e8-enr-field-k">Direct dial</span><span className="e8-enr-field-v">{c.directDial.value}</span><EnrichProvChip provider={c.directDial.provider} /><DSenr.ConfidenceChip state={c.directDial.confidence}>{c.directDial.confidence}</DSenr.ConfidenceChip></div>
          <div style={{ margin: '8px 0' }}><EnrichWaterfall label="How we found the dial" waterfall={c.directDial.waterfall} value={c.directDial.value} /></div>
          <div className="e8-enr-field"><span className="e8-enr-field-k">Seniority</span><span className="e8-enr-field-v">{c.seniority}</span></div>
          <div className="e8-enr-field"><span className="e8-enr-field-k">Buying role</span><span className="e8-enr-field-v">{c.buyingRole}</span></div>
        </div>
      </div>
    </div>
  );
}

/* ---- B2: candidate enrichment on the candidate record (base + contact waterfall, consent-gated) ---- */
function EnrichCandidatePanel({ candidateId }) {
  const D = window.E8DATA;
  const c = (D.enrichCandidates || {})[candidateId];
  const { showToast } = React.useContext(E8Ctx);
  const [running, setRunning] = React.useState(false);
  const consent = (window.candidateEnrichConsent ? window.candidateEnrichConsent(candidateId) : { allowed: true, state: 'allowed', reason: '' });
  // Consent gate at the record level: a suppressed / opted-out candidate is never enriched.
  if (!consent.allowed) {
    return (
      <div className="e8-card" style={{ padding: '14px 16px' }}>
        <div className="e8-enr-hd"><EnrichMark /><span className="e8-enr-title">Enrichment</span>
          <DSenr.Badge tone={consent.state === 'suppressed' ? 'danger' : 'warning'} style={{ marginLeft: 'auto' }}>{consent.state === 'suppressed' ? 'Suppressed' : 'Opted out'}</DSenr.Badge>
        </div>
        <div className="e8-enr-blocked">
          <span className="material-symbols-outlined" style={{ fontSize: 20, color: 'var(--ui-text-tertiary)' }}>block</span>
          <div>
            <div style={{ fontWeight: 600, fontSize: 'var(--ui-text-base)' }}>Enrichment skipped</div>
            <div style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-secondary)', marginTop: 2 }}>{consent.reason}. No provider is called for this candidate.</div>
          </div>
        </div>
      </div>
    );
  }
  if (!c) {
    return (
      <div className="e8-card" style={{ padding: '14px 16px' }}>
        <div className="e8-enr-hd"><EnrichMark /><span className="e8-enr-title">Enrichment</span></div>
        <DSenr.EmptyState icon="auto_awesome" title="Not enriched yet" body="Run enrichment to find a personal email, mobile, current role and inferred skills." />
      </div>
    );
  }
  const enrich = () => { setRunning(true); setTimeout(() => { setRunning(false); if (showToast) showToast('Found a personal email + mobile'); }, 1100); };
  return (
    <div className="e8-card" style={{ padding: '14px 16px' }}>
      <div className="e8-enr-hd">
        <EnrichMark /><span className="e8-enr-title">Enrichment</span>
        <DSenr.ProvenanceBadge kind="advised" />
        <DSenr.Button variant="secondary" size="sm" icon="auto_awesome" onClick={enrich} style={{ marginLeft: 'auto' }}>{running ? 'Enriching…' : 'Enrich'}</DSenr.Button>
      </div>
      <div style={{ marginTop: 10 }}>
        <EnrichCostReadout layers={['base', 'waterfall']} />
        <div style={{ marginTop: 10 }}>
          <EnrichFieldRow f={{ key: 'em', label: 'Personal email', value: c.personalEmail.value, provider: c.personalEmail.provider, confidence: c.personalEmail.confidence }} />
          <div className="e8-enr-field"><span className="e8-enr-field-k">Mobile</span><span className="e8-enr-field-v">{c.mobile.value}</span><EnrichProvChip provider={c.mobile.provider} /><DSenr.ConfidenceChip state={c.mobile.confidence}>{c.mobile.confidence}</DSenr.ConfidenceChip></div>
          <div style={{ margin: '8px 0' }}><EnrichWaterfall label="How we found the mobile" waterfall={c.mobile.waterfall} value={c.mobile.value} /></div>
          <div className="e8-enr-field"><span className="e8-enr-field-k">Current role</span><span className="e8-enr-field-v">{c.currentRole}</span></div>
          <div className="e8-enr-field" style={{ borderBottom: 0 }}><span className="e8-enr-field-k">Inferred skills</span><DSenr.SkillChips skills={c.skills} max={c.skills.length} style={{ flexWrap: 'wrap' }} /></div>
        </div>
      </div>
    </div>
  );
}

/* ---- Hub: the two-layer provider stack + enable/disable config (hub-local state; T3 seam to a store op) ---- */
function EnrichProviderStack({ providers, onToggle }) {
  return (
    <div>
      <div className="e8-enr-caveat" style={{ padding: '0 0 10px' }}>Layers name the job, not the vendor. Toggle a provider to preview the configured stack. Coverage figures are vendor-reported.</div>
      {['base', 'waterfall', 'compliance'].map((layer) => {
        const meta = ENR_LAYER_META[layer];
        const rows = providers.filter((p) => p.layer === layer);
        const on = rows.filter((p) => p.enabled).length;
        let wfOrder = 0;
        return (
          <div key={layer} className="e8-enr-layer">
            <div className="e8-enr-layer-hd">
              <span className="material-symbols-outlined e8-enr-layer-ico">{meta.icon}</span>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div className="e8-enr-layer-t">{meta.n ? meta.n + ' - ' : ''}{meta.label}</div>
                <div className="e8-enr-layer-d">{meta.desc}</div>
              </div>
              <DSenr.Badge tone={meta.tone}>{on} of {rows.length} on</DSenr.Badge>
            </div>
            {rows.map((p) => {
              if (layer === 'waterfall' && p.enabled) wfOrder += 1;
              return (
                <div key={p.id} className={'e8-enr-prov-row' + (p.enabled ? '' : ' off')}>
                  <div className="e8-enr-prov-main">
                    <div className="e8-enr-prov-name">
                      {layer === 'waterfall' && p.enabled ? <span className="e8-enr-prov-ord">{wfOrder}</span> : null}
                      {p.name}
                      {p.region && p.region !== 'Global' ? <span className="e8-enr-prov-region">{p.region}</span> : null}
                    </div>
                    <div className="e8-enr-prov-role">{p.role}</div>
                    <div className="e8-enr-caveat">{p.coverage ? p.coverage + ' · ' : ''}{p.coverageNote}</div>
                  </div>
                  <div className="e8-enr-prov-side">
                    <span className="e8-enr-prov-cost">{p.costPerRecord} credits/rec</span>
                    <DSenr.Toggle checked={p.enabled} onChange={() => onToggle(p.id)} aria-label={(p.enabled ? 'Disable ' : 'Enable ') + p.name} />
                  </div>
                </div>
              );
            })}
          </div>
        );
      })}
    </div>
  );
}

/* ---- Hub: compliance posture (honest + factual, no scare copy) ---- */
function EnrichCompliancePosture({ providers }) {
  const D = window.E8DATA;
  const meta = D.candidateMeta || {};
  let suppressed = 0, optedOut = 0;
  Object.keys(meta).forEach((id) => { const m = meta[id] || {}; if (m.suppressed) suppressed += 1; else if (m.consent === false) optedOut += 1; });
  const posture = [
    { k: 'Opt-out honored', icon: 'verified_user', v: 'Suppressed and opted-out candidates are skipped before any provider is called - never silently dropped.' },
    { k: 'Suppression / DSAR', icon: 'block', v: (suppressed + optedOut) + ' on the suppression list (' + suppressed + ' suppressed, ' + optedOut + ' opted out). An erasure request holds the record from processing.' },
    { k: 'Coverage figures', icon: 'info', v: 'Vendor-reported, not independently audited. At 1M scale all move to negotiated enterprise contracts.' },
  ];
  return (
    <DSenr.Card title="Compliance posture" flush>
      <div style={{ padding: '6px 0' }}>
        {posture.map((r) => (
          <div key={r.k} className="e8-enr-posture-row">
            <span className="material-symbols-outlined e8-enr-posture-ico">{r.icon}</span>
            <span className="e8-enr-posture-k">{r.k}</span>
            <span className="e8-enr-posture-v">{r.v}</span>
          </div>
        ))}
        <div className="e8-sectionlabel" style={{ margin: '10px 16px 6px' }}>Lawful basis per provider</div>
        {providers.map((p) => {
          const refuted = String(p.lawfulBasis || '').indexOf('Refuted') >= 0;
          return (
            <div key={p.id} className="e8-enr-posture-row">
              <span className="e8-enr-posture-k">{p.name}</span>
              <span className="e8-enr-posture-v">{p.lawfulBasis}</span>
              <DSenr.Badge tone={refuted ? 'danger' : 'warning'} style={{ flex: '0 0 auto' }}>{refuted ? 'Refuted' : 'Unverified'}</DSenr.Badge>
            </div>
          );
        })}
      </div>
    </DSenr.Card>
  );
}

/* ---- Hub: candidate contact enrichment run - the consent gate, made visible ---- */
function EnrichCandidateRun() {
  const { showToast } = React.useContext(E8Ctx);
  const [running, setRunning] = React.useState(false);
  const [result, setResult] = React.useState(null);
  const cohort = ENR_CAND_COHORT;
  const run = () => {
    setRunning(true);
    setTimeout(() => {
      const enriched = [], skipped = [];
      cohort.forEach((c) => {
        const g = window.candidateEnrichConsent ? window.candidateEnrichConsent(c.id) : { allowed: true };
        if (g.allowed) enriched.push(c); else skipped.push({ c: c, g: g });
      });
      setResult({ enriched: enriched, skipped: skipped });
      setRunning(false);
      if (showToast) showToast(enriched.length + ' enriched · ' + skipped.length + ' skipped');
    }, 900);
  };
  return (
    <div className="e8-card" style={{ padding: '14px 16px' }}>
      <div className="e8-sectionlabel" style={{ marginBottom: 8 }}>Candidate contact enrichment</div>
      <div style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-secondary)', marginBottom: 10 }}>{cohort.length} candidates in this cohort · consent is checked before any provider is called.</div>
      <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
        <DSenr.Button variant="primary" size="sm" icon="auto_awesome" onClick={run}>{running ? 'Running…' : 'Run enrichment'}</DSenr.Button>
        {result ? <span style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-tertiary)' }}>{result.enriched.length} enriched</span> : null}
      </div>
      {result ? (
        <div style={{ marginTop: 12 }}>
          {result.skipped.length ? (
            <React.Fragment>
              <div className="e8-enr-skiphd">{result.skipped.length} skipped - opted out or suppressed</div>
              {result.skipped.map((s) => (
                <div key={s.c.id} className="e8-enr-skip">
                  <span className="material-symbols-outlined" style={{ fontSize: 15, color: 'var(--ui-text-tertiary)' }}>block</span>
                  <span style={{ fontWeight: 600, fontSize: 'var(--ui-text-sm)' }}>{s.c.name}</span>
                  <span style={{ flex: 1, fontSize: 'var(--ui-text-xs)', color: 'var(--ui-text-secondary)' }}>{s.g.reason}</span>
                  <DSenr.Badge tone={s.g.state === 'suppressed' ? 'danger' : 'warning'}>{s.g.state === 'suppressed' ? 'Suppressed' : 'Opted out'}</DSenr.Badge>
                </div>
              ))}
            </React.Fragment>
          ) : <div style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-success-text)' }}>No candidates skipped - all had a lawful basis on record.</div>}
        </div>
      ) : null}
    </div>
  );
}

/* ---- One review-queue row. A candidate row is classified live by provenance precedence: a candidate-
   verified or recruiter-entered field is HELD (both values + provenance shown, explicit override vs keep);
   a blank / enrichment-owned field (and every non-candidate row) confirms straight to the record. ---- */
function EnrichQueueItem({ q, ctx, onDismiss }) {
  const D = window.E8DATA;
  const isCand = !!q.candId;
  // Consent gate on the review queue: a candidate-targeted row whose candidate is suppressed / opted-out is
  // blocked here exactly as the per-record panel is - no Confirm, no vendor value written. Reuses the T2
  // window.candidateEnrichConsent helper + the per-record panel's .e8-enr-blocked styling.
  const consent = isCand && window.candidateEnrichConsent ? window.candidateEnrichConsent(q.candId) : { allowed: true, state: 'allowed', reason: '' };
  if (isCand && !consent.allowed) {
    const suppressed = consent.state === 'suppressed';
    return (
      <div className="e8-enr-qrow">
        <span className="e8-enr-qrec">{q.record}</span>
        <div className="e8-enr-blocked" style={{ flex: 1, marginTop: 0 }}>
          <span className="material-symbols-outlined" style={{ fontSize: 20, color: 'var(--ui-text-tertiary)' }}>block</span>
          <div>
            <div style={{ fontWeight: 600, fontSize: 'var(--ui-text-base)' }}>{q.field} not enriched · {suppressed ? 'suppressed' : 'opted out'}</div>
            <div style={{ fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-secondary)', marginTop: 2 }}>{consent.reason}. No provider value is written to this candidate.</div>
          </div>
        </div>
        <DSenr.Badge tone={suppressed ? 'danger' : 'warning'} style={{ flex: '0 0 auto' }}>{suppressed ? 'Suppressed' : 'Opted out'}</DSenr.Badge>
      </div>
    );
  }
  const prof = isCand ? (D.matches || []).find((m) => m.id === q.candId) : null;
  const curVal = isCand ? (prof ? prof[q.fieldKey] : q.oldVal) : q.oldVal;
  const held = enrichIsHeld(q);
  if (held) {
    const prec = window.enrichPrecedence(q.candId, q.fieldKey);
    const prov = (((D.candidateMeta || {})[q.candId] || {}).fieldProv || {})[q.fieldKey] || {};
    const isCandOwned = prec === 'candidate';
    const who = isCandOwned ? 'The candidate verified this' : 'A recruiter entered this';
    return (
      <div className="e8-enr-qrow e8-enr-conflict">
        <div className="e8-enr-conflict-hd">
          <span className="material-symbols-outlined e8-enr-conflict-ico">shield_person</span>
          <span className="e8-enr-qrec">{q.record}</span>
          <span className="e8-enr-conflict-field">{q.field}</span>
          <DSenr.Badge tone={isCandOwned ? 'success' : 'info'} style={{ marginLeft: 'auto' }}>{isCandOwned ? 'Candidate verified' : 'Recruiter entered'}</DSenr.Badge>
        </div>
        <div className="e8-enr-conflict-note">{who} {enrAgo(prov.verifiedAt)} - {q.provider} disagrees. Held for an explicit override.</div>
        <div className="e8-enr-conflict-vals">
          <div className="e8-enr-conflict-val">
            <span className="e8-enr-conflict-k">Current{isCandOwned ? ' · candidate' : ' · recruiter'}</span>
            <b>{String(curVal == null || curVal === '' ? '-' : curVal)}</b>
          </div>
          <span className="material-symbols-outlined e8-enr-conflict-arrow">arrow_forward</span>
          <div className="e8-enr-conflict-val">
            <span className="e8-enr-conflict-k">{q.provider} · {q.confidence}%</span>
            <b>{q.newVal}</b>
          </div>
        </div>
        <div className="e8-enr-conflict-actions">
          <DSenr.Button variant="ghost" size="sm" onClick={() => onDismiss(q.id)}>Keep current</DSenr.Button>
          <DSenr.Button variant="secondary" size="sm" icon="published_with_changes" onClick={() => enrichConfirmOne(q, 'Overrode ' + q.record + ' ' + q.field + ' with the vendor value', ctx)}>Override with vendor value</DSenr.Button>
        </div>
      </div>
    );
  }
  return (
    <div className="e8-enr-qrow">
      <span className="e8-enr-qrec">{q.record}</span>
      <span style={{ flex: 1, fontSize: 'var(--ui-text-sm)', color: 'var(--ui-text-secondary)' }}>{q.field} <span style={{ color: 'var(--ui-text-tertiary)', textDecoration: 'line-through' }}>{String(curVal == null || curVal === '' ? '-' : curVal)}</span> <b style={{ color: 'var(--ui-text)' }}>{q.newVal}</b></span>
      <EnrichProvChip provider={q.provider} />
      <span className="e8-enr-conf">{q.confidence}%</span>
      <DSenr.Button variant="ghost" size="sm" onClick={() => onDismiss(q.id)}>Dismiss</DSenr.Button>
      <DSenr.Button variant="primary" size="sm" onClick={() => enrichConfirmOne(q, q.record + ' ' + q.field + ' saved to the record', ctx)}>Confirm</DSenr.Button>
    </div>
  );
}

/* ---- C: the central enrichment hub ---- */
function EnrichHubScreen() {
  const D = window.E8DATA;
  const { showToast } = React.useContext(E8Ctx);
  const { acted, markConfirmed, unmarkConfirmed, dismiss } = useEnrichActed();
  const ctx = { markConfirmed, unmarkConfirmed, showToast };
  // Provider config = hub-local state seeded from D.enrichProviders. Toggling previews the configured stack;
  // T3 can promote this to a replayable store op so it persists across reload.
  const [providers, setProviders] = React.useState(() => (D.enrichProviders || []).map((p) => ({ ...p })));
  // Toggle a provider. The toast is raised from the EVENT HANDLER (not inside the setProviders updater,
  // which React runs during render) so it never triggers a setState-in-render on App.
  const toggleProvider = (id) => {
    const cur = providers.find((p) => p.id === id);
    if (!cur) return;
    const willEnable = !cur.enabled;
    setProviders((prev) => prev.map((p) => (p.id === id ? { ...p, enabled: !p.enabled } : p)));
    if (showToast) showToast(cur.name + (willEnable ? ' enabled' : ' disabled') + ' for the ' + ENR_LAYER_META[cur.layer].label.toLowerCase());
  };
  const credits = D.enrichCredits;
  const left = credits.total - credits.used;
  const queue = (D.enrichQueue || []).filter((q) => acted.confirmed.indexOf(q.id) < 0 && acted.dismissed.indexOf(q.id) < 0);
  return (
    <React.Fragment>
      <Topbar crumbs={[{ label: 'Enrichment' }]} actions={<DSenr.Button variant="secondary" size="sm" icon="history" onClick={() => showToast && showToast('Full run history')}>Export usage</DSenr.Button>} />
      <div className="e8-content">
        <div className="e8-page">
          <div className="e8-pagehead"><div><h1 className="e8-h1">Enrichment</h1><div className="e8-pagehead-sub">Provider-agnostic data enrichment · {queue.length} updates to review</div></div></div>

          <div className="e8-enr-hubrow">
            <div className="e8-card" style={{ padding: '14px 16px' }}>
              <div className="e8-sectionlabel" style={{ marginBottom: 8 }}>Credits this month</div>
              <div style={{ fontSize: 'var(--ui-text-metric)', fontWeight: 700 }}>{credits.used.toLocaleString()} <span style={{ fontSize: 'var(--ui-text-base)', fontWeight: 400, color: 'var(--ui-text-tertiary)' }}>of {credits.total.toLocaleString()} used</span></div>
              <span className="e8-enr-meter"><span style={{ width: Math.round((credits.used / credits.total) * 100) + '%' }} /></span>
              <div style={{ fontSize: 'var(--ui-text-xs)', color: 'var(--ui-text-tertiary)' }}>Resets {credits.resets} · ~{left.toLocaleString()} enrichments left</div>
            </div>
            <EnrichCandidateRun />
          </div>

          <DSenr.Card title="Provider stack" flush>
            <div style={{ padding: '12px 16px' }}>
              <EnrichProviderStack providers={providers} onToggle={toggleProvider} />
            </div>
          </DSenr.Card>

          <div style={{ marginTop: 16 }}>
            <EnrichCompliancePosture providers={providers} />
          </div>

          <div style={{ marginTop: 16 }}>
            <DSenr.Card title={'Review queue · ' + queue.length + ' suggested updates'} flush>
              {queue.length === 0 ? <div style={{ padding: 16 }}><DSenr.EmptyState icon="task_alt" title="All caught up" body="No suggested updates waiting for review." /></div> : (
                <div style={{ padding: '4px 0' }}>
                  {queue.map((q) => <EnrichQueueItem key={q.id} q={q} ctx={ctx} onDismiss={dismiss} />)}
                  <div style={{ padding: '10px 16px', display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
                    <DSenr.Button variant="secondary" size="sm" icon="done_all" onClick={() => enrichConfirmMany(queue.filter((q) => q.confidence >= 80 && !enrichIsHeld(q) && !enrichConsentBlocked(q)), ctx)}>Confirm all high-confidence</DSenr.Button>
                    {queue.some((q) => enrichIsHeld(q)) ? <span className="e8-enr-caveat">Fields a candidate verified or a recruiter entered are held for an explicit override, not bulk-applied.</span> : null}
                  </div>
                </div>
              )}
            </DSenr.Card>
          </div>

          <div style={{ marginTop: 16 }}>
            <DSenr.Card title="Run history" flush>
              <ConfigurableTable listKey="enrichment-run-history" primary={false}
                flush density="compact"
                registry={[
                  { key: 'name', label: 'Run' },
                  { key: 'records', label: 'Records', width: 90, num: true },
                  { key: 'found', label: 'Found', width: 80, num: true },
                  { key: 'credits', label: 'Credits', width: 90, num: true },
                  { key: 'when', label: 'When', width: 120, secondary: true },
                ]}
                rows={D.enrichRuns}
              />
            </DSenr.Card>
          </div>
        </div>
      </div>
    </React.Fragment>
  );
}

Object.assign(window, { EnrichDossierCard, EnrichContactPanel, EnrichCandidatePanel, EnrichHubScreen, EnrichWaterfall });
