/* ===== pages-reagents.jsx — products, batches, outflow ===== */
(function () {
  const { Icon, Button, IconBtn, Card, Badge, AlarmChip, EmptyState, Input, Field, Textarea,
    DateField, SearchableSelect, Table, Tabs, Modal,
    useStore, navigate, cls, fmtDate, fmtRel, fmtQty, fmtCurrency,
    ListShell, FormShell, KV, Stat, SupplierNew, CustomerNew } = window;

  /* =========================================================
     Reagent Products catalog
  ========================================================= */
  function ReagentProducts({ mobile }) {
    const store = useStore();
    const { reagent_products, reagent_batches, suppliers } = store;
    const supById = (id) => suppliers.find((s) => s.id === id);

    const productStock = (p) => {
      const batches = reagent_batches.filter((b) => b.product_id === p.id);
      return {
        batches: batches.length,
        boxes: batches.reduce((a, b) => a + b.on_hand_boxes, 0),
        atoms: batches.reduce((a, b) => a + (b.on_hand_atoms || 0), 0),
      };
    };

    const columns = [
      { header: '产品', cell: (p) => (
        <div className="flex items-center gap-3">
          <span className="h-8 w-8 rounded-md bg-amber-50 text-amber-700 inline-flex items-center justify-center">
            <Icon.flask size={14} />
          </span>
          <div className="min-w-0">
            <div className="text-sm font-medium truncate">{p.name}</div>
            <div className="text-xs text-ink-500 font-mono">{p.code} · {p.cat_no}</div>
          </div>
        </div>
      )},
      { header: '厂家', cell: (p) => <span className="text-sm">{supById(p.supplier_id)?.short}</span> },
      { header: '单盒', cell: (p) => <span className="text-sm">{p.tests_per_box} {p.unit === 'kit' ? 'test' : p.unit}</span> },
      { header: '储存', cell: (p) => <Badge tone="sky">{p.storage}</Badge> },
      { header: '阈值', align: 'right', cell: (p) => <span className="font-mono text-sm">≤ {p.low_threshold}</span> },
      { header: '在库', align: 'right', cell: (p) => {
        const s = productStock(p);
        return <span className="font-mono text-sm">{s.boxes} 盒</span>;
      }},
      { header: '批次', align: 'right', cell: (p) => {
        const s = productStock(p);
        return <span className="font-mono text-sm">{s.batches}</span>;
      }},
      { header: '标签', cell: (p) => <div className="flex flex-wrap gap-1">{(p.tags || []).map(t => <Badge key={t} tone="neutral">{t}</Badge>)}</div> },
    ];

    return (
      <ListShell
        mobile={mobile}
        title="试剂产品目录"
        hint={`${reagent_products.length} 个 SKU · ${reagent_batches.length} 个批次在管`}
        items={reagent_products}
        searchKeys={['name','code','cat_no']}
        searchPlaceholder="搜产品名 / 编码 / 厂家货号"
        columns={columns}
        onRow={(p) => navigate(`#/reagents/batches?product=${p.id}`)}
        primaryAction={<Button size="sm" icon={Icon.plus}>新建产品</Button>}
        mobileItem={(p) => {
          const s = productStock(p);
          return (
            <li key={p.id} onClick={() => navigate(`#/reagents/batches?product=${p.id}`)}
              className="px-3 py-3 flex items-center gap-3 active:bg-ink-50">
              <span className="h-9 w-9 rounded-md bg-amber-50 text-amber-700 inline-flex items-center justify-center shrink-0">
                <Icon.flask size={14} />
              </span>
              <div className="flex-1 min-w-0 leading-tight">
                <div className="text-sm font-medium truncate">{p.name}</div>
                <div className="text-xs text-ink-500 truncate font-mono mt-0.5">{p.code}</div>
                <div className="text-[11px] text-ink-600 mt-1 font-mono">在库 {s.boxes} 盒 / {s.batches} 批次</div>
              </div>
              <Icon.chevRight size={14} className="text-ink-300 shrink-0" />
            </li>
          );
        }}
        filters={[
          { id: 'storage', label: '储存条件', options: [...new Set(reagent_products.map(p=>p.storage))].map(s => ({ value: s, label: s })), predicate: (r,v) => r.storage === v },
          { id: 'supplier', label: '厂家', options: suppliers.filter(s=>!s.archived).slice(0,6).map(s => ({ value: s.id, label: s.short })), predicate: (r,v) => r.supplier_id === v },
        ]}
      />
    );
  }

  /* =========================================================
     Reagent Batches list
  ========================================================= */
  function ReagentBatches({ mobile }) {
    const store = useStore();
    const { reagent_batches, reagent_products, suppliers } = store;
    const productFilter = (window.location.hash.match(/product=([^&]+)/) || [])[1];

    const rows = reagent_batches.map((b) => {
      const p = reagent_products.find((x) => x.id === b.product_id);
      const alarms = window.reagentAlarms(b, p);
      return { ...b, _product: p, _alarms: alarms };
    }).filter(b => !productFilter || b.product_id === productFilter);

    const columns = [
      { header: '产品', cell: (b) => (
        <div className="min-w-0">
          <div className="text-sm font-medium truncate">{b._product?.name}</div>
          <div className="text-xs text-ink-500 truncate">{b._product?.code} · {suppliers.find(s=>s.id===b.supplier_id)?.short}</div>
        </div>
      )},
      { header: '批号', cell: (b) => <span className="font-mono text-sm">{b.lot}</span> },
      { header: '到货', cell: (b) => <span className="font-mono text-xs text-ink-600">{fmtDate(b.received)}</span> },
      { header: '过期', cell: (b) => (
        <div>
          <span className="font-mono text-xs">{fmtDate(b.expiry)}</span>
          <div className="text-[11px] text-ink-500">{fmtRel(b.expiry)}</div>
        </div>
      )},
      { header: '剩余 / 收到', align: 'right', cell: (b) => (
        <div className="font-mono text-sm tabular-nums">
          <div>{fmtQty(b.on_hand_boxes, b.on_hand_atoms)}</div>
          <div className="text-[11px] text-ink-400">收 {b.received_boxes} 盒</div>
        </div>
      )},
      { header: '位置', cell: (b) => <span className="text-xs font-mono text-ink-700">{b.location}</span> },
      { header: '报警', cell: (b) => (
        <div className="flex flex-wrap gap-1">
          {b._alarms.length === 0 ? <span className="text-[11px] text-ink-300">—</span> :
            b._alarms.map((a, i) => <AlarmChip key={i} type={a.type} />)}
        </div>
      )},
    ];

    return (
      <ListShell
        mobile={mobile}
        title="试剂批次"
        hint={productFilter ? `已筛选：${reagent_products.find(p=>p.id===productFilter)?.name}` : `${rows.length} 个批次 · 含库存`}
        items={rows}
        searchKeys={['lot']}
        searchPlaceholder="搜批号 / Lot"
        columns={columns}
        onRow={(b) => navigate(`#/reagents/batches/${b.id}`)}
        primaryAction={
          <div className="flex gap-2">
            <Button variant="secondary" size="sm" icon={Icon.arrowR} onClick={() => navigate('#/reagents/outflow/new')}>取货</Button>
            <Button size="sm" icon={Icon.plus} onClick={() => navigate('#/reagents/batches/new')}>新建批次</Button>
          </div>
        }
        archivedKey="_archived"
        mobileItem={(b) => (
          <li key={b.id} onClick={() => navigate(`#/reagents/batches/${b.id}`)}
            className="px-3 py-3 active:bg-ink-50">
            <div className="flex items-center gap-2 mb-1">
              <div className="text-sm font-medium truncate flex-1 min-w-0">{b._product?.name}</div>
              <Icon.chevRight size={14} className="text-ink-300 shrink-0" />
            </div>
            <div className="flex items-center gap-3 text-xs text-ink-500 font-mono">
              <span>批 {b.lot}</span>
              <span>{fmtQty(b.on_hand_boxes, b.on_hand_atoms)}</span>
              <span>过期 {fmtDate(b.expiry)}</span>
            </div>
            {b._alarms.length > 0 && (
              <div className="flex flex-wrap gap-1 mt-1.5">
                {b._alarms.map((a, i) => <AlarmChip key={i} type={a.type} detail={a.detail} />)}
              </div>
            )}
          </li>
        )}
        filters={[
          { id: 'status', label: '状态', options: [
            { value: 'EXPIRED', label: '已过期', count: rows.filter(r => r._alarms.some(a=>a.type==='EXPIRED')).length },
            { value: 'EXPIRING', label: '即将过期', count: rows.filter(r => r._alarms.some(a=>a.type==='EXPIRING')).length },
            { value: 'LOW_STOCK', label: '低库存', count: rows.filter(r => r._alarms.some(a=>a.type==='LOW_STOCK')).length },
            { value: 'STALE', label: '滞库', count: rows.filter(r => r._alarms.some(a=>a.type==='STALE')).length },
          ], predicate: (r,v) => r._alarms.some(a=>a.type===v) },
          { id: 'storage', label: '储存', options: [
            { value: '2–8 °C', label: '2–8 °C' },
            { value: '-20 °C', label: '-20 °C' },
            { value: 'RT',      label: '室温' },
          ], predicate: (r,v) => r._product?.storage === v },
        ]}
      />
    );
  }

  /* =========================================================
     Batch Detail (with timeline of outflows/returns)
  ========================================================= */
  function ReagentBatchDetail({ id, mobile }) {
    const store = useStore();
    const b = store.reagent_batches.find((x) => x.id === id);
    const [tab, setTab] = React.useState('overview');
    if (!b) return <EmptyState title="未找到该批次" />;
    const p = store.reagent_products.find((x) => x.id === b.product_id);
    const supplier = store.suppliers.find((s) => s.id === b.supplier_id);
    const outflows = store.reagent_outflows.filter((o) => o.batch_id === id)
      .sort((a, b) => new Date(b.taken_at) - new Date(a.taken_at));
    const alarms = window.reagentAlarms(b, p);

    // build timeline events
    const events = [];
    events.push({ kind: 'receive', date: b.received, text: `入库 ${b.received_boxes} 盒`, sub: `${supplier?.short} · 批号 ${b.lot}` });
    for (const o of outflows) {
      const c = store.customers.find((c) => c.id === o.customer_id);
      events.push({ kind: 'outflow', date: o.taken_at, text: `取出 ${fmtQty(o.taken_boxes, o.taken_atoms)}`, sub: `→ ${c?.short || c?.name}`, ref: o });
      if (o.returned_at) {
        events.push({ kind: 'return', date: o.returned_at, text: `归还 ${fmtQty(o.returned_boxes, o.returned_atoms)}`, sub: `← ${c?.short || c?.name}`, ref: o });
      }
    }
    events.sort((a, b) => new Date(b.date) - new Date(a.date));

    return (
      <div>
        <div className={cls('bg-white border-b border-ink-100', mobile ? 'p-3' : 'p-6')}>
          <div className="flex items-center gap-2 text-xs text-ink-500 mb-2">
            <button onClick={() => navigate('#/reagents/batches')} className="hover:text-ink-900">批次</button>
            <Icon.chevRight size={11} />
            <span className="font-mono text-ink-900">{b.lot}</span>
          </div>
          <div className="flex items-start justify-between gap-3 flex-wrap">
            <div className="min-w-0">
              <div className="flex items-center gap-3 flex-wrap">
                <h1 className="text-xl font-semibold tracking-tight">{p?.name}</h1>
                <Badge tone="amber">{p?.code}</Badge>
                {alarms.map((a, i) => <AlarmChip key={i} type={a.type} detail={a.detail} size="md" />)}
              </div>
              <div className="text-xs text-ink-500 mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-0.5">
                <span className="font-mono">批号 {b.lot}</span>
                <span>·</span><span>{supplier?.short}</span>
                <span>·</span><span>位置 <span className="font-mono">{b.location}</span></span>
                <span>·</span><span>到货 {fmtDate(b.received)}</span>
              </div>
            </div>
            <div className="flex items-center gap-2 shrink-0">
              <Button variant="secondary" size="sm" icon={Icon.arrowR} onClick={() => navigate(`#/reagents/outflow/new?batch=${b.id}`)}>取货</Button>
              <Button variant="secondary" size="sm" icon={Icon.edit}>编辑</Button>
              <IconBtn icon={Icon.moreH} label="更多" variant="outline" />
            </div>
          </div>
          <div className="mt-4">
            <Tabs tabs={[
              { id: 'overview', label: '概览' },
              { id: 'timeline', label: '取还时间轴', count: events.length },
              { id: 'attach', label: '附件 / PDF', count: 2 },
            ]} active={tab} onChange={setTab} />
          </div>
        </div>

        <div className={cls(mobile ? 'p-3' : 'p-6')}>
          {tab === 'overview' && (
            <div className={cls('grid gap-4', mobile ? 'grid-cols-1' : 'grid-cols-3')}>
              {/* Stock big card */}
              <Card className={cls('p-5', !mobile && 'col-span-2')}>
                <div className="flex items-start justify-between mb-4">
                  <div>
                    <div className="text-[11px] uppercase tracking-wider text-ink-500">当前库存</div>
                    <div className="mt-1 flex items-baseline gap-3">
                      <span className="text-4xl font-semibold tabular-nums font-mono">{b.on_hand_boxes}</span>
                      <span className="text-base text-ink-500">盒</span>
                      {b.on_hand_atoms > 0 && (
                        <>
                          <span className="text-ink-300">+</span>
                          <span className="text-2xl font-semibold tabular-nums font-mono">{b.on_hand_atoms}</span>
                          <span className="text-base text-ink-500">个 test</span>
                        </>
                      )}
                    </div>
                    <div className="text-[11px] text-ink-500 mt-1.5">
                      共 <span className="font-mono">{b.on_hand_boxes * (p?.tests_per_box || 0) + (b.on_hand_atoms || 0)}</span> tests
                    </div>
                  </div>
                  <div className="text-right">
                    <div className="text-[11px] uppercase tracking-wider text-ink-500">阈值</div>
                    <div className="font-mono text-2xl mt-1">≤ {p?.low_threshold}</div>
                  </div>
                </div>

                {/* Stock progress */}
                <div className="mb-1.5 flex items-center justify-between text-[11px] text-ink-500">
                  <span>剩余 / 收到</span>
                  <span className="font-mono">{b.on_hand_boxes} / {b.received_boxes} 盒</span>
                </div>
                <div className="h-2 bg-ink-100 rounded-full overflow-hidden">
                  <div className={cls('h-full rounded-full',
                    alarms.some(a=>a.type==='LOW_STOCK') ? 'bg-amber-500' : 'bg-emerald-500')}
                    style={{ width: `${(b.on_hand_boxes / b.received_boxes) * 100}%` }}>
                  </div>
                </div>

                <div className="mt-5 pt-4 border-t border-ink-100 grid grid-cols-3 gap-4">
                  <KV label="到货日期"  v={<span className="font-mono">{fmtDate(b.received)}</span>} />
                  <KV label="过期日期"  v={<span className="font-mono">{fmtDate(b.expiry)}</span>} />
                  <KV label="过期倒计时" v={<span className={cls('font-medium', alarms.some(a=>a.type==='EXPIRED') && 'text-red-700', alarms.some(a=>a.type==='EXPIRING') && 'text-orange-700')}>{fmtRel(b.expiry)}</span>} />
                  <KV label="位置" v={<span className="font-mono">{b.location}</span>} />
                  <KV label="单盒成本" v={fmtCurrency(b.cost_per_box)} />
                  <KV label="估算价值" v={fmtCurrency(b.cost_per_box * b.on_hand_boxes)} />
                </div>
              </Card>

              {/* Quick info */}
              <Card className="p-5">
                <h3 className="text-sm font-semibold mb-3">产品信息</h3>
                <dl className="space-y-3 text-sm">
                  <KV label="厂家货号" v={<span className="font-mono">{p?.cat_no}</span>} />
                  <KV label="供货商" v={<button onClick={() => navigate(`#/suppliers/${supplier?.id}`)} className="text-blue-600 hover:underline">{supplier?.name}</button>} />
                  <KV label="储存条件" v={<Badge tone="sky">{p?.storage}</Badge>} />
                  <KV label="单盒规格" v={`${p?.tests_per_box} ${p?.unit === 'kit' ? 'test' : p?.unit}/盒`} />
                </dl>
              </Card>
            </div>
          )}

          {tab === 'timeline' && (
            <Card className="p-6">
              <Timeline events={events} customers={store.customers} />
            </Card>
          )}

          {tab === 'attach' && (
            <Card className="p-6">
              <div className="grid grid-cols-2 gap-3">
                {[
                  { name: 'CoA_L240712A.pdf', size: '184 KB', date: '2024-07-15' },
                  { name: 'Invoice_Merck_07-2024.pdf', size: '92 KB', date: '2024-07-20' },
                ].map((f) => (
                  <div key={f.name} className="flex items-center gap-3 p-3 rounded-lg ring-1 ring-ink-200 hover:bg-ink-50 cursor-pointer">
                    <span className="h-10 w-10 rounded-md bg-red-50 text-red-700 inline-flex items-center justify-center font-mono text-[10px]">PDF</span>
                    <div className="flex-1 min-w-0">
                      <div className="text-sm font-medium truncate">{f.name}</div>
                      <div className="text-xs text-ink-500">{f.size} · {f.date}</div>
                    </div>
                    <IconBtn icon={Icon.download} label="下载" />
                  </div>
                ))}
              </div>
            </Card>
          )}
        </div>
      </div>
    );
  }

  function Timeline({ events, customers }) {
    if (events.length === 0) return <EmptyState title="尚无活动" />;
    const meta = {
      receive:  { icon: Icon.pkg,    tone: 'bg-emerald-100 text-emerald-700', ring: 'ring-emerald-300', label: '入库' },
      outflow:  { icon: Icon.arrowR, tone: 'bg-sky-100 text-sky-700',         ring: 'ring-sky-300',    label: '取货' },
      return:   { icon: Icon.arrowL, tone: 'bg-violet-100 text-violet-700',   ring: 'ring-violet-300', label: '归还' },
    };
    return (
      <ol className="relative">
        <div className="absolute left-[15px] top-2 bottom-2 w-px bg-ink-200"></div>
        {events.map((e, i) => {
          const m = meta[e.kind];
          return (
            <li key={i} className="relative pl-10 pb-5 last:pb-0">
              <span className={cls('absolute left-0 top-0 h-8 w-8 rounded-full inline-flex items-center justify-center ring-2 ring-white', m.tone)}>
                <m.icon size={14} />
              </span>
              <div className="flex items-baseline justify-between gap-3 flex-wrap">
                <div>
                  <div className="text-sm font-medium">{e.text}</div>
                  <div className="text-xs text-ink-500 mt-0.5">{e.sub}</div>
                </div>
                <div className="text-xs text-ink-500 font-mono shrink-0">
                  {fmtDate(e.date)} <span className="text-ink-400">· {fmtRel(e.date)}</span>
                </div>
              </div>
              {e.ref && !e.ref.returned_at && e.kind === 'outflow' && (
                <div className="mt-2 inline-flex items-center gap-2 text-[11px]">
                  <Badge tone={new Date(e.ref.due_at) < new Date('2026-05-28') ? 'sky' : 'neutral'}>
                    应还 {fmtDate(e.ref.due_at)} · {fmtRel(e.ref.due_at)}
                  </Badge>
                  <button className="text-blue-600 hover:underline">记录归还</button>
                </div>
              )}
            </li>
          );
        })}
      </ol>
    );
  }

  /* =========================================================
     New Batch form
  ========================================================= */
  function ReagentBatchNew({ mobile }) {
    const store = useStore();
    const params = new URLSearchParams(window.location.hash.split('?')[1] || '');
    const [form, setForm] = React.useState({
      product_id: '', lot: '', supplier_id: '', received: '2026-05-28', expiry: '',
      received_boxes: '', cost_per_box: '', location: 'FB · ', notes: ''
    });
    const [supModal, setSupModal] = React.useState(false);
    const handle = (k) => (v) => setForm({ ...form, [k]: v });
    const handleI = (k) => (e) => setForm({ ...form, [k]: e.target.value });

    const ok = form.product_id && form.lot && form.received_boxes && form.expiry;

    const submit = (next) => {
      if (!ok) return;
      const id = window.nextId('rb');
      const row = {
        id, product_id: form.product_id, lot: form.lot, supplier_id: form.supplier_id,
        received: form.received, expiry: form.expiry,
        received_boxes: +form.received_boxes, on_hand_boxes: +form.received_boxes, on_hand_atoms: 0,
        location: form.location, cost_per_box: +form.cost_per_box || 0, last_movement: form.received,
      };
      store.add('reagent_batches', row);
      const p = store.reagent_products.find(p => p.id === form.product_id);
      store.toast(`已入库 ${form.received_boxes} 盒 · ${p?.name} (${form.lot})`);
      store.logActivity({ kind: 'receive', text: `入库 ${form.received_boxes} 盒 · ${p?.name} (${form.lot})`, ref: id });
      if (next) setForm({ ...form, lot: '', received_boxes: '', cost_per_box: '' });
      else navigate(`#/reagents/batches/${id}`);
    };

    const supplierOpts = store.suppliers.filter(s=>!s.archived).map(s => ({ id: s.id, label: s.name, sub: `${s.city} · ${s.tags?.join(' / ')}` }));
    const productOpts = store.reagent_products.map(p => ({ id: p.id, label: p.name, sub: `${p.code} · ${p.tests_per_box} ${p.unit === 'kit' ? 'test' : p.unit}/盒` }));

    return (
      <>
        <FormShell mobile={mobile} title="新建批次（入库）" hint="批次按 Lot 唯一，过期日和到货日是后续报警的依据"
          crumb={[{ label: '批次', to: '#/reagents/batches' }, { label: '新建' }]}
          actions={
            <>
              <Button variant="ghost" size="sm" onClick={() => navigate('#/reagents/batches')}>取消</Button>
              <Button variant="secondary" size="sm" disabled={!ok} onClick={() => submit(true)}>保存并新建下一条</Button>
              <Button size="sm" disabled={!ok} onClick={() => submit(false)} icon={Icon.check}>入库</Button>
            </>
          }>
          <div className={cls('grid gap-x-4 gap-y-4', mobile ? 'grid-cols-1' : 'grid-cols-2')}>
            <Field label="产品" required className={cls(!mobile && 'col-span-2')}>
              <SearchableSelect options={productOpts} value={form.product_id}
                onChange={handle('product_id')}
                recentIds={['rp-1','rp-2']} required={!form.product_id}
                placeholder="搜产品名 / 厂家货号" />
            </Field>
            <Field label="批号 / Lot" required>
              <Input value={form.lot} onChange={handleI('lot')} required={!form.lot} placeholder="L240712A" className="font-mono" />
            </Field>
            <Field label="供货商" hint="留空则用产品默认">
              <SearchableSelect options={supplierOpts} value={form.supplier_id}
                onChange={handle('supplier_id')} recentIds={['s-1','s-2']}
                allowCreate onCreate={() => setSupModal(true)}
                placeholder="选择供货商或新建" />
            </Field>
            <Field label="到货日期" required>
              <DateField value={form.received} onChange={handle('received')} required={!form.received} />
            </Field>
            <Field label="过期日期" required hint="影响 🔴 已过期 / 🟠 即将过期">
              <DateField value={form.expiry} onChange={handle('expiry')} required={!form.expiry} />
            </Field>
            <Field label="数量（盒）" required>
              <Input type="number" value={form.received_boxes} onChange={handleI('received_boxes')}
                placeholder="0" className="font-mono" required={!form.received_boxes} />
            </Field>
            <Field label="单盒成本 (€)" hint="可选 — 用于估算价值">
              <Input type="number" value={form.cost_per_box} onChange={handleI('cost_per_box')}
                placeholder="0" className="font-mono" />
            </Field>
            <Field label="位置" className={cls(!mobile && 'col-span-2')}>
              <Input value={form.location} onChange={handleI('location')} placeholder="FB · A-1-04" className="font-mono" leadingIcon={Icon.pin} />
            </Field>
            <Field label="备注" className={cls(!mobile && 'col-span-2')}>
              <Textarea value={form.notes} onChange={handleI('notes')} placeholder="可选 — 内部备注" />
            </Field>

            {/* Attach */}
            <Field label="附件 (CoA / Invoice)" className={cls(!mobile && 'col-span-2')}>
              <div className="border-2 border-dashed border-ink-200 rounded-lg p-5 text-center bg-ink-50/30 hover:bg-ink-50 cursor-pointer">
                <Icon.paperclip size={18} className="mx-auto text-ink-400 mb-1.5" />
                <div className="text-sm text-ink-700">拖入 PDF 或 <span className="text-blue-600 underline">浏览</span></div>
                <div className="text-[11px] text-ink-500 mt-1">最大 10 MB · 存储于 R2</div>
              </div>
            </Field>
          </div>
        </FormShell>

        <Modal open={supModal} onClose={() => setSupModal(false)} size="lg" title="即时新建供货商">
          <SupplierNew isModal onCreated={(s) => { setForm({ ...form, supplier_id: s.id }); setSupModal(false); }} onCancel={() => setSupModal(false)} />
        </Modal>
      </>
    );
  }

  /* =========================================================
     Outflow (取货) — high-frequency, the key form
  ========================================================= */
  function ReagentOutflowNew({ mobile }) {
    const store = useStore();
    const params = new URLSearchParams(window.location.hash.split('?')[1] || '');
    const initBatch = params.get('batch') || '';
    const initCust  = params.get('to') || '';

    const [form, setForm] = React.useState({
      product_id: initBatch ? store.reagent_batches.find(b=>b.id===initBatch)?.product_id : '',
      batch_id: initBatch,
      customer_id: initCust,
      taken_at: '2026-05-28',
      due_at: '2026-06-28',
      taken_boxes: 1,
      taken_atoms: 0,
      note: '',
    });
    const [custModal, setCustModal] = React.useState(false);
    const set = (k) => (v) => setForm({ ...form, [k]: v });
    const setI = (k) => (e) => setForm({ ...form, [k]: e.target.value });

    const product = store.reagent_products.find(p => p.id === form.product_id);
    const batch = store.reagent_batches.find(b => b.id === form.batch_id);
    const customer = store.customers.find(c => c.id === form.customer_id);

    // batches for selected product, sorted FIFO (oldest expiry first that's not expired)
    const batchOptions = store.reagent_batches
      .filter(b => b.product_id === form.product_id && b.on_hand_boxes > 0)
      .sort((a, b) => new Date(a.expiry) - new Date(b.expiry))
      .map(b => {
        const al = window.reagentAlarms(b, product);
        return {
          id: b.id,
          label: `批号 ${b.lot}`,
          sub: `剩 ${fmtQty(b.on_hand_boxes, b.on_hand_atoms)} · 过期 ${fmtDate(b.expiry)} ${al[0] ? `(${ALARM_META(al[0])})` : ''}`,
          alarms: al,
          batch: b,
        };
      });

    const ok = form.product_id && form.batch_id && form.customer_id && (form.taken_boxes > 0 || form.taken_atoms > 0);
    const enough = !batch || (form.taken_boxes <= batch.on_hand_boxes);

    const submit = (next) => {
      if (!ok || !enough) return;
      const id = window.nextId('ro');
      const row = {
        id, batch_id: form.batch_id, customer_id: form.customer_id,
        taken_at: form.taken_at, taken_boxes: +form.taken_boxes, taken_atoms: +form.taken_atoms,
        due_at: form.due_at, returned_at: null, returned_boxes: 0, returned_atoms: 0,
        by_user: store.currentUserId, note: form.note,
      };
      store.add('reagent_outflows', row);
      store.update('reagent_batches', form.batch_id, {
        on_hand_boxes: batch.on_hand_boxes - +form.taken_boxes,
        on_hand_atoms: Math.max(0, batch.on_hand_atoms - +form.taken_atoms),
        last_movement: form.taken_at,
      });
      store.toast(`取出 ${fmtQty(form.taken_boxes, form.taken_atoms)} · ${product.name} → ${customer.short || customer.name}`);
      store.logActivity({ kind: 'outflow', text: `取出 ${fmtQty(form.taken_boxes, form.taken_atoms)} ${product.name} (${batch.lot}) → ${customer.name}`, ref: form.batch_id });
      if (next) setForm({ ...form, taken_boxes: 1, taken_atoms: 0, note: '' });
      else navigate(`#/reagents/batches/${form.batch_id}`);
    };

    return (
      <>
        <FormShell mobile={mobile} title="取货（出库）" hint="⭐ 最高频操作 · 选产品 → 选批次（自动 FIFO 排序）→ 选客户"
          crumb={[{ label: '批次', to: '#/reagents/batches' }, { label: '取货' }]}
          actions={
            <>
              <Button variant="ghost" size="sm" onClick={() => navigate('#/reagents/batches')}>取消</Button>
              <Button variant="secondary" size="sm" disabled={!ok || !enough} onClick={() => submit(true)}>保存并新建下一条</Button>
              <Button size="sm" disabled={!ok || !enough} onClick={() => submit(false)} icon={Icon.check}>确认取货</Button>
            </>
          }>
          <div className={cls('grid gap-4', mobile ? 'grid-cols-1' : 'grid-cols-3')}>
            <div className={cls(mobile ? '' : 'col-span-2')}>
              <div className="grid gap-x-4 gap-y-4 grid-cols-2">
                <Field label="产品" required className="col-span-2">
                  <SearchableSelect
                    options={store.reagent_products.map(p => ({ id: p.id, label: p.name, sub: `${p.code} · ${p.tests_per_box}/盒` }))}
                    value={form.product_id}
                    onChange={(id) => setForm({ ...form, product_id: id, batch_id: '' })}
                    recentIds={['rp-1','rp-5','rp-2']}
                    required={!form.product_id}
                    placeholder="搜产品名 / 厂家货号" />
                </Field>

                <Field label="批次（FIFO 自动排序）" required className="col-span-2"
                  hint={form.product_id ? `${batchOptions.length} 个可用批次 · 按到期日先入先出` : '先选产品'}>
                  <SearchableSelect
                    options={batchOptions}
                    value={form.batch_id}
                    onChange={set('batch_id')}
                    required={!!form.product_id && !form.batch_id}
                    placeholder={form.product_id ? '选择批次' : '先选产品'}
                    renderOption={(o) => (
                      <>
                        <div className="flex-1 min-w-0">
                          <div className="text-ink-900 flex items-center gap-2">
                            <span className="font-mono text-xs">{o.batch.lot}</span>
                            {o.alarms.map((a, i) => <AlarmChip key={i} type={a.type} />)}
                          </div>
                          <div className="text-xs text-ink-500 mt-0.5">
                            剩 <span className="font-mono">{fmtQty(o.batch.on_hand_boxes, o.batch.on_hand_atoms)}</span> · 过期 <span className="font-mono">{fmtDate(o.batch.expiry)}</span> · {o.batch.location}
                          </div>
                        </div>
                      </>
                    )}
                  />
                </Field>

                <Field label="客户" required className="col-span-2">
                  <SearchableSelect
                    options={store.customers.filter(c=>!c.archived).map(c => ({ id: c.id, label: c.name, sub: `${c.city} · ${c.tags?.join(' / ')}` }))}
                    value={form.customer_id}
                    onChange={set('customer_id')}
                    recentIds={['c-1','c-3','c-2']}
                    allowCreate onCreate={() => setCustModal(true)}
                    required={!form.customer_id}
                    placeholder="搜客户名 — 找不到？「即时新建」" />
                </Field>

                <Field label="取出数量（盒）" required>
                  <Input type="number" min="0" value={form.taken_boxes} onChange={setI('taken_boxes')} className="font-mono" />
                </Field>
                <Field label="散装 test 数（可选）" hint="盒为主、test 为原子">
                  <Input type="number" min="0" value={form.taken_atoms} onChange={setI('taken_atoms')} className="font-mono" />
                </Field>

                <Field label="取出日期" required><DateField value={form.taken_at} onChange={set('taken_at')} /></Field>
                <Field label="应还日期" hint="逾期未还会进入 🔵 取货超期"><DateField value={form.due_at} onChange={set('due_at')} /></Field>

                <Field label="备注" className="col-span-2">
                  <Textarea value={form.note} onChange={setI('note')} placeholder="例：试用 / 配置 / 替代品" />
                </Field>
              </div>
            </div>

            {/* Right summary */}
            <Card className="p-5 self-start bg-ink-50/40 ring-ink-200">
              <div className="text-[10px] uppercase tracking-wider text-ink-500 mb-2">本次取货</div>
              {!product ? (
                <div className="text-sm text-ink-400 italic">尚未选择产品</div>
              ) : (
                <>
                  <div className="text-sm font-medium leading-snug">{product.name}</div>
                  {batch && (
                    <>
                      <div className="text-xs text-ink-500 mt-0.5">
                        批号 <span className="font-mono">{batch.lot}</span> · 过期 <span className="font-mono">{fmtDate(batch.expiry)}</span>
                      </div>
                      <div className="mt-3 p-3 bg-white rounded-lg ring-1 ring-ink-200">
                        <div className="text-[11px] uppercase tracking-wider text-ink-400">取出</div>
                        <div className="text-2xl font-semibold tabular-nums font-mono mt-0.5">
                          {form.taken_boxes || 0} <span className="text-sm text-ink-500">盒</span>
                          {form.taken_atoms > 0 && (
                            <> <span className="text-ink-300">+</span> {form.taken_atoms} <span className="text-sm text-ink-500">test</span></>
                          )}
                        </div>
                      </div>
                      <div className="mt-3 grid grid-cols-2 gap-3 text-xs">
                        <div>
                          <div className="text-ink-500">取前</div>
                          <div className="font-mono">{batch.on_hand_boxes} 盒</div>
                        </div>
                        <div>
                          <div className="text-ink-500">取后</div>
                          <div className={cls('font-mono', !enough ? 'text-red-700' : '')}>
                            {batch.on_hand_boxes - (+form.taken_boxes || 0)} 盒
                          </div>
                        </div>
                      </div>
                      {!enough && (
                        <div className="mt-3 px-2.5 py-2 rounded-md bg-red-50 ring-1 ring-red-200 text-xs text-red-700">
                          ⚠️ 数量超过当前库存 ({batch.on_hand_boxes} 盒)
                        </div>
                      )}
                      {batch.on_hand_boxes - (+form.taken_boxes || 0) <= (product.low_threshold || 2) && enough && (
                        <div className="mt-3 px-2.5 py-2 rounded-md bg-amber-50 ring-1 ring-amber-200 text-xs text-amber-800">
                          🟡 取后将触发低库存（阈值 ≤ {product.low_threshold}）
                        </div>
                      )}
                    </>
                  )}
                  {customer && (
                    <div className="mt-3 pt-3 border-t border-ink-200">
                      <div className="text-[11px] uppercase tracking-wider text-ink-500 mb-1">送至</div>
                      <div className="text-sm font-medium">{customer.short || customer.name}</div>
                      <div className="text-xs text-ink-500">{customer.city} · {customer.contact}</div>
                    </div>
                  )}
                </>
              )}
            </Card>
          </div>
        </FormShell>

        <Modal open={custModal} onClose={() => setCustModal(false)} size="lg" title="即时新建客户">
          <CustomerNew isModal onCreated={(c) => { setForm({ ...form, customer_id: c.id }); setCustModal(false); }} onCancel={() => setCustModal(false)} />
        </Modal>
      </>
    );
  }

  function ALARM_META(a) {
    return window.ALARM_META[a.type]?.emoji || '';
  }

  Object.assign(window, { ReagentProducts, ReagentBatches, ReagentBatchDetail, ReagentBatchNew, ReagentOutflowNew });
})();
