/* ============================================================
   MUIY — Donor Pool: sync status indicator + professional
   "Export donors" PDF report.
   Plain JSX, attaches components to window for the other Babel
   scripts (pool.jsx, shell.jsx). No external libraries — the PDF
   is produced by the browser's own print pipeline, fully offline.
   ============================================================ */

/* ------------------------------------------------------------
   1. SYNC STATUS BADGE
   A subtle, idle-quiet indicator for the Donor Pool header.
   App-level state (store.donorSync) flips to 'syncing' whenever a
   donor is added / edited / deleted / imported / deferred / logged,
   settles to 'synced' (or 'error' when offline) and eases back to
   'idle'. Tapping it surfaces the honest last-sync detail.
   ------------------------------------------------------------ */
function SyncBadge({ store }) {
  const state = store.donorSync || 'idle';
  const sync = store.sync || {};
  const META = {
    idle:    { icon: 'cloud',   label: 'Saved' },
    syncing: { icon: 'refresh', label: 'Syncing' },
    synced:  { icon: 'check',   label: 'Synced' },
    error:   { icon: 'cloud',   label: 'Offline' },
  };
  const meta = META[state] || META.idle;
  const onTap = () => {
    const when = sync.lastSyncedAt ? TC.relAgo(sync.lastSyncedAt) : 'just now';
    if (state === 'syncing') return store.showToast('Saving your changes…', '🔄');
    if (state === 'error') return store.showToast('Offline — changes are saved on this device and will sync once you reconnect.', '☁️');
    store.showToast(`All donor changes saved · synced ${when}`, '🌱');
  };
  return (
    <button
      className={'dx-sync dx-sync--' + state}
      onClick={onTap}
      aria-label={'Donor sync status: ' + meta.label}
      title={meta.label}
    >
      <span className="dx-sync-ico"><Icon name={meta.icon} size={15} sw={2.1} /></span>
      <span className="dx-sync-lbl">{meta.label}</span>
    </button>
  );
}

/* ------------------------------------------------------------
   2. REPORT MODEL
   Pure function over the donor list → everything the PDF needs.
   ------------------------------------------------------------ */
const DX_STATUS = {
  ELIGIBLE: { label: 'Eligible',  color: '#1A8F5E', wash: '#E3F3EA' },
  SOON:     { label: 'Due soon',  color: '#C07A12', wash: '#FBEED6' },
  WAITING:  { label: 'Resting',   color: '#5B7378', wash: '#EAF1F1' },
  DEFERRED: { label: 'Deferred',  color: '#B0531D', wash: '#F8E7DB' },
};

// monochrome-teal tints for the blood-group distribution bar/legend
const DX_BG_TINTS = ['#14716A', '#2E8B83', '#4FA59D', '#73BDB6', '#9AD2CC', '#BFE2DE', '#1F5C57', '#3C9A91'];

function dxNextEligible(donor, st) {
  if (st.status === 'ELIGIBLE') return null; // eligible now
  const last = TC.lastDonation(donor);
  if (!last) return null;
  const win = TC.ELIGIBILITY_WINDOWS[last.method] || 90;
  return TC.addDays(last.date, win);
}

function buildDonorReport(donors, patient, scope) {
  const active = (donors || []).filter((d) => !d.archived);
  let filtered = active;
  if (scope.type === 'eligible') {
    filtered = active.filter((d) => TC.donorStatus(d).status === 'ELIGIBLE');
  } else if (scope.type === 'groups' && scope.groups && scope.groups.length) {
    const set = new Set(scope.groups);
    filtered = active.filter((d) => set.has(d.bloodGroup));
  }

  const rows = filtered.map((d) => {
    const st = TC.donorStatus(d);
    const last = TC.lastDonation(d);
    const dob = d.dob || null;
    let age = null;
    if (dob) { try { age = Math.floor((TC.now() - TC.parse(dob).getTime()) / (365.25 * 864e5)); } catch (e) {} }
    return {
      d, st, last,
      count: TC.donationCount(d),
      next: dxNextEligible(d, st),
      gender: d.gender || d.sex || null,
      dob, age,
      email: d.email || null,
      location: d.location || d.address || null,
    };
  });

  // sort: status (eligible → soon → resting → deferred), then most-rested, then name
  const order = { ELIGIBLE: 0, SOON: 1, WAITING: 2, DEFERRED: 3 };
  rows.sort((a, b) =>
    (order[a.st.status] - order[b.st.status]) ||
    ((b.st.daysRested || 0) - (a.st.daysRested || 0)) ||
    a.d.name.localeCompare(b.d.name));

  // summary tallies (over the filtered set)
  const counts = { eligible: 0, soon: 0, ineligible: 0 };
  rows.forEach((r) => {
    if (r.st.status === 'ELIGIBLE') counts.eligible++;
    else if (r.st.status === 'SOON') counts.soon++;
    else counts.ineligible++; // WAITING + DEFERRED
  });

  // blood-group breakdown (ordered, only groups present)
  const byGroup = {};
  rows.forEach((r) => { byGroup[r.d.bloodGroup] = (byGroup[r.d.bloodGroup] || 0) + 1; });
  const groups = TC.BLOOD_GROUPS.filter((g) => byGroup[g]).map((g) => ({ g, n: byGroup[g] }));

  // which optional columns to render (only when at least one donor has data)
  const cols = {
    gender:   rows.some((r) => r.gender),
    age:      rows.some((r) => r.age != null || r.dob),
    email:    rows.some((r) => r.email),
    location: rows.some((r) => r.location),
  };

  const now = new Date();
  return {
    rows, counts, groups, cols,
    total: filtered.length,
    poolTotal: active.length,
    scope,
    patient,
    generatedAt: now,
  };
}

function dxScopeLabel(scope) {
  if (scope.type === 'eligible') return 'Eligible donors only';
  if (scope.type === 'groups' && scope.groups && scope.groups.length) return 'Blood groups: ' + scope.groups.join(', ');
  return 'All active donors';
}

/* Build an .xlsx workbook (Summary + Donors sheets) from the report model. */
function exportDonorWorkbook(model, fname) {
  const m = model;
  const dt = m.generatedAt;
  const stamp = dt.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' })
    + ' ' + dt.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' });

  // ---- Summary sheet ----
  const summaryRows = [
    ['MUIY — Donor Pool Report', ''],
    ['Generated', stamp],
    ['Scope', dxScopeLabel(m.scope)],
    ['Donors in report', m.total],
    ['Active pool total', m.poolTotal],
    ['', ''],
    ['Eligible now', m.counts.eligible],
    ['Due soon', m.counts.soon],
    ['Ineligible', m.counts.ineligible],
    ['', ''],
    ['Blood group', 'Donors'],
  ].concat(m.groups.map(({ g, n }) => [g, n]));

  // ---- Donors sheet (columns mirror the PDF, optional ones only when present) ----
  const header = ['#', 'Full name', 'Blood group'];
  if (m.cols.gender) header.push('Gender');
  if (m.cols.age) header.push('Date of birth', 'Age');
  header.push('Contact number');
  if (m.cols.email) header.push('Email');
  if (m.cols.location) header.push('Location');
  header.push('Last donation', 'Next eligible', 'Donations', 'Status', 'Notes / restrictions');

  const rows = m.rows.map((r, i) => {
    const sm = DX_STATUS[r.st.status] || DX_STATUS.WAITING;
    const row = [i + 1, r.d.name, r.d.bloodGroup];
    if (m.cols.gender) row.push(r.gender || '');
    if (m.cols.age) {
      row.push(r.dob ? TC.fmtDate(r.dob, { day: '2-digit', month: 'short', year: 'numeric' }) : '');
      row.push(r.age != null ? r.age : '');
    }
    row.push(r.d.mobile || '');
    if (m.cols.email) row.push(r.email || '');
    if (m.cols.location) row.push(r.location || '');
    row.push(r.last ? TC.fmtDate(r.last.date) : 'No donations');
    row.push(r.st.status === 'ELIGIBLE' ? 'Eligible now' : (r.next ? TC.fmtDate(r.next) : ''));
    row.push(r.count);
    row.push(sm.label + (r.st.status === 'DEFERRED' && r.st.reason ? ' — ' + r.st.reason : ''));
    row.push(r.d.notes || '');
    return row;
  });

  // column widths (chars) sized to the columns actually present
  const cols = [5, 26, 12];
  if (m.cols.gender) cols.push(9);
  if (m.cols.age) cols.push(14, 6);
  cols.push(18);
  if (m.cols.email) cols.push(26);
  if (m.cols.location) cols.push(22);
  cols.push(15, 15, 11, 16, 40);

  TCXLSX.download(fname, {
    sheets: [
      { name: 'Summary', cols: [22, 30], rows: summaryRows },
      { name: 'Donors', cols, header, rows },
    ],
  });
}

/* ------------------------------------------------------------
   3. PRINT DOCUMENT  (#donor-print-root, portalled to <body>)
   Colours are literal hex — the print root lives OUTSIDE the
   themed .screen scope, so CSS custom properties don't resolve.
   ------------------------------------------------------------ */
function DonorPrintDoc({ model }) {
  const m = model;
  const dt = m.generatedAt;
  const dateStr = dt.toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' });
  const timeStr = dt.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' });

  const cell = (v) => (v == null || v === '') ? <span className="dx-mut">—</span> : v;

  // report reference, e.g. DP-260621-2209
  const pad = (n) => String(n).padStart(2, '0');
  const ref = 'DP-' + String(dt.getFullYear()).slice(2) + pad(dt.getMonth() + 1) + pad(dt.getDate())
    + '-' + pad(dt.getHours()) + pad(dt.getMinutes());

  // distribution segments (proportional) for the slim group bar
  const distTotal = m.groups.reduce((a, x) => a + x.n, 0) || 1;
  const colSpanFull = 3 + (m.cols.gender ? 1 : 0) + (m.cols.age ? 1 : 0) + 1 +
    (m.cols.email ? 1 : 0) + (m.cols.location ? 1 : 0) + 4;

  return (
    <div id="donor-print-root">
      {/* repeating page footer */}
      <div className="dx-foot">
        <span className="dx-foot-l"><b>MUIY</b> Donor Pool Registry · {ref}</span>
        <span>Confidential — contains personal contact data</span>
      </div>

      {/* ---- masthead ---- */}
      <header className="dx-mast">
        <div className="dx-mast-id">
          <span className="dx-mark" aria-hidden="true"></span>
          <span className="dx-wordmark">MUIY</span>
          <span className="dx-mast-sep" aria-hidden="true"></span>
          <span className="dx-mast-kind">Blood Donor Registry</span>
        </div>
        <div className="dx-mast-ref">{ref}</div>
      </header>

      <div className="dx-rule" aria-hidden="true"></div>

      {/* ---- title + meta ---- */}
      <div className="dx-head">
        <div className="dx-head-l">
          <h1 className="dx-title">Donor Pool Report</h1>
          <p className="dx-sub">{dxScopeLabel(m.scope)}</p>
        </div>
        <dl className="dx-meta">
          <div><dt>Generated</dt><dd>{dateStr} · {timeStr}</dd></div>
          {m.patient && m.patient.name ? <div><dt>Patient</dt><dd>{m.patient.name}</dd></div> : null}
          <div><dt>Donors in report</dt><dd>{m.total}{m.total !== m.poolTotal ? ` of ${m.poolTotal}` : ''}</dd></div>
        </dl>
      </div>

      {/* ---- KPI band ---- */}
      <div className="dx-kpis">
        <div className="dx-kpi dx-kpi--lead">
          <span className="dx-kpi-n">{m.total}</span>
          <span className="dx-kpi-l">Total donors</span>
        </div>
        <div className="dx-kpi">
          <span className="dx-kpi-n"><i className="dx-dot dx-dot--ok" />{m.counts.eligible}</span>
          <span className="dx-kpi-l">Eligible now</span>
        </div>
        <div className="dx-kpi">
          <span className="dx-kpi-n"><i className="dx-dot dx-dot--soon" />{m.counts.soon}</span>
          <span className="dx-kpi-l">Due soon</span>
        </div>
        <div className="dx-kpi">
          <span className="dx-kpi-n"><i className="dx-dot dx-dot--off" />{m.counts.ineligible}</span>
          <span className="dx-kpi-l">Ineligible</span>
        </div>
      </div>

      {/* ---- blood-group distribution ---- */}
      {m.groups.length > 0 && (
        <section className="dx-block">
          <div className="dx-eyebrow">Blood group distribution</div>
          <div className="dx-bar" aria-hidden="true">
            {m.groups.map(({ g, n }, i) => (
              <span key={g} className="dx-bar-seg" style={{ width: (n / distTotal * 100) + '%', background: DX_BG_TINTS[i % DX_BG_TINTS.length] }} />
            ))}
          </div>
          <div className="dx-legend">
            {m.groups.map(({ g, n }, i) => (
              <span key={g} className="dx-leg">
                <i className="dx-leg-sw" style={{ background: DX_BG_TINTS[i % DX_BG_TINTS.length] }} />
                <b>{g}</b><span className="dx-leg-n">{n}</span>
              </span>
            ))}
          </div>
        </section>
      )}

      {/* ---- directory ---- */}
      <section className="dx-block">
        <div className="dx-eyebrow dx-eyebrow--table">
          Donor directory
          <span className="dx-eyebrow-meta">{m.rows.length} record{m.rows.length === 1 ? '' : 's'} · sorted by eligibility</span>
        </div>
        <table className="dx-table">
          <thead>
            <tr>
              <th className="dx-c-num">#</th>
              <th>Donor</th>
              <th className="dx-c-grp">Group</th>
              {m.cols.gender && <th>Sex</th>}
              {m.cols.age && <th>DOB / Age</th>}
              <th>Contact</th>
              {m.cols.email && <th>Email</th>}
              {m.cols.location && <th>Location</th>}
              <th>Last donation</th>
              <th>Next eligible</th>
              <th className="dx-c-cnt">Donations</th>
              <th className="dx-c-stat">Status</th>
            </tr>
          </thead>
          <tbody>
            {m.rows.map((r, i) => {
              const sm = DX_STATUS[r.st.status] || DX_STATUS.WAITING;
              return (
                <React.Fragment key={r.d.id}>
                  <tr className="dx-row">
                    <td className="dx-c-num dx-mut">{pad(i + 1)}</td>
                    <td className="dx-name">{r.d.name}</td>
                    <td className="dx-c-grp"><span className="dx-grp">{r.d.bloodGroup}</span></td>
                    {m.cols.gender && <td>{cell(r.gender)}</td>}
                    {m.cols.age && <td>{r.dob ? `${TC.fmtDate(r.dob, { day: '2-digit', month: 'short', year: 'numeric' })}${r.age != null ? ` · ${r.age}y` : ''}` : <span className="dx-mut">—</span>}</td>}
                    <td className="dx-mono">{cell(r.d.mobile)}</td>
                    {m.cols.email && <td className="dx-mono">{cell(r.email)}</td>}
                    {m.cols.location && <td>{cell(r.location)}</td>}
                    <td>{r.last ? TC.fmtDate(r.last.date) : <span className="dx-mut">No donations</span>}</td>
                    <td>{r.st.status === 'ELIGIBLE' ? <span className="dx-ok">Eligible now</span> : (r.next ? TC.fmtDate(r.next) : <span className="dx-mut">—</span>)}</td>
                    <td className="dx-c-cnt">{r.count}</td>
                    <td className="dx-c-stat"><span className="dx-stat-cell"><i className="dx-dot" style={{ background: sm.color }} />{sm.label}</span></td>
                  </tr>
                  {r.d.notes && (
                    <tr className="dx-noterow">
                      <td></td>
                      <td colSpan={colSpanFull - 1} className="dx-note">
                        <span className="dx-note-k">Notes</span> {r.d.notes}
                        {r.st.status === 'DEFERRED' && r.st.reason ? ` · Deferred: ${r.st.reason}` : ''}
                      </td>
                    </tr>
                  )}
                </React.Fragment>
              );
            })}
            {m.rows.length === 0 && (
              <tr><td colSpan={12} className="dx-empty">No donors match this selection.</td></tr>
            )}
          </tbody>
        </table>
      </section>

      <p className="dx-disclaimer">
        <b>End of report.</b> Generated by MUIY from the patient's own donor records for personal
        coordination of blood donations. This document contains private contact information — handle confidentially
        and dispose of securely. Not a clinical or laboratory record.
      </p>
    </div>
  );
}

/* ------------------------------------------------------------
   4. EXPORT SHEET — scope picker + offline PDF generation
   ------------------------------------------------------------ */
function ExportDonorsSheet({ store, close }) {
  const [scopeType, setScopeType] = React.useState('all'); // all | eligible | groups
  const [groups, setGroups] = React.useState([]);
  const [format, setFormat] = React.useState('pdf'); // pdf | xlsx
  const [busy, setBusy] = React.useState(false);

  const scope = { type: scopeType, groups };
  const model = React.useMemo(
    () => buildDonorReport(store.donors, store.patient, scope),
    [store.donors, store.patient, scopeType, groups.join(',')]
  );

  // groups present in the active pool, with counts (for the chip picker)
  const poolGroups = React.useMemo(() => {
    const by = {};
    (store.donors || []).filter((d) => !d.archived).forEach((d) => { by[d.bloodGroup] = (by[d.bloodGroup] || 0) + 1; });
    return TC.BLOOD_GROUPS.filter((g) => by[g]).map((g) => ({ g, n: by[g] }));
  }, [store.donors]);

  const toggleGroup = (g) => setGroups((s) => s.includes(g) ? s.filter((x) => x !== g) : [...s, g]);

  const count = model.total;
  const canGenerate = count > 0;

  const baseName = 'DonorPool_' + new Date().toISOString().slice(0, 10); // DonorPool_YYYY-MM-DD

  const generate = () => {
    if (!canGenerate || busy) return;
    if (format === 'xlsx') {
      setBusy(true);
      try {
        exportDonorWorkbook(model, baseName);
        store.showToast('Excel file downloaded', '📗');
      } catch (e) {
        store.showToast('Could not generate the Excel file', '⚠️');
      }
      setTimeout(() => { setBusy(false); close(); }, 400);
      return;
    }
    setBusy(true);
    const fname = baseName;
    const prevTitle = document.title;
    // The browser's print/save-as-PDF dialog seeds the filename from the
    // document title — set it so the saved file is DonorPool_YYYY-MM-DD.pdf
    document.title = fname;
    const restore = () => {
      document.title = prevTitle;
      window.removeEventListener('afterprint', restore);
      setBusy(false);
    };
    window.addEventListener('afterprint', restore);
    // give the just-set title + portalled doc a beat to settle, then print
    setTimeout(() => {
      try { window.print(); } catch (e) { /* no-op */ }
      // safety net if afterprint never fires (some mobile browsers)
      setTimeout(() => { if (document.title === fname) restore(); }, 1200);
    }, 60);
  };

  const SCOPES = [
    { k: 'all', t: 'All donors', d: 'Every active donor in your pool' },
    { k: 'eligible', t: 'Eligible only', d: 'Donors who can give right now' },
    { k: 'groups', t: 'Choose blood groups', d: 'Pick one or more groups' },
  ];

  return (
    <Sheet title="Export donor pool" sub="Print-ready PDF or an Excel workbook — both work fully offline." onClose={close}>
      <div className="dx-fmt">
        {[{ k: 'pdf', t: 'PDF report', i: 'clipboard' }, { k: 'xlsx', t: 'Excel (.xlsx)', i: 'grid' }].map((o) => (
          <button
            key={o.k}
            className={'dx-fmt-btn' + (format === o.k ? ' on' : '')}
            onClick={() => setFormat(o.k)}
          >
            <Icon name={o.i} size={16} color={format === o.k ? 'var(--orange)' : 'var(--ink-3)'} />
            {o.t}
          </button>
        ))}
      </div>
      <div className="dx-opts">
        {SCOPES.map((s) => (
          <button
            key={s.k}
            className={'dx-opt' + (scopeType === s.k ? ' on' : '')}
            onClick={() => setScopeType(s.k)}
          >
            <span className="dx-opt-radio" aria-hidden="true"></span>
            <span className="dx-opt-txt">
              <span className="dx-opt-t">{s.t}</span>
              <span className="dx-opt-d">{s.d}</span>
            </span>
          </button>
        ))}
      </div>

      {scopeType === 'groups' && (
        <div className="dx-groups">
          {poolGroups.length === 0
            ? <div className="dx-groups-empty">No donors in the pool yet.</div>
            : poolGroups.map(({ g, n }) => (
                <button
                  key={g}
                  className={'chip' + (groups.includes(g) ? ' on' : '')}
                  onClick={() => toggleGroup(g)}
                >{g} <span style={{ opacity: 0.6, fontWeight: 700 }}>· {n}</span></button>
              ))}
        </div>
      )}

      <div className="dx-preview">
        <div className="dx-preview-row">
          <span>In this export</span>
          <b>{count} donor{count === 1 ? '' : 's'}</b>
        </div>
        <div className="dx-preview-stats">
          <span><b style={{ color: 'var(--eligible)' }}>{model.counts.eligible}</b> eligible</span>
          <span><b style={{ color: 'var(--soon)' }}>{model.counts.soon}</b> due soon</span>
          <span><b style={{ color: 'var(--ink-2)' }}>{model.counts.ineligible}</b> ineligible</span>
        </div>
        <div className="dx-preview-file">
          <Icon name="download" size={13} color="var(--ink-3)" />
          {baseName}.{format === 'xlsx' ? 'xlsx' : 'pdf'}
        </div>
      </div>

      <div className="row" style={{ gap: 10, marginTop: 4 }}>
        <button className="btn btn-ghost" style={{ flex: 1 }} onClick={close}>Cancel</button>
        <button
          className="btn btn-primary"
          style={{ flex: 1.5, gap: 8, opacity: canGenerate ? 1 : 0.5 }}
          onClick={generate}
          disabled={!canGenerate}
        >
          <Icon name="download" size={17} color="#fff" />
          {busy ? 'Preparing…' : (format === 'xlsx' ? 'Download Excel' : 'Generate PDF')}
        </button>
      </div>

      {/* print-only document, portalled to <body> so the print stylesheet can
          show it cleanly outside the scaled phone frame */}
      {canGenerate && ReactDOM.createPortal(<DonorPrintDoc model={model} />, document.body)}
    </Sheet>
  );
}

Object.assign(window, { SyncBadge, buildDonorReport, DonorPrintDoc, ExportDonorsSheet });
