// East Quabbin Health — Identity & Record Matching
// Explains the identity model (EQH resident.id ↔ FHIR Patient.id ↔ Epic MRN)
// and gives coordinators a dedup / merge queue + match-rules config.

// ─── Mock data ───────────────────────────────────────────────────────
// Pending duplicate review queue (probable matches inside EQH itself)
const ID_DEDUP_QUEUE = [
  {
    id: 'dup-001',
    confidence: 94, status: 'pending',
    reasons: ['Same DOB', 'Same last name', 'Same phone', 'Address 0.8mi apart'],
    foundAt: '2026-04-23T09:12:00',
    trigger: 'New intake at Petersham library (Sarah K.)',
    a: { id: 'r001', first: 'Margaret', last: 'Whitcomb', dob: '04/12/1953',
         phone: '(978) 555-0142', address: '47 Brook Rd, Petersham, MA 01366',
         created: '2025-11-14', fhirId: 'epic-Patient-a8f291', mrn: 'UMM-2841097',
         source: 'CHW home visit' },
    b: { id: 'r-new', first: 'Maggie', last: 'Whitcomb', dob: '04/12/1953',
         phone: '(978) 555-0142', address: '47 Brook Road, Petersham MA',
         created: '2026-04-23', fhirId: null, mrn: null,
         source: 'Library signup kiosk' },
  },
  {
    id: 'dup-002',
    confidence: 78, status: 'pending',
    reasons: ['Same DOB', 'Same first name', 'Same town', 'Different last name (née?)'],
    foundAt: '2026-04-22T14:33:00',
    trigger: 'Cohort analyst (Marcus D.)',
    a: { id: 'r003', first: 'Rosa', last: 'Mendoza', dob: '08/22/1971',
         phone: '(978) 555-0203', address: '88 Main St Apt 2, Barre, MA 01005',
         created: '2024-06-02', fhirId: 'epic-Patient-c4d812', mrn: 'UMM-5829104',
         source: 'Health fair' },
    b: { id: 'r-new-2', first: 'Rosa', last: 'Mendoza-Ruiz', dob: '08/22/1971',
         phone: '(413) 555-8821', address: '88 Main Street #2, Barre',
         created: '2026-03-18', fhirId: 'epic-Patient-f22a09', mrn: 'UMM-6104772',
         source: 'Self-serve online' },
  },
  {
    id: 'dup-003',
    confidence: 62, status: 'pending',
    reasons: ['Same last name', 'Same town', 'DOB 1 year apart', 'Phone differs'],
    foundAt: '2026-04-21T11:05:00',
    trigger: 'Automated nightly scan',
    a: { id: 'r017', first: 'Stanley', last: 'Beauchamp', dob: '03/02/1937',
         phone: '(978) 555-1683', address: 'Main St, Hubbardston',
         created: '2024-09-11', fhirId: 'epic-Patient-11bb40', mrn: 'UMM-9382016',
         source: 'CHW home visit' },
    b: { id: 'r-new-3', first: 'Stan', last: 'Beauchamp', dob: '03/02/1938',
         phone: '(978) 555-4419', address: '22 Oak Ln, Hubbardston MA',
         created: '2026-02-08', fhirId: null, mrn: null,
         source: 'Senior center sign-up' },
  },
];

const ID_MATCH_RULES = [
  { field: 'Date of birth',    weight: 30, type: 'exact',      current: true },
  { field: 'Last name',         weight: 25, type: 'fuzzy (Jaro-Winkler ≥ 0.85)', current: true },
  { field: 'First name',        weight: 15, type: 'fuzzy + nickname table (Maggie↔Margaret)', current: true },
  { field: 'Phone number',      weight: 20, type: 'normalized digits', current: true },
  { field: 'Address (street)',  weight: 10, type: 'normalized + geocode within 1mi', current: true },
  { field: 'Email',             weight: 10, type: 'exact (lowercased)', current: true },
  { field: 'SSN last 4',        weight: 40, type: 'exact', current: false, note: 'Not collected by EQH' },
];

const ID_STATS = {
  residents: 247,
  epicLinked: 189,
  epicLinkRate: 76,
  pendingReview: 3,
  mergedThisMonth: 7,
  autoMatchedThisMonth: 34,
  ambiguous: 2,
};

// ─── Component ───────────────────────────────────────────────────────
const ChwIdentityPage = () => {
  const [selected, setSelected] = React.useState(ID_DEDUP_QUEUE[0].id);
  const [mergePrimary, setMergePrimary] = React.useState('a');
  const [showMerge, setShowMerge] = React.useState(false);
  const [resolved, setResolved] = React.useState({}); // duplicate id -> 'merged' | 'distinct'

  const dup = ID_DEDUP_QUEUE.find(d => d.id === selected);

  return (
    <div style={{ padding: '24px 28px 40px', maxWidth: 1280 }}>
      <div style={{ marginBottom: 20 }}>
        <h1 style={{ fontFamily: 'var(--eq-serif)', fontSize: 30, fontWeight: 500, margin: 0, letterSpacing: '-0.01em' }}>
          Identity &amp; record matching
        </h1>
        <div style={{ color: 'var(--eq-muted)', fontSize: 14, marginTop: 6, maxWidth: 720 }}>
          How East Quabbin resident records line up with FHIR <code style={{ fontFamily: 'var(--eq-mono, monospace)', fontSize: 12.5, background: 'var(--eq-surface-2)', padding: '1px 6px', borderRadius: 4 }}>Patient.id</code> in Epic &mdash; and how we catch duplicates when someone signs up twice.
        </div>
      </div>

      {/* Stats strip */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10, marginBottom: 20 }}>
        <IdStat label="Residents on file" value={ID_STATS.residents} />
        <IdStat label="Linked to Epic" value={ID_STATS.epicLinked} sub={ID_STATS.epicLinkRate + '% match rate'} tone="epic" />
        <IdStat label="Pending dedup review" value={ID_STATS.pendingReview} tone="warn" />
        <IdStat label="Auto-matched this month" value={ID_STATS.autoMatchedThisMonth} sub={ID_STATS.mergedThisMonth + ' merged manually'} tone="ok" />
      </div>

      {/* Identity model explainer */}
      <IdModelCard />

      {/* Dedup queue */}
      <div style={{
        marginTop: 24,
        background: 'var(--eq-surface)',
        border: '1px solid var(--eq-line)',
        borderRadius: 'var(--eq-r-lg)',
        overflow: 'hidden',
      }}>
        <div style={{ padding: '16px 20px', borderBottom: '1px solid var(--eq-line)', display: 'flex', alignItems: 'center', gap: 12 }}>
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: 'var(--eq-serif)', fontSize: 20, fontWeight: 500 }}>Dedup review queue</div>
            <div style={{ fontSize: 12.5, color: 'var(--eq-muted)', marginTop: 2 }}>
              Probable duplicates flagged by our matcher. High-confidence merges (≥95) run automatically; anything below lands here.
            </div>
          </div>
          <div style={{ fontSize: 11, padding: '4px 10px', borderRadius: 999, background: 'color-mix(in srgb, var(--eq-warn) 14%, transparent)', color: 'var(--eq-warn)', fontWeight: 600 }}>
            {ID_DEDUP_QUEUE.filter(d => !resolved[d.id]).length} pending
          </div>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '280px 1fr', minHeight: 460 }}>
          <div style={{ borderRight: '1px solid var(--eq-line)', overflowY: 'auto' }}>
            {ID_DEDUP_QUEUE.map(d => {
              const r = resolved[d.id];
              return (
                <div key={d.id} onClick={() => { setSelected(d.id); setShowMerge(false); }}
                     style={{
                       padding: '14px 16px',
                       borderBottom: '1px solid var(--eq-line)',
                       background: selected === d.id ? 'var(--eq-surface-2)' : 'transparent',
                       cursor: 'pointer',
                       borderLeft: selected === d.id ? '3px solid var(--eq-sage-deep)' : '3px solid transparent',
                     }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
                    <span style={{ fontWeight: 600, fontSize: 13.5 }}>{d.a.first} {d.a.last}</span>
                    <span style={{ color: 'var(--eq-muted)', fontSize: 11 }}>·</span>
                    <span style={{ fontSize: 11, color: 'var(--eq-muted)' }}>DOB {d.a.dob}</span>
                  </div>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                    <ConfidenceBar value={d.confidence} />
                    <span style={{ fontSize: 11, fontWeight: 600, color: confColor(d.confidence) }}>{d.confidence}%</span>
                  </div>
                  <div style={{ fontSize: 11, color: 'var(--eq-muted)', marginTop: 4 }}>
                    {r === 'merged' ? '✓ Merged' : r === 'distinct' ? '◦ Kept separate' : d.reasons[0]}
                  </div>
                </div>
              );
            })}
          </div>

          {dup && (
            <div style={{ padding: 20, overflowY: 'auto' }}>
              {resolved[dup.id] ? (
                <IdResolvedView action={resolved[dup.id]} dup={dup} onUndo={() => setResolved(r => { const n = {...r}; delete n[dup.id]; return n; })} />
              ) : showMerge ? (
                <IdMergeWizard dup={dup} primary={mergePrimary} setPrimary={setMergePrimary}
                               onCancel={() => setShowMerge(false)}
                               onComplete={() => { setResolved(r => ({ ...r, [dup.id]: 'merged' })); setShowMerge(false); }} />
              ) : (
                <IdCompare dup={dup}
                           onMerge={() => setShowMerge(true)}
                           onDistinct={() => setResolved(r => ({ ...r, [dup.id]: 'distinct' }))} />
              )}
            </div>
          )}
        </div>
      </div>

      {/* Match rules config */}
      <IdMatchRulesCard />

      {/* Future-signup scenario */}
      <IdRepeatSignupCard />
    </div>
  );
};

// ─── Identity model explainer ─────────────────────────────────────────
const IdModelCard = () => (
  <div style={{
    background: 'var(--eq-surface)',
    border: '1px solid var(--eq-line)',
    borderRadius: 'var(--eq-r-lg)',
    padding: 24,
  }}>
    <div style={{ fontFamily: 'var(--eq-serif)', fontSize: 20, fontWeight: 500, marginBottom: 4 }}>
      How we identify a resident
    </div>
    <div style={{ fontSize: 13, color: 'var(--eq-muted)', marginBottom: 20, maxWidth: 760 }}>
      Every resident has one canonical EQH id. External systems (Epic, MassHealth, etc.) each contribute their own identifier as an <em>alias</em>. We never overwrite the canonical record — we add identifiers to it.
    </div>

    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16 }}>
      <IdCol
        color="var(--eq-sage-deep)" bg="color-mix(in srgb, var(--eq-sage) 16%, var(--eq-surface))"
        label="East Quabbin Health"
        id="resident.id"
        sample="r001"
        kind="Canonical key"
        rules={[
          'Assigned at first intake',
          'Never changes, never reused',
          'Internal only — not shared with Epic',
        ]}
      />
      <IdCol
        color="#6a49a3" bg="color-mix(in srgb, #9678c7 14%, var(--eq-surface))"
        label="FHIR / Epic"
        id="Patient.id"
        sample="epic-Patient-a8f291"
        kind="External alias"
        rules={[
          'Returned by Epic\u2019s FHIR API on match',
          'Stored as identifiers[] on our record',
          'Used for every subsequent read/write',
        ]}
      />
      <IdCol
        color="#C8102E" bg="color-mix(in srgb, #C8102E 10%, var(--eq-surface))"
        label="Human-readable"
        id="Epic MRN"
        sample="UMM-2841097"
        kind="Display identifier"
        rules={[
          'What clinicians say out loud',
          'Shown in drawer, audit log, referrals',
          'Redundant with Patient.id, but stable',
        ]}
      />
    </div>

    <div style={{
      marginTop: 18, padding: '12px 14px',
      background: 'var(--eq-surface-2)',
      borderRadius: 'var(--eq-r-sm)',
      fontFamily: 'var(--eq-mono, monospace)', fontSize: 12, lineHeight: 1.7,
      color: 'var(--eq-ink-2)',
      border: '1px dashed var(--eq-line)',
    }}>
      <div style={{ color: 'var(--eq-muted)', marginBottom: 6, fontFamily: 'inherit', fontSize: 10.5, letterSpacing: '0.08em', textTransform: 'uppercase', fontWeight: 600 }}>
        Stored shape on a linked resident
      </div>
      <div>{'{'}</div>
      <div style={{ paddingLeft: 20 }}>
        <span style={{ color: 'var(--eq-sage-deep)' }}>id</span>: <span>"r001"</span>,<br/>
        <span style={{ color: 'var(--eq-sage-deep)' }}>firstName</span>: <span>"Margaret"</span>, <span style={{ color: 'var(--eq-sage-deep)' }}>lastName</span>: <span>"Whitcomb"</span>,<br/>
        <span style={{ color: 'var(--eq-sage-deep)' }}>identifiers</span>: [<br/>
        <span style={{ paddingLeft: 20, display: 'inline-block' }}>
          {'{ system: "urn:epic:UMMemorial", value: "UMM-2841097", use: "official" },'}<br/>
          {'{ system: "urn:ietf:rfc:3986",    value: "epic-Patient-a8f291", use: "fhir" },'}<br/>
          {'{ system: "urn:masshealth",       value: "MH-44718023", use: "secondary" }'}
        </span><br/>
        ],<br/>
        <span style={{ color: 'var(--eq-sage-deep)' }}>matchConfidence</span>: 98, <span style={{ color: 'var(--eq-sage-deep)' }}>matchedAt</span>: <span>"2025-11-14"</span>,<br/>
        <span style={{ color: 'var(--eq-sage-deep)' }}>mergedFrom</span>: []
      </div>
      <div>{'}'}</div>
    </div>
  </div>
);

const IdCol = ({ color, bg, label, id, sample, kind, rules }) => (
  <div style={{ background: bg, border: `1px solid ${color}`, borderRadius: 'var(--eq-r-md)', padding: 16 }}>
    <div style={{ fontSize: 10.5, letterSpacing: '0.08em', textTransform: 'uppercase', color, fontWeight: 700, marginBottom: 4 }}>{label}</div>
    <div style={{ fontFamily: 'var(--eq-mono, monospace)', fontSize: 13, fontWeight: 600, color: 'var(--eq-ink)' }}>{id}</div>
    <div style={{ fontFamily: 'var(--eq-mono, monospace)', fontSize: 11.5, color: 'var(--eq-ink-2)', marginTop: 2 }}>e.g. {sample}</div>
    <div style={{ fontSize: 11, color: 'var(--eq-muted)', marginTop: 4, fontStyle: 'italic' }}>{kind}</div>
    <ul style={{ fontSize: 12, color: 'var(--eq-ink-2)', lineHeight: 1.5, marginTop: 10, paddingLeft: 16, margin: '10px 0 0', paddingInlineStart: 16 }}>
      {rules.map((r, i) => <li key={i} style={{ marginBottom: 3 }}>{r}</li>)}
    </ul>
  </div>
);

// ─── Stat cell ─────────────────────────────────────────────────────
const IdStat = ({ label, value, sub, tone }) => {
  const tones = {
    epic: { color: '#C8102E', bg: 'color-mix(in srgb, #C8102E 8%, var(--eq-surface))' },
    warn: { color: 'var(--eq-warn)', bg: 'color-mix(in srgb, var(--eq-warn) 10%, var(--eq-surface))' },
    ok:   { color: 'var(--eq-sage-deep)', bg: 'color-mix(in srgb, var(--eq-sage) 14%, var(--eq-surface))' },
  };
  const s = tones[tone] || { color: 'var(--eq-ink)', bg: 'var(--eq-surface)' };
  return (
    <div style={{ background: s.bg, border: '1px solid var(--eq-line)', borderRadius: 'var(--eq-r-md)', padding: '14px 16px' }}>
      <div style={{ fontSize: 10.5, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--eq-muted)', fontWeight: 600 }}>{label}</div>
      <div style={{ fontFamily: 'var(--eq-serif)', fontSize: 28, fontWeight: 500, color: s.color, marginTop: 2, lineHeight: 1 }}>{value}</div>
      {sub && <div style={{ fontSize: 11, color: 'var(--eq-muted)', marginTop: 4 }}>{sub}</div>}
    </div>
  );
};

// ─── Confidence meter ────────────────────────────────────────────
const ConfidenceBar = ({ value }) => (
  <div style={{ flex: 1, height: 5, background: 'var(--eq-line)', borderRadius: 3, overflow: 'hidden' }}>
    <div style={{ width: value + '%', height: '100%', background: confColor(value), transition: 'width 200ms' }} />
  </div>
);
const confColor = (v) => v >= 90 ? 'var(--eq-sage-deep)' : v >= 75 ? '#b8860b' : 'var(--eq-warn)';

// ─── Side-by-side compare ─────────────────────────────────────────
const IdCompare = ({ dup, onMerge, onDistinct }) => {
  const fields = [
    { k: 'first', l: 'First name' },
    { k: 'last', l: 'Last name' },
    { k: 'dob', l: 'Date of birth' },
    { k: 'phone', l: 'Phone' },
    { k: 'address', l: 'Address' },
    { k: 'source', l: 'Source' },
    { k: 'created', l: 'Created' },
    { k: 'fhirId', l: 'FHIR Patient.id', mono: true },
    { k: 'mrn', l: 'Epic MRN', mono: true },
  ];
  return (
    <>
      <div style={{ marginBottom: 14 }}>
        <div style={{ fontSize: 11, color: 'var(--eq-muted)', letterSpacing: '0.08em', textTransform: 'uppercase', fontWeight: 600, marginBottom: 4 }}>Why flagged</div>
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
          {dup.reasons.map((r, i) => (
            <span key={i} style={{ fontSize: 11.5, padding: '3px 10px', borderRadius: 999, background: 'var(--eq-surface-2)', border: '1px solid var(--eq-line)' }}>{r}</span>
          ))}
        </div>
        <div style={{ fontSize: 11.5, color: 'var(--eq-muted)', marginTop: 8 }}>
          Flagged {formatRelTime(dup.foundAt)} · {dup.trigger}
        </div>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '160px 1fr 1fr', border: '1px solid var(--eq-line)', borderRadius: 'var(--eq-r-md)', overflow: 'hidden' }}>
        <div />
        <div style={{ padding: '10px 14px', background: 'var(--eq-surface-2)', borderBottom: '1px solid var(--eq-line)', fontSize: 11, fontWeight: 700, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--eq-muted)' }}>
          Existing · {dup.a.id}
        </div>
        <div style={{ padding: '10px 14px', background: 'color-mix(in srgb, var(--eq-warn) 8%, var(--eq-surface-2))', borderBottom: '1px solid var(--eq-line)', fontSize: 11, fontWeight: 700, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--eq-warn)' }}>
          New signup · {dup.b.id}
        </div>
        {fields.map((f, i) => {
          const av = dup.a[f.k], bv = dup.b[f.k];
          const same = av === bv;
          return (
            <React.Fragment key={f.k}>
              <div style={{ padding: '10px 14px', background: 'var(--eq-surface)', borderTop: i > 0 ? '1px solid var(--eq-line)' : 'none', fontSize: 12, fontWeight: 600, color: 'var(--eq-ink-2)' }}>{f.l}</div>
              <div style={{ padding: '10px 14px', borderTop: i > 0 ? '1px solid var(--eq-line)' : 'none', fontSize: 13, fontFamily: f.mono ? 'var(--eq-mono, monospace)' : 'inherit' }}>{av || <em style={{ color: 'var(--eq-muted)' }}>—</em>}</div>
              <div style={{ padding: '10px 14px', borderTop: i > 0 ? '1px solid var(--eq-line)' : 'none', fontSize: 13, fontFamily: f.mono ? 'var(--eq-mono, monospace)' : 'inherit',
                            background: same ? 'transparent' : 'color-mix(in srgb, var(--eq-warn) 9%, transparent)',
                            fontWeight: same ? 400 : 600 }}>
                {bv || <em style={{ color: 'var(--eq-muted)' }}>—</em>}
              </div>
            </React.Fragment>
          );
        })}
      </div>

      <div style={{ display: 'flex', gap: 10, marginTop: 18 }}>
        <button className="chw-btn chw-btn-primary" onClick={onMerge} style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
          <ChwIcon name="plus" size={13} stroke={2.5} /> Merge records…
        </button>
        <button className="chw-btn chw-btn-ghost" onClick={onDistinct}>
          Mark as distinct people
        </button>
        <div style={{ flex: 1 }} />
        <button className="chw-btn chw-btn-ghost">
          <ChwIcon name="mail" size={13} /> Ask CHW to verify
        </button>
      </div>
    </>
  );
};

// ─── Merge wizard ─────────────────────────────────────────────────
const IdMergeWizard = ({ dup, primary, setPrimary, onCancel, onComplete }) => {
  const p = dup[primary];
  const s = dup[primary === 'a' ? 'b' : 'a'];
  const [picks, setPicks] = React.useState({});
  const [note, setNote] = React.useState('');
  const conflictFields = ['first', 'last', 'phone', 'address'].filter(k => dup.a[k] !== dup.b[k]);

  return (
    <>
      <div style={{ marginBottom: 14 }}>
        <div style={{ fontFamily: 'var(--eq-serif)', fontSize: 18, fontWeight: 500 }}>Merge records</div>
        <div style={{ fontSize: 12.5, color: 'var(--eq-muted)', marginTop: 2 }}>
          One resident survives with all identifiers and history. The other id is archived with a redirect.
        </div>
      </div>

      <div style={{ marginBottom: 18 }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--eq-muted)', marginBottom: 8 }}>1. Primary record (survives)</div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          {['a', 'b'].map(k => {
            const r = dup[k];
            const sel = primary === k;
            return (
              <button key={k} onClick={() => setPrimary(k)} style={{
                padding: 14, textAlign: 'left',
                background: sel ? 'color-mix(in srgb, var(--eq-sage) 12%, var(--eq-surface))' : 'var(--eq-surface)',
                border: '1.5px solid ' + (sel ? 'var(--eq-sage-deep)' : 'var(--eq-line)'),
                borderRadius: 'var(--eq-r-md)', cursor: 'pointer', transition: 'all 140ms',
              }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                  <div style={{ width: 16, height: 16, borderRadius: '50%', border: '2px solid ' + (sel ? 'var(--eq-sage-deep)' : 'var(--eq-line)'), background: sel ? 'var(--eq-sage-deep)' : 'transparent' }} />
                  <span style={{ fontWeight: 600, fontSize: 14 }}>{r.first} {r.last}</span>
                  <span style={{ fontSize: 11, color: 'var(--eq-muted)', marginLeft: 'auto' }}>{r.id}</span>
                </div>
                <div style={{ fontSize: 11.5, color: 'var(--eq-muted)', marginTop: 6 }}>
                  Created {r.created} · {r.source}
                </div>
                {r.mrn && <div style={{ fontSize: 11, color: '#C8102E', marginTop: 4, fontFamily: 'var(--eq-mono, monospace)' }}>MRN {r.mrn}</div>}
              </button>
            );
          })}
        </div>
      </div>

      {conflictFields.length > 0 && (
        <div style={{ marginBottom: 18 }}>
          <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--eq-muted)', marginBottom: 8 }}>2. Resolve conflicts</div>
          <div style={{ border: '1px solid var(--eq-line)', borderRadius: 'var(--eq-r-md)', overflow: 'hidden' }}>
            {conflictFields.map((k, i) => {
              const chosen = picks[k] || primary;
              return (
                <div key={k} style={{ display: 'grid', gridTemplateColumns: '140px 1fr 1fr', borderTop: i > 0 ? '1px solid var(--eq-line)' : 'none' }}>
                  <div style={{ padding: '10px 14px', fontSize: 12, fontWeight: 600, color: 'var(--eq-ink-2)', background: 'var(--eq-surface-2)' }}>
                    {k === 'first' ? 'First name' : k === 'last' ? 'Last name' : k === 'phone' ? 'Phone' : 'Address'}
                  </div>
                  {['a', 'b'].map(side => (
                    <button key={side} onClick={() => setPicks(o => ({ ...o, [k]: side }))}
                            style={{
                              padding: '10px 14px', textAlign: 'left',
                              background: chosen === side ? 'color-mix(in srgb, var(--eq-sage) 12%, transparent)' : 'var(--eq-surface)',
                              border: 'none',
                              borderLeft: '1px solid var(--eq-line)',
                              fontSize: 13, cursor: 'pointer',
                              fontWeight: chosen === side ? 600 : 400,
                              color: chosen === side ? 'var(--eq-sage-deep)' : 'var(--eq-ink)',
                            }}>
                      {chosen === side && <span style={{ marginRight: 6 }}>✓</span>}
                      {dup[side][k]}
                    </button>
                  ))}
                </div>
              );
            })}
          </div>
          <div style={{ fontSize: 11.5, color: 'var(--eq-muted)', marginTop: 6 }}>
            Both records\u2019 identifiers (FHIR Patient.id, MRN, MassHealth) are kept on the survivor.
          </div>
        </div>
      )}

      <div style={{ marginBottom: 18 }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--eq-muted)', marginBottom: 8 }}>
          {conflictFields.length > 0 ? '3' : '2'}. Merge note (for audit log)
        </div>
        <textarea value={note} onChange={(e) => setNote(e.target.value)} rows={2}
                  placeholder="e.g. Resident confirmed 'Maggie' is a nickname. Same phone, same home."
                  style={{ width: '100%', padding: 10, fontSize: 13, border: '1px solid var(--eq-line)', borderRadius: 'var(--eq-r-sm)', fontFamily: 'inherit', resize: 'vertical' }} />
      </div>

      <div style={{ padding: '12px 14px', background: 'var(--eq-surface-2)', border: '1px dashed var(--eq-line)', borderRadius: 'var(--eq-r-sm)', fontSize: 12, lineHeight: 1.5, marginBottom: 18 }}>
        <div style={{ fontWeight: 600, marginBottom: 4 }}>What happens on merge</div>
        <ul style={{ margin: 0, paddingLeft: 18, color: 'var(--eq-ink-2)' }}>
          <li><code style={{ fontFamily: 'var(--eq-mono, monospace)' }}>{s.id}</code> &rarr; redirect to <code style={{ fontFamily: 'var(--eq-mono, monospace)' }}>{p.id}</code>, marked <em>merged</em></li>
          <li>Both identifiers (MRN, FHIR Patient.id) attached to survivor</li>
          <li>Activity, consents, and SDoH answers combined &mdash; newest wins on conflict</li>
          <li>Merge logged in audit trail under your name</li>
          <li>Reversible for 30 days</li>
        </ul>
      </div>

      <div style={{ display: 'flex', gap: 10 }}>
        <button className="chw-btn chw-btn-primary" onClick={onComplete}>Complete merge</button>
        <button className="chw-btn chw-btn-ghost" onClick={onCancel}>Back</button>
      </div>
    </>
  );
};

// ─── Resolved view ────────────────────────────────────────────────
const IdResolvedView = ({ action, dup, onUndo }) => (
  <div style={{
    padding: 24, textAlign: 'center',
    background: action === 'merged' ? 'color-mix(in srgb, var(--eq-sage) 12%, var(--eq-surface))' : 'var(--eq-surface)',
    border: '1px dashed var(--eq-line)', borderRadius: 'var(--eq-r-md)',
  }}>
    <div style={{ fontFamily: 'var(--eq-serif)', fontSize: 20, fontWeight: 500, marginBottom: 6 }}>
      {action === 'merged' ? '✓ Records merged' : '◦ Kept as distinct people'}
    </div>
    <div style={{ fontSize: 13, color: 'var(--eq-muted)', maxWidth: 420, margin: '0 auto' }}>
      {action === 'merged'
        ? `${dup.b.id} now redirects to ${dup.a.id}. Both MRNs are attached to the surviving record.`
        : `These are different people. They\u2019ll stay separate and the matcher won\u2019t flag them again.`}
    </div>
    <button className="chw-btn chw-btn-ghost" onClick={onUndo} style={{ marginTop: 14 }}>Undo</button>
  </div>
);

// ─── Match rules config card ─────────────────────────────────────
const IdMatchRulesCard = () => {
  const total = ID_MATCH_RULES.filter(r => r.current).reduce((a, r) => a + r.weight, 0);
  return (
    <div style={{
      marginTop: 24,
      background: 'var(--eq-surface)',
      border: '1px solid var(--eq-line)',
      borderRadius: 'var(--eq-r-lg)',
      padding: 24,
    }}>
      <div style={{ display: 'flex', alignItems: 'flex-end', gap: 12, marginBottom: 4 }}>
        <div style={{ flex: 1 }}>
          <div style={{ fontFamily: 'var(--eq-serif)', fontSize: 20, fontWeight: 500 }}>Match rules</div>
          <div style={{ fontSize: 13, color: 'var(--eq-muted)', marginTop: 2 }}>
            Each field contributes to a probabilistic match score. Tuned with the Fellegi-Sunter model, calibrated against ground truth from our first 200 enrollments.
          </div>
        </div>
        <button className="chw-btn chw-btn-ghost" style={{ fontSize: 12 }}>Edit weights…</button>
      </div>

      <div style={{ display: 'flex', gap: 14, marginTop: 16, marginBottom: 16, fontSize: 12 }}>
        <Threshold label="Auto-link" color="var(--eq-sage-deep)" from={95} to={100} />
        <Threshold label="Review queue" color="#b8860b" from={70} to={94} />
        <Threshold label="Ignore" color="var(--eq-muted)" from={0} to={69} />
      </div>

      <div style={{ border: '1px solid var(--eq-line)', borderRadius: 'var(--eq-r-md)', overflow: 'hidden' }}>
        <div style={{ display: 'grid', gridTemplateColumns: '1.5fr 80px 2fr 1fr', padding: '10px 14px', background: 'var(--eq-surface-2)', fontSize: 11, fontWeight: 700, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--eq-muted)' }}>
          <div>Field</div><div>Weight</div><div>Match type</div><div>Status</div>
        </div>
        {ID_MATCH_RULES.map((r, i) => (
          <div key={r.field} style={{ display: 'grid', gridTemplateColumns: '1.5fr 80px 2fr 1fr', padding: '11px 14px', borderTop: '1px solid var(--eq-line)', alignItems: 'center', fontSize: 13, opacity: r.current ? 1 : 0.55 }}>
            <div style={{ fontWeight: 500 }}>{r.field}</div>
            <div style={{ fontFamily: 'var(--eq-mono, monospace)', fontSize: 12, color: r.current ? 'var(--eq-sage-deep)' : 'var(--eq-muted)', fontWeight: 600 }}>
              {r.current ? r.weight : '—'}
            </div>
            <div style={{ fontSize: 12, color: 'var(--eq-ink-2)' }}>{r.type}</div>
            <div style={{ fontSize: 11 }}>
              {r.current ? (
                <span style={{ padding: '2px 8px', borderRadius: 999, background: 'color-mix(in srgb, var(--eq-sage) 14%, transparent)', color: 'var(--eq-sage-deep)', fontWeight: 600 }}>Active</span>
              ) : (
                <span style={{ padding: '2px 8px', borderRadius: 999, background: 'var(--eq-surface-2)', color: 'var(--eq-muted)' }}>{r.note || 'Disabled'}</span>
              )}
            </div>
          </div>
        ))}
      </div>
      <div style={{ fontSize: 12, color: 'var(--eq-muted)', marginTop: 10 }}>
        Active weight total: <b style={{ color: 'var(--eq-ink)' }}>{total}</b> &middot; normalized to 0–100 score before threshold comparison.
      </div>
    </div>
  );
};

const Threshold = ({ label, color, from, to }) => (
  <div style={{ flex: 1, padding: '10px 12px', border: `1px solid ${color}`, borderRadius: 'var(--eq-r-sm)', background: `color-mix(in srgb, ${color} 8%, transparent)` }}>
    <div style={{ fontSize: 10.5, letterSpacing: '0.08em', textTransform: 'uppercase', color, fontWeight: 700 }}>{label}</div>
    <div style={{ fontFamily: 'var(--eq-mono, monospace)', fontSize: 13, fontWeight: 600, marginTop: 2 }}>{from}–{to}</div>
  </div>
);

// ─── Repeat-signup explainer ─────────────────────────────────────
const IdRepeatSignupCard = () => (
  <div style={{
    marginTop: 24,
    background: 'var(--eq-surface)',
    border: '1px solid var(--eq-line)',
    borderRadius: 'var(--eq-r-lg)',
    padding: 24,
  }}>
    <div style={{ fontFamily: 'var(--eq-serif)', fontSize: 20, fontWeight: 500 }}>When someone signs up again</div>
    <div style={{ fontSize: 13, color: 'var(--eq-muted)', marginTop: 2, marginBottom: 18, maxWidth: 780 }}>
      Every new intake runs through the matcher before a record is created. The coordinator sees both EQH and Epic matches inline — no duplicates unless they are confirmed as distinct people.
    </div>

    <div style={{ display: 'grid', gridTemplateColumns: '1fr auto 1fr auto 1fr', gap: 12, alignItems: 'stretch' }}>
      <FlowStep n="1" title="Resident types DOB + name" body="Matcher fires on blur of the DOB field with &ge;3 characters of last name." />
      <FlowArrow />
      <FlowStep n="2" title="Scored against EQH + Epic" body="Local residents checked first, then FHIR Patient $match call if no EQH hit." />
      <FlowArrow />
      <FlowStep n="3" title="Coordinator confirms" body="&ge;95 auto-links silently. 70&ndash;94 shows match card in intake UI. &lt;70 creates new." />
    </div>

    <div style={{ marginTop: 18, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
      <div style={{ padding: 14, background: 'color-mix(in srgb, var(--eq-sage) 10%, var(--eq-surface))', border: '1px solid var(--eq-line)', borderRadius: 'var(--eq-r-md)' }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--eq-sage-deep)', marginBottom: 6 }}>Scenario A &middot; Same person, different wording</div>
        <div style={{ fontSize: 12.5, color: 'var(--eq-ink-2)', lineHeight: 1.5 }}>
          Margaret signed up in Nov; &quot;Maggie Whitcomb&quot; signs up today at the library. Matcher hits 94%, lands in review queue. Coordinator merges &mdash; one record, both touchpoints attached.
        </div>
      </div>
      <div style={{ padding: 14, background: 'color-mix(in srgb, var(--eq-warn) 8%, var(--eq-surface))', border: '1px solid var(--eq-line)', borderRadius: 'var(--eq-r-md)' }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--eq-warn)', marginBottom: 6 }}>Scenario B &middot; Truly new person</div>
        <div style={{ fontSize: 12.5, color: 'var(--eq-ink-2)', lineHeight: 1.5 }}>
          Score stays below 70 &mdash; no match prompt shown, new <code style={{ fontFamily: 'var(--eq-mono, monospace)', fontSize: 11.5, background: 'var(--eq-surface-2)', padding: '1px 4px', borderRadius: 3 }}>resident.id</code> minted, Epic queried for a fresh FHIR Patient.id on first clinical read.
        </div>
      </div>
    </div>
  </div>
);

const FlowStep = ({ n, title, body }) => (
  <div style={{ padding: 14, background: 'var(--eq-surface-2)', border: '1px solid var(--eq-line)', borderRadius: 'var(--eq-r-md)' }}>
    <div style={{ fontFamily: 'var(--eq-serif)', fontSize: 22, fontWeight: 500, color: 'var(--eq-sage-deep)', lineHeight: 1 }}>{n}</div>
    <div style={{ fontSize: 13, fontWeight: 600, marginTop: 6 }}>{title}</div>
    <div style={{ fontSize: 12, color: 'var(--eq-ink-2)', lineHeight: 1.45, marginTop: 4 }}>{body}</div>
  </div>
);
const FlowArrow = () => (
  <div style={{ display: 'grid', placeItems: 'center', color: 'var(--eq-muted)', fontSize: 22 }}>→</div>
);

// ─── utils ───────────────────────────────────────────────────────
function formatRelTime(iso) {
  const d = new Date(iso);
  const diff = Date.now() - d.getTime();
  const h = Math.floor(diff / 3600000);
  if (h < 1) return 'just now';
  if (h < 24) return h + 'h ago';
  const days = Math.floor(h / 24);
  return days + 'd ago';
}

window.ChwIdentityPage = ChwIdentityPage;
