// ═══════════════════════════════════════════════════════════════
// inventory-lenses.jsx — the lens tools (Inventory Step 7)
// ═══════════════════════════════════════════════════════════════
//
// WHAT THIS IS (in plain English):
// Its own "Lenses" tab in the Stock Room, with the four things that only
// lenses need:
//
//   Tomorrow's pull — the day before surgery, someone takes the lenses off
//   the shelf. SurgSuite already set aside a specific lens for each eye when
//   the worklist came in, so this is a checklist: scan each box as you pull
//   it, and anything that doesn't belong gets flagged. Two boxes per eye is
//   normal (the calculated lens plus a backup power), so a second box of the
//   same lens is accepted quietly. When it's all covered you mark it
//   verified and it locks, with your name and the time on it.
//
//   Shelf audit — scan every lens on the shelf; get back what matched, what
//   is on the shelf but unknown to the system, and what the system thinks it
//   has but nobody scanned. Fixes are one click each (or all at once) and
//   every one writes a proper stock line saying it came from the audit, so
//   the history always explains itself.
//
//   Returns — take a lens out of stock with a reason or tracking number,
//   optionally tied to a patient and eye (that is how a STAAR backup going
//   back gets recorded), and generate the vendor's own return form.
//
//   Consigned lenses — the lenses a vendor sent for one named patient:
//   which serial went in, which went back, which is still in the drawer.
//   Nothing is stored: the status is read off the stock history.
// ═══════════════════════════════════════════════════════════════

const invTomorrow = () => {
  const d = new Date(invToday() + 'T12:00:00');
  d.setDate(d.getDate() + 1);
  return d.toLocaleDateString('en-CA');
};

// Serial number → the lens it belongs to, for everything currently in stock.
// A lens box barcode carries the product code as well as the serial, but a
// serial typed in by hand (or read off the sticker) carries neither — and the
// system already knows which lens that serial came in as, so it should not
// have to be told.
function invSerialIndex(onHand) {
  const m = new Map();
  for (const [pid, bal] of Object.entries((onHand && onHand.products) || {})) {
    for (const lot of bal.lots || []) if (lot.serial && lot.onHand > 0) m.set(invNorm(lot.serial), pid);
  }
  return m;
}

// ─── A scan box that records every scan, recognized or not ───────
// Anything the catalog knows comes back with its product; anything it does
// not is still recorded, because "on the shelf but unknown to the system" is
// exactly the finding an audit exists to make.
function InvLensScanBox({ products, onScan, placeholder, autoFocus, disabled, serialIndex }) {
  const [q, setQ] = invUseState('');
  const submit = () => {
    const raw = q.trim();
    if (!raw) return;
    const found = invFindByScan(products, raw);
    const scan = found.scan || {};
    const serial = scan.serial || (scan.gtin && scan.gtin !== raw ? '' : raw);
    let product = found.product;
    if (!product && serial && serialIndex) {
      const pid = serialIndex.get(invNorm(serial));
      if (pid) product = products.find((p) => p.id === pid) || null;
    }
    onScan({
      raw,
      serial,
      lot: scan.lot || '',
      expiry: scan.expiry || '',
      gtin: scan.gtin || '',
      productId: product ? product.id : '',
      description: product ? product.description : '',
    });
    setQ('');
  };
  return (
    <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
      <input type="text" value={q} autoFocus={autoFocus} disabled={disabled}
        onChange={(e) => setQ(e.target.value)}
        onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); submit(); } }}
        placeholder={placeholder || 'Scan a lens box…'} style={{ minWidth: 300, flex: '0 1 380px' }} />
      <button className="btn btn-outline btn-sm" disabled={disabled || !q.trim()} onClick={submit}>Add</button>
    </div>
  );
}

// ─── Tomorrow's lens pull ────────────────────────────────────────
function InvLensPull({ stores, savedOrConflict, toast, me, refreshAll, onHand }) {
  const P = stores.products.data || [];
  const PULLS = stores.lens_pulls.data || [];
  const serialIndex = invUseMemo(() => invSerialIndex(onHand), [onHand.products]);
  const [date, setDate] = invUseState(invTomorrow);
  const [scans, setScans] = invUseState([]);
  const [result, setResult] = invUseState(null);
  const [busy, setBusy] = invUseState(false);
  const record = PULLS.find((r) => r.date === date) || null;
  const locked = !!(record && record.locked);

  const run = invUseCallback(async (nextScans) => {
    setBusy(true);
    try { setResult(await InvApi.lensPull({ date, scans: nextScans })); }
    catch (e) { toast('⚠️ ' + e.message, 'error'); }
    setBusy(false);
  }, [date, toast]);

  // Reload whenever the day changes, picking up whatever was already scanned
  // on this date so two people (or one person after a break) see one list.
  invUseEffect(() => {
    const saved = (stores.lens_pulls.data || []).find((r) => r.date === date);
    const s = saved ? (saved.scans || []) : [];
    setScans(s); run(s);
  }, [date, stores.lens_pulls.version]);

  const persistScans = async (next) => {
    setScans(next);
    await run(next);
    const rest = PULLS.filter((r) => r.date !== date);
    const rec = { ...(record || {}), date, scans: next, updatedAt: new Date().toISOString(), updatedBy: (me && (me.name || me.email)) || '' };
    await savedOrConflict(stores.lens_pulls, [...rest, rec]);
  };
  const addScan = (s) => { if (!locked) persistScans([...scans, s]); };
  const removeScan = (i) => { if (!locked) persistScans(scans.filter((_, idx) => idx !== i)); };

  // A serial the system has never seen is nearly always a consigned lens that
  // arrived without being received — so offer to receive it right here.
  const receiveSerial = async (scan) => {
    const p = P.find((x) => x.id === scan.productId);
    if (!p) return;
    try {
      await invPostLines([{
        type: 'received', productId: p.id, qty: 1, serial: scan.serial, lot: scan.lot || '', expiry: scan.expiry || '',
        shelf: p.shelf || 'IOL', unitCost: Number(p.purchasePrice) || 0, uom: p.dispenseUom,
        note: `Received at the lens pull for ${date}`, ref: { kind: 'other', id: 'lens-pull' },
      }]);
      toast(`${p.description} — serial ${scan.serial} received into stock`);
      refreshAll && refreshAll();
      run(scans);
    } catch (e) { toast('⚠️ ' + e.message, 'error'); }
  };

  const setLock = async (lock) => {
    const rest = PULLS.filter((r) => r.date !== date);
    const now = new Date().toISOString();
    const rec = {
      ...(record || {}), date, scans, locked: lock,
      verifiedBy: lock ? ((me && (me.name || me.email)) || '') : '', verifiedAt: lock ? now : '',
      summary: lock && result ? { expected: result.slots.length, covered: result.slots.filter((s) => s.matched).length } : (record && record.summary) || null,
      updatedAt: now, updatedBy: (me && (me.name || me.email)) || '',
    };
    if (await savedOrConflict(stores.lens_pulls, [...rest, rec], lock ? 'Lens pull verified and locked' : 'Lens pull reopened')) {
      InvApi.audit('INV_LENS_PULL', `${lock ? 'Verified' : 'Reopened'} the lens pull for ${date}`);
    }
  };

  const slots = (result && result.slots) || [];
  const problems = (result && result.problems) || { notNeeded: [], extra: [], unknown: [], serialNotInStock: [] };
  const covered = slots.filter((s) => s.matched).length;

  return (
    <div>
      <p style={{ fontSize: '0.85rem', color: 'var(--gray-600)', maxWidth: 760 }}>
        The lenses set aside for a day's cases, from the Dispensary. Scan each box as you pull it. A second box of the same lens is taken as the backup; anything else is flagged.
      </p>
      <div style={{ display: 'flex', gap: 12, alignItems: 'flex-end', flexWrap: 'wrap', marginBottom: 12 }}>
        <InvField label="Surgery date"><input type="date" value={date} onChange={(e) => setDate(e.target.value)} /></InvField>
        <div style={{ paddingBottom: 6, fontSize: '0.85rem', color: 'var(--gray-600)' }}>
          {busy ? 'Checking…' : `${covered} of ${slots.length} lenses accounted for`}
        </div>
        <span style={{ flex: 1 }} />
        {locked
          ? <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
              <InvTag color="var(--green)">✓ verified &amp; locked</InvTag>
              <span style={{ fontSize: '0.78rem', color: 'var(--gray-500)' }}>{record.verifiedBy}{record.verifiedAt ? ' · ' + new Date(record.verifiedAt).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : ''}</span>
              <button className="btn btn-outline btn-sm" onClick={() => setLock(false)}>Reopen</button>
            </div>
          : <button className="btn btn-primary btn-sm" disabled={!result || !result.complete} title={result && !result.complete ? 'Everything has to be accounted for first' : ''} onClick={() => setLock(true)}>✓ Mark verified</button>}
      </div>

      {!locked && <div style={{ marginBottom: 12 }}><InvLensScanBox products={P} serialIndex={serialIndex} onScan={addScan} autoFocus placeholder="Scan the lens box you just pulled…" /></div>}

      {slots.length === 0 && !busy && (
        <div className="card" style={{ maxWidth: 640 }}><div className="card-body" style={{ color: 'var(--gray-500)', fontSize: '0.86rem' }}>
          No lenses are set aside for {date}. The Dispensary fills this in when the worklist for that day is imported.
        </div></div>
      )}

      {slots.length > 0 && (
        <div className="card" style={{ margin: '0 0 12px' }}>
          <div className="tbl-wrap" style={{ overflowX: 'auto' }}>
            <table style={{ width: '100%', borderCollapse: 'collapse' }}>
              <thead><tr><th style={invTh} /><th style={invTh}>Patient</th><th style={invTh}>Eye</th><th style={invTh}>Lens</th><th style={invTh}>Scanned</th></tr></thead>
              <tbody>
                {slots.map((s) => (
                  <tr key={s.key}>
                    <td style={{ ...invCell, width: 28, fontSize: '1rem' }}>{s.matched ? <span style={{ color: 'var(--green)' }}>✓</span> : <span style={{ color: 'var(--gray-400)' }}>○</span>}</td>
                    <td style={invCell}>{s.patientName || s.patientId || '—'}</td>
                    <td style={invCell}>{s.eye || '—'}</td>
                    {/* the catalog description already carries the power, so don't print it twice */}
                    <td style={invCell}>{s.description}{s.iolPower && !invNorm(s.description).includes(invNorm(s.iolPower)) ? <span style={{ color: 'var(--gray-500)' }}> {s.iolPower}</span> : null}</td>
                    <td style={{ ...invCell, fontSize: '0.78rem', color: 'var(--gray-600)' }}>{s.serials.length ? s.serials.map((x) => 'SN ' + x).join(', ') : '—'}{s.scanned > s.qty ? ` (+${s.scanned - s.qty} backup)` : ''}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      )}

      <InvReconSection title="Correct lens, but that serial isn't in stock" color="var(--amber)" count={problems.serialNotInStock.length}>
        {problems.serialNotInStock.map((s, i) => (
          <tr key={i}>
            <td style={invCell}>{s.description || s.raw}<span style={{ color: 'var(--gray-500)', fontSize: '0.78rem' }}> SN {s.serial}</span></td>
            <td style={invCell}>{s.productId ? <button className="btn btn-sm" disabled={locked} onClick={() => receiveSerial(s)}>Receive it into stock</button> : <span style={{ color: 'var(--gray-500)', fontSize: '0.78rem' }}>unknown lens</span>}</td>
          </tr>
        ))}
      </InvReconSection>
      <InvReconSection title="Not needed for this day" color="var(--red)" count={problems.notNeeded.length}>
        {problems.notNeeded.map((s, i) => <tr key={i}><td style={invCell}>{s.description || s.raw}{s.serial ? <span style={{ color: 'var(--gray-500)', fontSize: '0.78rem' }}> SN {s.serial}</span> : null}</td></tr>)}
      </InvReconSection>
      <InvReconSection title="More boxes than this day needs" color="var(--amber)" count={problems.extra.length}>
        {problems.extra.map((s, i) => <tr key={i}><td style={invCell}>{s.description || s.raw}{s.serial ? <span style={{ color: 'var(--gray-500)', fontSize: '0.78rem' }}> SN {s.serial}</span> : null}</td></tr>)}
      </InvReconSection>
      <InvReconSection title="Barcode not recognized" color="var(--red)" count={problems.unknown.length}>
        {problems.unknown.map((s, i) => <tr key={i}><td style={invCell}>{s.raw}</td></tr>)}
      </InvReconSection>

      {scans.length > 0 && !locked && (
        <div style={{ marginTop: 14 }}>
          <div style={{ fontWeight: 600, fontSize: '0.8rem', color: 'var(--gray-600)' }}>Scanned so far ({scans.length})</div>
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginTop: 6 }}>
            {scans.map((s, i) => (
              <span key={i} style={{ fontSize: '0.74rem', border: '1px solid var(--gray-300)', borderRadius: 6, padding: '2px 6px' }}>
                {s.serial || s.raw} <button className="btn btn-sm" style={{ padding: '0 4px' }} onClick={() => removeScan(i)} title="Remove this scan">✕</button>
              </span>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

// ─── The shelf audit ─────────────────────────────────────────────
function InvLensAudit({ stores, savedOrConflict, toast, me, refreshAll, onHand }) {
  const P = stores.products.data || [];
  const serialIndex = invUseMemo(() => invSerialIndex(onHand), [onHand.products]);
  const [scans, setScans] = invUseState([]);
  const [result, setResult] = invUseState(null);
  const [busy, setBusy] = invUseState(false);

  const check = async (list) => {
    setBusy(true);
    try { setResult(await InvApi.lensAudit(list || scans)); }
    catch (e) { toast('⚠️ ' + e.message, 'error'); }
    setBusy(false);
  };
  const addScan = (s) => { const next = [...scans, s]; setScans(next); if (result) check(next); };

  const addToStock = async (rows) => {
    const lines = [];
    for (const r of rows) {
      const p = P.find((x) => x.id === r.scan.productId);
      if (!p) continue;
      lines.push({ type: 'received', productId: p.id, qty: 1, serial: r.scan.serial, lot: r.scan.lot || '', expiry: r.scan.expiry || '', shelf: p.shelf || 'IOL', unitCost: Number(p.purchasePrice) || 0, uom: p.dispenseUom, note: `Found in the lens shelf audit ${invToday()}`, ref: { kind: 'count', id: 'lens-audit' } });
    }
    if (!lines.length) { toast('⚠️ Those barcodes are not in the catalog, so there is nothing to add yet.', 'error'); return; }
    try { await invPostLines(lines); toast(`${lines.length} lens(es) added to stock`); refreshAll && refreshAll(); check(); }
    catch (e) { toast('⚠️ ' + e.message, 'error'); }
  };
  const removeFromStock = async (rows) => {
    const lines = rows.map((s) => ({ type: 'adjusted', productId: s.productId, qty: -s.onHand, serial: s.serial || '', lot: s.lot || '', shelf: s.shelf || '', note: `Not found in the lens shelf audit ${invToday()}`, ref: { kind: 'count', id: 'lens-audit' } }));
    if (!lines.length) return;
    try { await invPostLines(lines); toast(`${lines.length} lens(es) taken out of stock`); refreshAll && refreshAll(); check(); }
    catch (e) { toast('⚠️ ' + e.message, 'error'); }
  };

  const saveAudit = async () => {
    if (!result) return;
    const AUD = stores.lens_audits.data || [];
    const rec = {
      id: invId('AUD'), at: new Date().toISOString(), by: (me && (me.name || me.email)) || '',
      counts: result.counts,
      scannedNotInSystem: result.scannedNotInSystem.map((r) => ({ serial: r.scan.serial, raw: r.scan.raw, description: r.scan.description || '' })),
      inSystemNotScanned: result.inSystemNotScanned.map((s) => ({ serial: s.serial, description: s.description, onHand: s.onHand })),
    };
    if (await savedOrConflict(stores.lens_audits, [rec, ...AUD], 'Audit saved')) { setScans([]); setResult(null); }
  };

  const AUD = (stores.lens_audits.data || []).slice(0, 5);
  return (
    <div>
      <p style={{ fontSize: '0.85rem', color: 'var(--gray-600)', maxWidth: 760 }}>
        Scan every lens on the shelf, then check the shelf against the system. Nothing changes until you say so, and each correction is written into the stock history with the audit's date on it.
      </p>
      <div style={{ marginBottom: 10 }}><InvLensScanBox products={P} serialIndex={serialIndex} onScan={addScan} autoFocus placeholder="Scan a lens from the shelf…" /></div>
      <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap', marginBottom: 12 }}>
        <button className="btn btn-teal" disabled={busy || !scans.length} onClick={() => check()}>Check {scans.length} scanned lens(es) against the system</button>
        {scans.length > 0 && <button className="btn btn-outline btn-sm" onClick={() => { setScans([]); setResult(null); }}>Start over</button>}
      </div>

      {result && (
        <div>
          {result.clean && <div style={{ color: 'var(--green)', fontWeight: 600, marginBottom: 10 }}>✓ The shelf matches the system exactly.</div>}
          <InvReconSection title="Matched" color="var(--green)" count={result.matched.length}>
            {result.matched.map((r, i) => <tr key={i}><td style={invCell}>{r.stock.description}</td><td style={{ ...invCell, fontSize: '0.78rem', color: 'var(--gray-600)' }}>SN {r.stock.serial}</td></tr>)}
          </InvReconSection>
          <InvReconSection title="On the shelf, not in the system" color="var(--amber)" count={result.scannedNotInSystem.length}>
            {result.scannedNotInSystem.map((r, i) => (
              <tr key={i}>
                <td style={invCell}>{r.scan.description || <span style={{ color: 'var(--gray-500)' }}>not in the catalog</span>}</td>
                <td style={{ ...invCell, fontSize: '0.78rem', color: 'var(--gray-600)' }}>SN {r.scan.serial || r.scan.raw}</td>
                <td style={invCell}>{r.scan.productId && <button className="btn btn-sm" onClick={() => addToStock([r])}>Add to stock</button>}</td>
              </tr>
            ))}
          </InvReconSection>
          {result.scannedNotInSystem.filter((r) => r.scan.productId).length > 1 &&
            <div style={{ marginTop: 6 }}><button className="btn btn-outline btn-sm" onClick={() => addToStock(result.scannedNotInSystem.filter((r) => r.scan.productId))}>Add all {result.scannedNotInSystem.filter((r) => r.scan.productId).length} to stock</button></div>}
          <InvReconSection title="In the system, not on the shelf" color="var(--red)" count={result.inSystemNotScanned.length}>
            {result.inSystemNotScanned.map((s, i) => (
              <tr key={i}>
                <td style={invCell}>{s.description}</td>
                <td style={{ ...invCell, fontSize: '0.78rem', color: 'var(--gray-600)' }}>{s.serial ? 'SN ' + s.serial : s.lot ? 'lot ' + s.lot : '—'} · {invFmtQty(s.onHand)} on hand</td>
                <td style={invCell}><button className="btn btn-sm" onClick={() => { if (window.confirm(`Take ${s.description} out of stock? Only do this if it is truly not on the shelf.`)) removeFromStock([s]); }}>Take out of stock</button></td>
              </tr>
            ))}
          </InvReconSection>
          {result.inSystemNotScanned.length > 1 &&
            <div style={{ marginTop: 6 }}><button className="btn btn-outline btn-sm" onClick={() => { if (window.confirm(`Take all ${result.inSystemNotScanned.length} out of stock? Only do this if they are truly not on the shelf.`)) removeFromStock(result.inSystemNotScanned); }}>Take all {result.inSystemNotScanned.length} out of stock</button></div>}
          <div style={{ marginTop: 16 }}><button className="btn btn-primary" onClick={saveAudit}>Save this audit</button></div>
        </div>
      )}

      {AUD.length > 0 && (
        <div style={{ marginTop: 20 }}>
          <div style={{ fontWeight: 700, fontSize: '0.8rem', color: 'var(--gray-600)', textTransform: 'uppercase' }}>Past audits</div>
          <div className="card" style={{ margin: '6px 0 0' }}>
            <table style={{ width: '100%', borderCollapse: 'collapse' }}>
              <tbody>
                {AUD.map((a) => (
                  <tr key={a.id}>
                    <td style={{ ...invCell, whiteSpace: 'nowrap' }}>{new Date(a.at).toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit' })}</td>
                    <td style={{ ...invCell, fontSize: '0.8rem' }}>{a.counts.scanned} scanned · {a.counts.matched} matched · {a.counts.scannedNotInSystem} not in the system · {a.counts.inSystemNotScanned} not on the shelf</td>
                    <td style={{ ...invCell, fontSize: '0.78rem', color: 'var(--gray-600)' }}>{a.by}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      )}
    </div>
  );
}

// ─── Returns: take a lens out of stock, and the vendor's own form ─
function InvLensReturns({ stores, savedOrConflict, toast, me, onHand, refreshAll }) {
  const P = stores.products.data || [];
  const S = stores.settings.data || {};
  const lensStock = invUseMemo(() => {
    const out = [];
    for (const p of P) {
      if (!p.isIol || p.active === false) continue;
      const bal = onHand.products[p.id];
      if (!bal) continue;
      for (const lot of bal.lots) if (lot.onHand > 0) out.push({ product: p, lot });
    }
    return out.sort((a, b) => a.product.description.localeCompare(b.product.description));
  }, [P, onHand.products]);

  // ── take one lens out of stock ──
  const [q, setQ] = invUseState('');
  const [picked, setPicked] = invUseState(null);
  const [reason, setReason] = invUseState('');
  const [linkPatient, setLinkPatient] = invUseState('');
  const [linkEye, setLinkEye] = invUseState('');
  const [busy, setBusy] = invUseState(false);
  const matches = invUseMemo(() => {
    const n = invNorm(q);
    if (n.length < 2) return [];
    return lensStock.filter((r) => invNorm(r.product.description).includes(n) || invNorm(r.lot.serial).includes(n) || invNorm(r.lot.lot).includes(n)).slice(0, 12);
  }, [q, lensStock]);

  const removeLens = async () => {
    if (!picked || !reason.trim()) return;
    setBusy(true);
    try {
      await invPostLines([{
        type: 'returned', productId: picked.product.id, qty: picked.lot.onHand, serial: picked.lot.serial || '', lot: picked.lot.lot || '',
        shelf: picked.lot.shelf || '', unitCost: picked.lot.unitCost, uom: picked.product.dispenseUom,
        note: reason.trim(),
        ref: { kind: 'return', id: 'lens-return', ...(linkPatient ? { patientId: linkPatient } : {}), ...(linkEye ? { eye: linkEye } : {}) },
      }]);
      InvApi.audit('INV_LENS_RETURN', `${picked.product.description} SN ${picked.lot.serial} out of stock — ${reason.trim()}`);
      toast(`${picked.product.description} taken out of stock`);
      setPicked(null); setReason(''); setLinkPatient(''); setLinkEye(''); setQ('');
      refreshAll && refreshAll();
    } catch (e) { toast('⚠️ ' + e.message, 'error'); }
    setBusy(false);
  };

  // ── the vendor's return form ──
  const FORMS = (window.SurgSuiteLensForms || {}).LENS_RETURN_FORMS || {};
  const [vendor, setVendor] = invUseState('jnj_expired');
  const cfg = FORMS[vendor];
  const defaults = (S.lensFormDefaults || {})[vendor] || {};
  const [practice, setPractice] = invUseState(defaults);
  const [formRows, setFormRows] = invUseState([]);
  const [formQ, setFormQ] = invUseState('');
  const [showAllMakers, setShowAllMakers] = invUseState(false);
  invUseEffect(() => { setPractice(((stores.settings.data || {}).lensFormDefaults || {})[vendor] || {}); setFormRows([]); setShowAllMakers(false); }, [vendor]);
  // A vendor's form only covers that vendor's own lenses, so the search offers
  // theirs and says how many it is holding back.
  const vendorMatch = (p) => window.SurgSuiteLensForms.lensFormVendorMatch(vendor, p);
  const formSearch = invUseMemo(() => {
    const n = invNorm(formQ);
    if (n.length < 2) return { list: [], hidden: 0 };
    const all = lensStock.filter((r) => invNorm(r.product.description).includes(n) || invNorm(r.lot.serial).includes(n));
    const mine = all.filter((r) => vendorMatch(r.product) !== false);
    return { list: (showAllMakers ? all : mine).slice(0, 12), hidden: showAllMakers ? 0 : all.length - mine.length };
  }, [formQ, lensStock, showAllMakers, vendor]);
  const formMatches = formSearch.list;
  const mismatched = formRows.filter((r) => r._vendorMismatch);

  const addFormRow = (r) => {
    if (!cfg || formRows.length >= cfg.maxRows) { toast(`⚠️ ${cfg.label} takes at most ${cfg.maxRows} lenses on one sheet.`, 'error'); return; }
    setFormRows([...formRows, {
      ...window.SurgSuiteLensForms.lensFormRowFromStock(vendor, r.product, r.lot),
      _productId: r.product.id, _serial: r.lot.serial, _lot: r.lot.lot, _shelf: r.lot.shelf, _onHand: r.lot.onHand, _unitCost: r.lot.unitCost, _fromStock: true,
      _maker: r.product.manufacturer || '', _vendorMismatch: vendorMatch(r.product) === false,
    }]);
    setFormQ('');
  };
  const generate = async () => {
    if (!cfg || !formRows.length) return;
    if (mismatched.length && !window.confirm(`${mismatched.length} lens(es) on this sheet were not made by ${cfg.label.split('—')[0].trim()} (${[...new Set(mismatched.map((r) => r._maker))].join(', ')}). A vendor will usually send the whole form back. Generate it anyway?`)) return;
    setBusy(true);
    try {
      window.SurgSuiteLensForms.lensFormGenerate(vendor, { practice, date: invToday(), lenses: formRows });
      // the practice's own details are the same every time — remember them
      const nextSettings = { ...S, lensFormDefaults: { ...(S.lensFormDefaults || {}), [vendor]: practice } };
      await savedOrConflict(stores.settings, nextSettings);
      // lenses that came off the shelf go out of stock with the form
      const lines = formRows.filter((r) => r._fromStock && r._productId).map((r) => ({
        type: 'returned', productId: r._productId, qty: r._onHand || 1, serial: r._serial || '', lot: r._lot || '', shelf: r._shelf || '',
        unitCost: r._unitCost, note: `${cfg.label} — returned on the vendor form`, ref: { kind: 'return', id: vendor },
      }));
      if (lines.length) { await invPostLines(lines); refreshAll && refreshAll(); }
      toast(`${cfg.label} generated${lines.length ? ` — ${lines.length} lens(es) taken out of stock` : ''}`);
      setFormRows([]);
    } catch (e) { toast('⚠️ ' + e.message, 'error'); }
    setBusy(false);
  };

  return (
    <div>
      <div className="card" style={{ margin: '0 0 16px' }}>
        <div className="card-header">Take a lens out of stock</div>
        <div className="card-body">
          <p style={{ fontSize: '0.84rem', color: 'var(--gray-600)', marginTop: 0 }}>
            For a lens going back to the vendor, borrowed by a rep, or otherwise leaving the shelf. Tie it to a patient and eye when it is a STAAR backup going back — that is what lets an invoice billing it afterwards be caught.
          </p>
          {picked ? (
            <div>
              <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginBottom: 10, flexWrap: 'wrap' }}>
                <strong>{picked.product.description}</strong>
                <span style={{ color: 'var(--gray-600)', fontSize: '0.82rem' }}>{picked.lot.serial ? 'SN ' + picked.lot.serial : picked.lot.lot ? 'lot ' + picked.lot.lot : ''} · {invFmtQty(picked.lot.onHand)} on hand</span>
                <button className="btn btn-sm" onClick={() => setPicked(null)}>Change</button>
              </div>
              <InvField label="Tracking number or reason"><input type="text" value={reason} onChange={(e) => setReason(e.target.value)} placeholder="e.g. shipped back to STAAR, tracking 1Z999… — or 'rep borrowed for demo'" /></InvField>
              <div className="field-row c2" style={{ margin: 0 }}>
                <InvField label="Patient (optional — for a STAAR backup going back)"><input type="text" value={linkPatient} onChange={(e) => setLinkPatient(e.target.value)} placeholder="name or MRN" /></InvField>
                <InvField label="Eye"><select value={linkEye} onChange={(e) => setLinkEye(e.target.value)}><option value="">—</option><option value="OD">OD</option><option value="OS">OS</option></select></InvField>
              </div>
              <button className="btn btn-primary" disabled={busy || !reason.trim()} onClick={removeLens}>Take it out of stock</button>
            </div>
          ) : (
            <div style={{ position: 'relative', maxWidth: 460 }}>
              <input type="text" value={q} onChange={(e) => setQ(e.target.value)} placeholder="Search the lenses on hand by name, serial or lot…" />
              {matches.length > 0 && (
                <div className="card" style={{ margin: '4px 0 0', position: 'absolute', zIndex: 6, width: '100%', maxHeight: 240, overflowY: 'auto' }}>
                  {matches.map((r, i) => (
                    <div key={i} onClick={() => { setPicked(r); setQ(''); }} style={{ padding: 8, cursor: 'pointer', borderBottom: '1px solid var(--gray-200)', fontSize: '0.84rem' }}>
                      <div style={{ fontWeight: 600 }}>{r.product.description}</div>
                      <div style={{ color: 'var(--gray-500)', fontSize: '0.74rem' }}>{r.lot.serial ? 'SN ' + r.lot.serial : r.lot.lot ? 'lot ' + r.lot.lot : 'no serial'} · {r.lot.shelf || 'IOL'}</div>
                    </div>
                  ))}
                </div>
              )}
            </div>
          )}
        </div>
      </div>

      <div className="card" style={{ margin: 0 }}>
        <div className="card-header">Vendor return form</div>
        <div className="card-body">
          <p style={{ fontSize: '0.84rem', color: 'var(--gray-600)', marginTop: 0 }}>
            Each vendor wants their own certificate of destruction. Your practice details are remembered per vendor. Generating the form also takes those lenses out of stock.
          </p>
          <InvField label="Vendor form">
            <select value={vendor} onChange={(e) => setVendor(e.target.value)}>
              {Object.entries(FORMS).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
            </select>
          </InvField>
          {cfg && (
            <div>
              <div className="field-row c3" style={{ margin: '0 0 6px' }}>
                {cfg.practiceFields.map(([k, label]) => (
                  <InvField key={k} label={label}><input type="text" value={practice[k] || ''} onChange={(e) => setPractice({ ...practice, [k]: e.target.value })} /></InvField>
                ))}
              </div>
              <div style={{ position: 'relative', maxWidth: 460, marginBottom: 8 }}>
                <input type="text" value={formQ} onChange={(e) => setFormQ(e.target.value)} placeholder={`Add a lens to the form (${formRows.length}/${cfg.maxRows})…`} />
                {formMatches.length > 0 && (
                  <div className="card" style={{ margin: '4px 0 0', position: 'absolute', zIndex: 6, width: '100%', maxHeight: 240, overflowY: 'auto' }}>
                    {formMatches.map((r, i) => (
                      <div key={i} onClick={() => addFormRow(r)} style={{ padding: 8, cursor: 'pointer', borderBottom: '1px solid var(--gray-200)', fontSize: '0.84rem' }}>
                        <div style={{ fontWeight: 600 }}>{r.product.description}{vendorMatch(r.product) === false && <span style={{ color: 'var(--red)', fontWeight: 400 }}> — {r.product.manufacturer}, not this vendor</span>}</div>
                        <div style={{ color: 'var(--gray-500)', fontSize: '0.74rem' }}>{r.lot.serial ? 'SN ' + r.lot.serial : 'no serial'}{r.lot.expiry ? ' · expires ' + r.lot.expiry : ''}</div>
                      </div>
                    ))}
                  </div>
                )}
              </div>
              <div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap', marginBottom: 8, fontSize: '0.78rem', color: 'var(--gray-600)' }}>
                <label style={{ display: 'flex', gap: 5, alignItems: 'center' }}>
                  <input type="checkbox" checked={showAllMakers} onChange={(e) => setShowAllMakers(e.target.checked)} />
                  Show lenses from every maker
                </label>
                {formSearch.hidden > 0 && <span>{formSearch.hidden} lens(es) from another maker hidden — this form only covers {cfg.label.split('—')[0].trim()} lenses.</span>}
              </div>
              <button className="btn btn-outline btn-sm" style={{ marginBottom: 8 }} disabled={formRows.length >= cfg.maxRows}
                onClick={() => setFormRows([...formRows, Object.fromEntries(cfg.lensCols.map(([k]) => [k, '']))])}>+ blank row</button>
              {formRows.length > 0 && (
                <div className="tbl-wrap" style={{ overflowX: 'auto' }}>
                  <table style={{ width: '100%', borderCollapse: 'collapse' }}>
                    <thead><tr>{cfg.lensCols.map(([k, h]) => <th key={k} style={invTh}>{h}</th>)}<th style={invTh} /></tr></thead>
                    <tbody>
                      {formRows.map((row, i) => (
                        <tr key={i}>
                          {cfg.lensCols.map(([k]) => (
                            <td key={k} style={invCell}><input type="text" value={row[k] || ''} onChange={(e) => setFormRows(formRows.map((r, idx) => (idx === i ? { ...r, [k]: e.target.value } : r)))} style={{ width: '100%', minWidth: 90 }} /></td>
                          ))}
                          <td style={{ ...invCell, whiteSpace: 'nowrap' }}>
                            {row._vendorMismatch && <span title={`${row._maker} lens on a ${cfg.label} form — the vendor will send this back`} style={{ color: 'var(--red)', fontWeight: 700, marginRight: 6 }}>⚠ {row._maker}</span>}
                            <button className="btn btn-sm" onClick={() => setFormRows(formRows.filter((_, idx) => idx !== i))}>✕</button>
                          </td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
              )}
              {mismatched.length > 0 && (
                <div style={{ marginTop: 8, color: 'var(--red)', fontSize: '0.82rem', fontWeight: 600 }}>
                  ⚠ {mismatched.length} lens(es) on this sheet weren't made by {cfg.label.split('—')[0].trim()}. A vendor will usually send the whole form back.
                </div>
              )}
              <div style={{ marginTop: 10 }}>
                <button className="btn btn-primary" disabled={busy || !formRows.length} onClick={generate}>Generate {cfg.label}</button>
                {formRows.some((r) => r._fromStock) && <span style={{ marginLeft: 10, fontSize: '0.78rem', color: 'var(--gray-600)' }}>{formRows.filter((r) => r._fromStock).length} of these will come out of stock</span>}
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ─── Consigned lenses (STAAR ICLs and anything ordered per patient) ──
function InvConsignedLenses({ toast }) {
  const [lenses, setLenses] = invUseState(null);
  const [err, setErr] = invUseState('');
  invUseEffect(() => {
    let alive = true;
    InvApi.consignedLenses()
      .then((d) => { if (alive) setLenses(d.lenses || []); })
      .catch((e) => { if (alive) setErr(e.message); });
    return () => { alive = false; };
  }, []);
  if (err) return <div className="card"><div className="card-body" style={{ color: 'var(--red)' }}>⚠️ {err}</div></div>;
  if (!lenses) return <div style={{ color: 'var(--gray-500)', padding: 16 }}>Reading the lens history…</div>;

  const STATUS = { pending: ['Waiting', 'var(--amber)'], implanted: ['Implanted', 'var(--green)'], shippedBack: ['Shipped back', 'var(--gray-500)'] };
  return (
    <div>
      <p style={{ fontSize: '0.85rem', color: 'var(--gray-600)', maxWidth: 760 }}>
        Lenses a vendor sent for one named patient — STAAR ICLs above all, where several arrive and only one goes in. A lens appears here as soon as it is received with a patient on it, and its status is read straight from the stock history, so there is nothing to keep up to date by hand.
      </p>
      {lenses.length === 0 ? (
        <div className="card" style={{ maxWidth: 640 }}><div className="card-body" style={{ color: 'var(--gray-500)', fontSize: '0.86rem' }}>
          No consigned lenses yet. When a lens is received, fill in "ordered for patient" (and the eye and whether it is the primary or a backup) and it will show up here.
        </div></div>
      ) : (
        <div className="card" style={{ margin: 0 }}>
          <div className="tbl-wrap" style={{ overflowX: 'auto' }}>
            <table style={{ width: '100%', borderCollapse: 'collapse' }}>
              <thead><tr><th style={invTh}>Patient</th><th style={invTh}>Eye</th><th style={invTh}>Role</th><th style={invTh}>Lens</th><th style={invTh}>Serial</th><th style={invTh}>Status</th><th style={invTh}>When</th></tr></thead>
              <tbody>
                {lenses.map((l) => (
                  <tr key={l.serial}>
                    <td style={invCell}>{l.orderedFor}</td>
                    <td style={invCell}>{l.eye || '—'}</td>
                    <td style={invCell}>{l.role ? l.role.charAt(0).toUpperCase() + l.role.slice(1) : '—'}</td>
                    <td style={invCell}>{l.description}</td>
                    <td style={{ ...invCell, fontSize: '0.8rem' }}>{l.serial}</td>
                    <td style={invCell}><InvTag color={(STATUS[l.status] || [])[1] || 'var(--gray-500)'}>{(STATUS[l.status] || [])[0] || l.status}</InvTag></td>
                    <td style={{ ...invCell, fontSize: '0.78rem', color: 'var(--gray-600)' }}>
                      {l.statusAt ? new Date(l.statusAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : new Date(l.receivedAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}
                      {l.note ? <div style={{ fontSize: '0.72rem' }}>{l.note}</div> : null}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      )}
    </div>
  );
}

// ─── Top-level: the Lenses tab ───────────────────────────────────
function InvLenses(ctx) {
  const [sub, setSub] = invUseState('pull');
  const tabs = [['pull', "Tomorrow's pull"], ['audit', 'Shelf audit'], ['returns', 'Returns & forms'], ['consigned', 'Consigned lenses']];
  return (
    <div>
      <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginBottom: 12 }}>
        {tabs.map(([id, label]) => <button key={id} className={`btn btn-sm ${sub === id ? 'btn-teal' : 'btn-outline'}`} onClick={() => setSub(id)}>{label}</button>)}
      </div>
      {sub === 'pull' && <InvLensPull {...ctx} />}
      {sub === 'audit' && <InvLensAudit {...ctx} />}
      {sub === 'returns' && <InvLensReturns {...ctx} />}
      {sub === 'consigned' && <InvConsignedLenses {...ctx} />}
    </div>
  );
}
