/* global React, Ic, useStore, MovementBadge, AnimNumber */
const { useState: useRS, useMemo: useRM } = React;

// % shelf life remaining for a lot: time left / total shelf span (mfg -> expiry).
// Falls back to the item's shelfLife when the lot has no mfgDate.
function lifePct(s, item) {
  if (!s.expiry) return null;
  const total = (s.mfgDate && s.expiry > s.mfgDate)
    ? s.expiry - s.mfgDate
    : (item?.shelfLife > 0 ? item.shelfLife * 86400000 : null);
  if (!total) return null;
  return Math.max(0, Math.min(100, Math.round((s.expiry - Date.now()) / total * 100)));
}
const lifeColor = (p) => p < 50 ? 'var(--danger)' : p < 75 ? 'var(--warning)' : 'var(--success)';

// ========= Stock Balance Report =========
function PageStockReport() {
  const { state } = useStore();
  const [q, setQ] = useRS('');
  const [filter, setFilter] = useRS('all');
  const [zone, setZone] = useRS('all');
  const [advOpen, setAdvOpen] = useRS(false);
  const [catF, setCatF] = useRS('all');
  const [whF, setWhF] = useRS('all');
  const [openSkus, setOpenSkus] = useRS({}); // sku -> true = lot detail expanded
  const toggleSku = (sku) => setOpenSkus(o => ({ ...o, [sku]: !o[sku] }));
  const cats = ['all', ...[...new Set(state.items.map(i => i.category).filter(Boolean))].sort((a, b) => String(a).localeCompare(String(b), 'th'))];

  const data = useRM(() => {
    return state.items.map(i => {
      const stocks = state.stock.filter(s => s.sku === i.sku);
      const totalQty = stocks.reduce((a,b)=>a+b.qty,0);
      const filteredStocks = stocks.filter(s => {
        const loc = state.locations.find(l => l.id === s.locId);
        return (zone === 'all' || loc?.zone === zone) && (whF === 'all' || loc?.wh === whF);
      });
      const value = totalQty * i.unit_cost;
      const status = totalQty === 0 ? 'OUT' : totalQty < i.min ? 'LOW' : totalQty > i.max * 0.95 ? 'FULL' : 'OK';
      // expiry data: earliest expiry across lots with qty > 0
      const dated = stocks.filter(s => s.expiry && s.qty > 0);
      const earliestExpiry = dated.length ? Math.min(...dated.map(s => s.expiry)) : null;
      const earlyDays = earliestExpiry ? Math.floor((earliestExpiry - Date.now()) / 86400000) : null;
      const expQtyExpired = stocks.filter(s => s.expiry && s.expiry < Date.now()).reduce((a,b)=>a+b.qty,0);
      // "near expiry" = remaining shelf life under 1.5 years (matches the dashboard shelf-life alert)
      const expQtyExpiring = stocks.filter(s => s.expiry && s.expiry >= Date.now() && s.expiry - Date.now() < 548 * 86400000).reduce((a,b)=>a+b.qty,0);
      return { ...i, totalQty, locCount: filteredStocks.length, value, status, stocks: filteredStocks, earliestExpiry, earlyDays, expQtyExpired, expQtyExpiring };
    }).filter(x => (zone === 'all' && whF === 'all') || x.locCount > 0);
  }, [state, zone, whF]);

  const filtered = data.filter(x => {
    const ql = q.toLowerCase();
    const matchQ = !q || x.sku.toLowerCase().includes(ql) || (x.name_th || '').toLowerCase().includes(ql) || (x.name || '').toLowerCase().includes(ql);
    const matchCat = catF === 'all' || x.category === catF;
    let matchF = true;
    if (filter === 'ok') matchF = x.status === 'OK';
    else if (filter === 'low') matchF = x.status === 'LOW';
    else if (filter === 'out') matchF = x.status === 'OUT';
    else if (filter === 'expiring') matchF = x.expQtyExpiring > 0;
    else if (filter === 'expired') matchF = x.expQtyExpired > 0;
    return matchQ && matchF && matchCat;
  }).sort((a,b) => String(a.name_th || '').localeCompare(String(b.name_th || ''), 'th', { numeric: true }));

  const totalUnits = filtered.reduce((a,b)=>a+b.totalQty,0);
  const totalValue = filtered.reduce((a,b)=>a+b.value,0);
  const expiringSku = data.filter(x => x.expQtyExpiring > 0 || x.expQtyExpired > 0).length;

  return (
    <div className="page-enter">
      <div className="page-header">
        <div>
          <h2 className="page-title">รายงานสต็อกคงเหลือ <span className="muted mono" style={{fontSize:14, marginLeft:8}}>// STOCK BALANCE</span></h2>
          <div className="page-subtitle">ภาพรวม inventory ทั้งหมด · พร้อมส่งออก CSV / Excel</div>
        </div>
        <div className="row">
          <button className={`btn btn-ghost ${advOpen ? 'active' : ''}`} onClick={() => setAdvOpen(o => !o)}><Ic name="filter" size={14}/>Advanced Filter</button>
          <button className="btn btn-primary" onClick={() => {
            const rows = [];
            filtered.forEach(x => {
              const lots = [...x.stocks].filter(s => s.qty > 0)
                .sort((a, b) => String(a.locId || '').localeCompare(String(b.locId || '')) || (a.expiry || Infinity) - (b.expiry || Infinity));
              if (lots.length === 0) {
                rows.push([x.sku, x.name_th, x.category, '', '', '', '', '', '', '', 0, x.unit_cost, 0, x.status]);
                return;
              }
              lots.forEach(s => {
                const loc = state.locations.find(l => l.id === s.locId) || {};
                const days = s.expiry ? Math.floor((s.expiry - Date.now()) / 86400000) : '';
                rows.push([x.sku, x.name_th, x.category, loc.wh || '', loc.whName || '', s.locId || '', s.lot || '',
                  s.mfgDate ? fmtDate(s.mfgDate) : '', s.expiry ? fmtDate(s.expiry) : '', days, s.qty, x.unit_cost, s.qty * x.unit_cost, x.status]);
              });
            });
            downloadCSV('stock-balance.csv', ['SKU', 'ชื่อสินค้า', 'หมวด', 'WH', 'คลัง', 'Location', 'Lot', 'MFG', 'Expiry', 'วันคงเหลือ', 'คงเหลือ', 'ต้นทุน/หน่วย', 'มูลค่า', 'Status'], rows);
          }}><Ic name="export" size={14}/>Export CSV</button>
        </div>
      </div>

      {advOpen && (
        <div className="card tech" style={{ padding: 12, marginBottom: 14 }}>
          <div className="row" style={{ gap: 14, flexWrap: 'wrap', alignItems: 'flex-end' }}>
            <div className="field" style={{ minWidth: 180 }}>
              <label>หมวดสินค้า</label>
              <select className="select" value={catF} onChange={e => setCatF(e.target.value)}>
                {cats.map(c => <option key={c} value={c}>{c === 'all' ? 'ทุกหมวด' : c}</option>)}
              </select>
            </div>
            <div className="field" style={{ minWidth: 200 }}>
              <label>คลัง (Warehouse)</label>
              <select className="select" value={whF} onChange={e => setWhF(e.target.value)}>
                <option value="all">ทุกคลัง</option>
                {state.warehouses.map(w => <option key={w.id} value={w.id}>{w.id} · {w.name}</option>)}
              </select>
            </div>
            <button className="btn btn-ghost btn-sm" onClick={() => { setCatF('all'); setWhF('all'); setZone('all'); setFilter('all'); setQ(''); }}>ล้างตัวกรอง</button>
          </div>
        </div>
      )}

      <div className="grid-4" style={{ marginBottom: 14 }}>
        <ReportKPI label="SKU ทั้งหมด" value={filtered.length} color="var(--accent)"/>
        <ReportKPI label="มูลค่ารวม" value={totalValue} prefix="฿" color="var(--success)"/>
        <ReportKPI label="ต่ำกว่า Min" value={filtered.filter(x=>x.status==='LOW'||x.status==='OUT').length} suffix=" SKU" color="var(--warning)"/>
        <ReportKPI label="ใกล้/หมดอายุ" value={expiringSku} suffix=" SKU" color="var(--danger)"/>
      </div>

      <div className="card tech" style={{ padding: 12, marginBottom: 14 }}>
        <div className="row" style={{ gap: 10, flexWrap: 'wrap' }}>
          <div className="searchbar" style={{ flex: 1, maxWidth: 'none', borderRadius: 8, minWidth: 240 }}>
            <Ic name="search" size={14}/>
            <input placeholder="ค้นหา SKU, ชื่อสินค้า..." value={q} onChange={e => setQ(e.target.value)}/>
          </div>
          <div className="segmented">
            <button className={filter==='all'?'active':''} onClick={()=>setFilter('all')}>ทั้งหมด</button>
            <button className={filter==='ok'?'active':''} onClick={()=>setFilter('ok')}>OK</button>
            <button className={filter==='low'?'active':''} onClick={()=>setFilter('low')}>LOW</button>
            <button className={filter==='out'?'active':''} onClick={()=>setFilter('out')}>OUT</button>
            <button className={filter==='expiring'?'active':''} onClick={()=>setFilter('expiring')}>ใกล้หมด</button>
            <button className={filter==='expired'?'active':''} onClick={()=>setFilter('expired')}>หมดแล้ว</button>
          </div>
          <select className="select" style={{ width: 140 }} value={zone} onChange={e => setZone(e.target.value)}>
            <option value="all">ทุก Zone</option>
            {['A','B','C','D','E','F'].map(z => <option key={z} value={z}>Zone {z}</option>)}
          </select>
          <select className="select" style={{ width: 180 }} value={catF} onChange={e => setCatF(e.target.value)}>
            {cats.map(c => <option key={c} value={c}>{c === 'all' ? 'ทุกหมวด' : c}</option>)}
          </select>
        </div>
      </div>

      <div className="card tech" style={{ padding: 0 }}>
        <div className="table-wrap" style={{ border: 'none' }}>
          <table className="data">
            <thead>
              <tr>
                <th>SKU</th>
                <th>ชื่อสินค้า</th>
                <th>หมวด</th>
                <th>WH · Location · อายุ · จำนวน</th>
                <th style={{textAlign:'right'}}>คงเหลือ</th>
                <th style={{textAlign:'right'}}>Min / Max</th>
                <th style={{textAlign:'right'}}>มูลค่า</th>
                <th>Status</th>
              </tr>
            </thead>
            <tbody>
              {filtered.map(x => {
                const statusMap = { OK: 'ok', LOW: 'warn', OUT: 'crit', FULL: 'info' };
                const isOpen = !!openSkus[x.sku];
                const lots = [...x.stocks].filter(s => s.qty > 0).sort((a, b) =>
                  (a.locId || '').localeCompare(b.locId || '') || (a.expiry || Infinity) - (b.expiry || Infinity));
                const pcts = lots.map(s => lifePct(s, x)).filter(p => p !== null);
                const minPct = pcts.length ? Math.min(...pcts) : null;
                const whCount = new Set(lots.map(s => s.locId && state.locations.find(l => l.id === s.locId)?.wh).filter(Boolean)).size;
                return (
                  <tr key={x.sku} onClick={() => lots.length > 0 && toggleSku(x.sku)} style={{ cursor: lots.length > 0 ? 'pointer' : 'default' }}>
                    <td className="mono" style={{ color: 'var(--accent)', whiteSpace: 'nowrap' }}>
                      <span style={{ display: 'inline-block', width: 14, color: 'var(--text-muted)' }}>{lots.length > 0 ? (isOpen ? '▾' : '▸') : ''}</span>
                      {x.sku}
                    </td>
                    <td>{x.name_th}<div className="mono" style={{ fontSize: 10.5, color: 'var(--text-muted)' }}>{x.barcode}</div></td>
                    <td><span className="badge neutral">{x.category}</span></td>
                    <td style={{ minWidth: 280 }}>
                      {lots.length === 0 ? (
                        <span className="mono" style={{ fontSize: 10.5, color: 'var(--text-dim)' }}>— ไม่มีสต็อก</span>
                      ) : !isOpen ? (
                        // Collapsed: compact summary — click the row to expand
                        <div className="row" style={{ gap: 8, flexWrap: 'wrap', lineHeight: 1.4 }}>
                          <span className="mono" style={{ fontSize: 10.5, color: 'var(--text-secondary)' }}>{lots.length} รายการ · {whCount} คลัง</span>
                          {minPct !== null && (
                            <span className="mono" style={{ fontSize: 10.5, fontWeight: 700, color: lifeColor(minPct) }}>Life {minPct}%</span>
                          )}
                          <span className="mono" style={{ fontSize: 9.5, color: 'var(--text-dim)' }}>คลิกเพื่อดูรายละเอียด</span>
                        </div>
                      ) : (
                        // Expanded: one line per lot, WH -> Location -> earliest expiry (FEFO)
                        <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
                          {lots.map((s, i) => {
                            const loc = state.locations.find(l => l.id === s.locId);
                            const st = s.expiry ? expiryStatus(s.expiry) : null;
                            const pct = lifePct(s, x);
                            return (
                              <div key={(s.locId || '') + (s.lot || '') + i} className="row" style={{ gap: 6, flexWrap: 'wrap', lineHeight: 1.4 }}>
                                <span className="badge neutral" style={{ fontSize: 9 }}>{loc?.whName || loc?.wh || '—'}</span>
                                <span className="mono" style={{ fontSize: 10.5, color: 'var(--accent)' }}>{s.locId}</span>
                                {s.expiry ? (
                                  <>
                                    <span className="mono" style={{ fontSize: 10, color: (st.cls === 'expired' || st.cls === 'crit') ? 'var(--danger)' : 'var(--text-secondary)' }}>{fmtDate(s.expiry)}</span>
                                    <span className={`badge ${st.badge}`}>{st.label}</span>
                                    {pct !== null && (
                                      <span className="mono" style={{ fontSize: 10.5, fontWeight: 700, color: lifeColor(pct) }}>{pct}%</span>
                                    )}
                                  </>
                                ) : <span className="mono" style={{ fontSize: 10, color: 'var(--text-dim)' }}>ไม่ติดตามอายุ</span>}
                                <span className="mono" style={{ fontSize: 10.5, color: 'var(--text-primary)', fontWeight: 600 }}>· {s.qty.toLocaleString()} u</span>
                                {s.lot && <span className="mono" style={{ fontSize: 9, color: 'var(--text-dim)' }}>LOT {s.lot}</span>}
                              </div>
                            );
                          })}
                        </div>
                      )}
                    </td>
                    <td className="num">{x.totalQty.toLocaleString()}</td>
                    <td className="num" style={{ color: 'var(--text-muted)' }}>{x.min}/{x.max}</td>
                    <td className="num">฿{x.value.toLocaleString()}</td>
                    <td><span className={`badge ${statusMap[x.status]}`}>{x.status}</span></td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  );
}

function ReportKPI({ label, value, prefix, suffix, color }) {
  return (
    <div className="card tech" style={{ padding: 14 }}>
      <div className="mono" style={{ fontSize: 10.5, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 6 }}>{label}</div>
      <div className="mono" style={{ fontSize: 24, color, fontWeight: 700 }}>
        <AnimNumber value={value} prefix={prefix} suffix={suffix}/>
      </div>
    </div>
  );
}

// ========= Movement Report =========
function PageMovement() {
  const { state } = useStore();
  const [type, setType] = useRS('all');
  const [q, setQ] = useRS('');
  const [range, setRange] = useRS('7d');

  const days = range === 'today' ? 1 : range === '7d' ? 7 : range === '30d' ? 30 : 365;
  const cutoff = Date.now() - days * 86400000;

  const filtered = state.movements.filter(m => {
    if (m.ts < cutoff) return false;
    if (type !== 'all' && m.type !== type) return false;
    if (q && !m.sku.toLowerCase().includes(q.toLowerCase()) && !m.ref.toLowerCase().includes(q.toLowerCase())) return false;
    return true;
  });

  const counts = useRM(() => {
    const c = { IN: 0, OUT: 0, TRANSFER: 0, COUNT: 0 };
    filtered.forEach(m => { c[m.type] = (c[m.type] || 0) + m.qty; });
    return c;
  }, [filtered]);

  // Stack chart by day
  const series = useRM(() => {
    const d = Math.min(days, 30);
    const today = new Date(); today.setHours(0,0,0,0);
    const result = Array.from({ length: d }, (_, i) => ({ day: new Date(today.getTime() - (d - 1 - i) * 86400000), IN: 0, OUT: 0, TRANSFER: 0, COUNT: 0 }));
    filtered.forEach(m => {
      const dt = new Date(m.ts); dt.setHours(0,0,0,0);
      const diff = Math.floor((today - dt) / 86400000);
      if (diff >= 0 && diff < d) {
        result[d - 1 - diff][m.type] += m.qty;
      }
    });
    return result;
  }, [filtered, days]);

  return (
    <div className="page-enter">
      <div className="page-header">
        <div>
          <h2 className="page-title">รายงานเคลื่อนไหว <span className="muted mono" style={{fontSize:14, marginLeft:8}}>// MOVEMENT REPORT</span></h2>
          <div className="page-subtitle">ติดตามทุกการเคลื่อนไหวของสต็อก · audit trail</div>
        </div>
        <div className="row">
          <div className="segmented">
            <button className={range==='today'?'active':''} onClick={()=>setRange('today')}>วันนี้</button>
            <button className={range==='7d'?'active':''} onClick={()=>setRange('7d')}>7 วัน</button>
            <button className={range==='30d'?'active':''} onClick={()=>setRange('30d')}>30 วัน</button>
            <button className={range==='all'?'active':''} onClick={()=>setRange('all')}>ทั้งหมด</button>
          </div>
          <button className="btn btn-primary" onClick={() => {
            const itName = (sku) => state.items.find(i => i.sku === sku)?.name_th || '';
            const rows = filtered.map(m => [
              new Date(m.ts).toLocaleString('th-TH'),
              m.type, m.sku, itName(m.sku), m.ref || '',
              m.from || '', (m.type === 'OUT' ? (m.customer || m.to || '') : (m.to || '')),
              (m.delta !== undefined ? m.delta : m.qty), m.lot || '', m.user || '',
            ]);
            downloadCSV('movement-report.csv', ['เวลา', 'ประเภท', 'SKU', 'ชื่อสินค้า', 'Ref Doc', 'From', 'To', 'Qty', 'Lot', 'By'], rows);
          }}><Ic name="export" size={14}/>Export</button>
        </div>
      </div>

      <div className="grid-4" style={{ marginBottom: 14 }}>
        <MoveKPI label="รับเข้า" value={counts.IN} icon="arrow-down" color="var(--success)"/>
        <MoveKPI label="จ่ายออก" value={counts.OUT} icon="arrow-up" color="var(--warning)"/>
        <MoveKPI label="โอน" value={counts.TRANSFER} icon="transfer" color="var(--info)"/>
        <MoveKPI label="ตรวจนับ" value={counts.COUNT} icon="count" color="var(--purple)"/>
      </div>

      <div className="card tech" style={{ marginBottom: 14 }}>
        <div className="card-head">
          <div>
            <div className="card-title">Movement Volume · Stacked by Type</div>
            <div className="card-sub">{range.toUpperCase()} · UNIT</div>
          </div>
          <div className="row" style={{ gap: 12 }}>
            <span className="lead-dot" style={{ color: 'var(--success)' }}>IN</span>
            <span className="lead-dot" style={{ color: 'var(--warning)' }}>OUT</span>
            <span className="lead-dot" style={{ color: 'var(--info)' }}>TRANSFER</span>
            <span className="lead-dot" style={{ color: 'var(--purple)' }}>COUNT</span>
          </div>
        </div>
        <StackedChart series={series}/>
      </div>

      <div className="card tech" style={{ padding: 12, marginBottom: 14 }}>
        <div className="row" style={{ gap: 10 }}>
          <div className="searchbar" style={{ flex: 1, maxWidth: 'none', borderRadius: 8 }}>
            <Ic name="search" size={14}/>
            <input placeholder="ค้นหา SKU, document reference..." value={q} onChange={e => setQ(e.target.value)}/>
          </div>
          <div className="segmented">
            <button className={type==='all'?'active':''} onClick={()=>setType('all')}>All</button>
            <button className={type==='IN'?'active':''} onClick={()=>setType('IN')}>IN</button>
            <button className={type==='OUT'?'active':''} onClick={()=>setType('OUT')}>OUT</button>
            <button className={type==='TRANSFER'?'active':''} onClick={()=>setType('TRANSFER')}>TR</button>
            <button className={type==='COUNT'?'active':''} onClick={()=>setType('COUNT')}>CT</button>
          </div>
        </div>
      </div>

      <div className="card tech" style={{ padding: 0 }}>
        <div className="table-wrap" style={{ border: 'none' }}>
          <table className="data">
            <thead>
              <tr>
                <th>Time</th>
                <th>Type</th>
                <th>SKU</th>
                <th>ชื่อสินค้า</th>
                <th>Ref Doc</th>
                <th>From</th>
                <th>To</th>
                <th style={{textAlign:'right'}}>Qty</th>
                <th>By</th>
              </tr>
            </thead>
            <tbody>
              {filtered.slice(0, 200).map(m => {
                const it = state.items.find(i => i.sku === m.sku);
                return (
                  <tr key={m.id}>
                    <td className="mono" style={{ fontSize: 11 }}>{new Date(m.ts).toLocaleString('th-TH', { dateStyle: 'short', timeStyle: 'short' })}</td>
                    <td><MovementBadge type={m.type}/></td>
                    <td className="mono" style={{ color: 'var(--accent)' }}>{m.sku}</td>
                    <td>{it?.name_th || '—'}</td>
                    <td className="mono" style={{ color: 'var(--info)' }}>{m.ref}</td>
                    <td className="mono" style={{ fontSize: 11 }}>{m.from?.slice(-9) || '—'}</td>
                    <td className="mono" style={{ fontSize: 11 }}>
                      {m.type === 'OUT'
                        ? (m.customer || m.to || <span style={{ color: 'var(--text-dim)' }}>—</span>)
                        : (m.to?.slice(-9) || '—')}
                    </td>
                    <td className="num">{m.delta !== undefined ? (m.delta > 0 ? `+${m.delta}` : m.delta) : m.qty}</td>
                    <td style={{ fontSize: 11.5, color: 'var(--text-muted)' }}>{m.user}</td>
                  </tr>
                );
              })}
              {filtered.length === 0 && <tr><td colSpan="9"><div className="empty"><Ic name="history" size={28}/>ไม่มีข้อมูลในช่วงเวลาที่เลือก</div></td></tr>}
            </tbody>
          </table>
        </div>
        {filtered.length > 200 && <div className="mono" style={{ fontSize: 11, color: 'var(--text-muted)', padding: 12, textAlign: 'center' }}>// แสดง 200 แถวแรก จาก {filtered.length} รายการ</div>}
      </div>
    </div>
  );
}

function MoveKPI({ label, value, icon, color }) {
  return (
    <div className="card tech" style={{ padding: 14 }}>
      <div className="row" style={{ justifyContent: 'space-between', marginBottom: 6 }}>
        <span className="mono" style={{ fontSize: 10.5, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.08em' }}>{label}</span>
        <span style={{ color }}><Ic name={icon} size={14}/></span>
      </div>
      <div className="mono" style={{ fontSize: 24, color, fontWeight: 700 }}>
        <AnimNumber value={value}/>
      </div>
    </div>
  );
}

function StackedChart({ series }) {
  const w = 800, h = 200, pad = 30;
  const types = ['IN', 'OUT', 'TRANSFER', 'COUNT'];
  const colors = { IN: '#10d29a', OUT: '#f5b32a', TRANSFER: '#4d8bff', COUNT: '#a78bfa' };
  const max = Math.max(10, ...series.map(d => types.reduce((a,t)=>a+d[t],0)));
  const bw = (w - pad * 2) / series.length;
  return (
    <svg viewBox={`0 0 ${w} ${h}`} style={{ width: '100%', height: 220 }}>
      {[0.25, 0.5, 0.75].map((p, i) => (
        <line key={i} x1={pad} x2={w-pad} y1={pad + (h-pad*2)*p} y2={pad + (h-pad*2)*p} stroke="var(--border)" strokeDasharray="2 4"/>
      ))}
      {series.map((d, i) => {
        const x = pad + i * bw + 2;
        let yOff = h - pad;
        return (
          <g key={i}>
            {types.map((t, ti) => {
              const v = d[t];
              if (v === 0) return null;
              const bh = (v / max) * (h - pad * 2);
              const y = yOff - bh;
              yOff = y;
              return <rect key={t} className="bar" x={x} y={y} width={bw - 4} height={bh} fill={colors[t]} rx="2" style={{ animationDelay: `${i * 0.02 + ti * 0.05}s` }}/>;
            })}
            {i % Math.max(1, Math.floor(series.length/10)) === 0 && (
              <text x={x + (bw-4)/2} y={h - 8} textAnchor="middle" className="chart-axis">{d.day.getDate()}/{d.day.getMonth()+1}</text>
            )}
          </g>
        );
      })}
    </svg>
  );
}

// ========= Document Trail — grouped by Ref Doc =========
function PageDocuments() {
  const { state } = useStore();
  const [type, setType] = useRS('all');
  const [q, setQ] = useRS('');
  const [range, setRange] = useRS('all');
  const [custF, setCustF] = useRS('all'); // customer / destination filter
  const [openDoc, setOpenDoc] = useRS(null); // document opened in the detail modal

  const days = range === 'today' ? 1 : range === '7d' ? 7 : range === '30d' ? 30 : 99999;
  const cutoff = Date.now() - days * 86400000;

  const movements = state.movements.filter(m => {
    if (m.ts < cutoff) return false;
    if (type !== 'all' && m.type !== type) return false;
    if (custF !== 'all' && (m.customer || m.to) !== custF) return false; // customer/destination filter
    if (q && !String(m.ref || '').toLowerCase().includes(q.toLowerCase())) return false;
    return true;
  });

  // distinct customers/destinations across goods-issue documents
  const customerOpts = useRM(() => {
    const set = new Set();
    state.movements.forEach(m => { if (m.type === 'OUT') { const c = m.customer || m.to; if (c) set.add(c); } });
    return [...set].sort((a, b) => String(a).localeCompare(String(b), 'th'));
  }, [state.movements]);

  const docs = useRM(() => {
    const map = {};
    movements.forEach(m => {
      const ref = m.ref || '—';
      if (!map[ref]) map[ref] = { ref, type: m.type, ts: m.ts, user: m.user, lines: [] };
      const g = map[ref];
      g.lines.push(m);
      if (m.ts > g.ts) g.ts = m.ts;
    });
    return Object.values(map).sort((a, b) => b.ts - a.ts);
  }, [movements]);

  const itemName = (sku) => state.items.find(i => i.sku === sku)?.name_th || sku;

  return (
    <div className="page-enter">
      <div className="page-header">
        <div>
          <h2 className="page-title">ประวัติเอกสาร <span className="muted mono" style={{ fontSize: 14, marginLeft: 8 }}>// DOC TRAIL</span></h2>
          <div className="page-subtitle">ไล่ดูตามเลขที่เอกสาร (Ref Doc) · แจกแจง SKU · Qty · From · To · Time · By</div>
        </div>
        <div className="row">
          <div className="segmented">
            <button className={range === 'today' ? 'active' : ''} onClick={() => setRange('today')}>วันนี้</button>
            <button className={range === '7d' ? 'active' : ''} onClick={() => setRange('7d')}>7 วัน</button>
            <button className={range === '30d' ? 'active' : ''} onClick={() => setRange('30d')}>30 วัน</button>
            <button className={range === 'all' ? 'active' : ''} onClick={() => setRange('all')}>ทั้งหมด</button>
          </div>
        </div>
      </div>

      <div className="card tech" style={{ padding: 12, marginBottom: 14 }}>
        <div className="row" style={{ gap: 10, flexWrap: 'wrap' }}>
          <div className="searchbar" style={{ flex: 1, maxWidth: 'none', borderRadius: 8, minWidth: 240 }}>
            <Ic name="search" size={14}/>
            <input placeholder="ค้นหาเลขที่เอกสาร (GR / GI / TR / CT)..." value={q} onChange={e => setQ(e.target.value)}/>
          </div>
          <div className="segmented">
            <button className={type === 'all' ? 'active' : ''} onClick={() => setType('all')}>All</button>
            <button className={type === 'IN' ? 'active' : ''} onClick={() => setType('IN')}>รับเข้า</button>
            <button className={type === 'OUT' ? 'active' : ''} onClick={() => setType('OUT')}>จ่ายออก</button>
            <button className={type === 'TRANSFER' ? 'active' : ''} onClick={() => setType('TRANSFER')}>โอน</button>
            <button className={type === 'COUNT' ? 'active' : ''} onClick={() => setType('COUNT')}>นับ</button>
          </div>
          <select className="select" style={{ width: 210 }} value={custF} onChange={e => setCustF(e.target.value)} title="กรองตามลูกค้า/ปลายทาง">
            <option value="all">ลูกค้า/ปลายทาง: ทุกราย</option>
            {customerOpts.map(c => <option key={c} value={c}>{c}</option>)}
          </select>
          <span className="mono" style={{ fontSize: 11, color: 'var(--text-muted)' }}>{docs.length} เอกสาร</span>
        </div>
      </div>

      <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
        {docs.slice(0, 100).map(d => {
          const totalQty = d.lines.reduce((a, b) => a + (b.qty || 0), 0);
          return (
            <div key={d.ref} className="card tech" style={{ padding: 0 }}>
              <div className="card-head" style={{ padding: '12px 16px', flexWrap: 'wrap', gap: 8 }}>
                <div className="row" style={{ gap: 10 }}>
                  <MovementBadge type={d.type}/>
                  <span className="mono" style={{ fontSize: 15, color: 'var(--accent)', fontWeight: 700 }}>{d.ref}</span>
                  {d.lines[0]?.docNo && d.lines[0].docNo !== d.ref && <span className="mono" style={{ fontSize: 10.5, color: 'var(--text-dim)' }}>· {d.lines[0].docNo}</span>}
                  <span className="mono" style={{ fontSize: 11, color: 'var(--text-muted)' }}>{d.lines.length} รายการ · {totalQty.toLocaleString()} u</span>
                </div>
                <div className="row" style={{ gap: 12 }}>
                  {d.type === 'OUT' && (d.lines[0]?.customer || d.lines[0]?.to) && (
                    <span className="badge info" style={{ fontSize: 10.5 }}><Ic name="truck" size={11}/>{d.lines[0].customer || d.lines[0].to}</span>
                  )}
                  <span className="mono" style={{ fontSize: 11, color: 'var(--text-muted)' }}>{new Date(d.ts).toLocaleString('th-TH', { dateStyle: 'short', timeStyle: 'short' })}</span>
                  <span style={{ fontSize: 11.5, color: 'var(--text-secondary)' }}>โดย {d.user}</span>
                  <button className="btn btn-ghost btn-sm" onClick={() => setOpenDoc(d)}><Ic name="eye" size={13}/>รายละเอียด</button>
                </div>
              </div>
              <div className="table-wrap" style={{ border: 'none' }}>
                <table className="data">
                  <thead><tr><th>SKU</th><th>ชื่อสินค้า</th><th>From</th><th>To</th><th style={{ textAlign: 'right' }}>Qty</th><th>LOT</th><th>เวลา</th><th>By</th></tr></thead>
                  <tbody>
                    {d.lines.map(m => (
                      <tr key={m.id}>
                        <td className="mono" style={{ color: 'var(--accent)' }}>{m.sku}</td>
                        <td>{itemName(m.sku)}</td>
                        <td className="mono" style={{ fontSize: 11 }}>{m.from?.slice(-9) || '—'}</td>
                        <td className="mono" style={{ fontSize: 11 }}>{m.type === 'OUT' ? (m.customer || m.to || '—') : (m.to?.slice(-9) || '—')}</td>
                        <td className="num">{m.delta !== undefined ? (m.delta > 0 ? `+${m.delta}` : m.delta) : m.qty}</td>
                        <td className="mono" style={{ fontSize: 10.5, color: 'var(--text-muted)' }}>{m.lot || '—'}</td>
                        <td className="mono" style={{ fontSize: 11 }}>{new Date(m.ts).toLocaleTimeString('th-TH', { hour: '2-digit', minute: '2-digit' })}</td>
                        <td style={{ fontSize: 11.5, color: 'var(--text-muted)' }}>{m.user}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            </div>
          );
        })}
        {docs.length === 0 && <div className="card tech"><div className="empty"><Ic name="history" size={28}/>ไม่มีเอกสารในเงื่อนไขที่เลือก</div></div>}
        {docs.length > 100 && <div className="mono" style={{ fontSize: 11, color: 'var(--text-muted)', padding: 8, textAlign: 'center' }}>// แสดง 100 เอกสารล่าสุด จาก {docs.length}</div>}
      </div>

      {openDoc && <DocDetailModal doc={openDoc} state={state} onClose={() => setOpenDoc(null)}/>}
    </div>
  );
}

// Row of label:value used in the document detail modal
function DocMetaRow({ label, value, strong }) {
  return (
    <div className="row" style={{ justifyContent: 'space-between', gap: 12, borderBottom: '1px dashed var(--border)', padding: '5px 0' }}>
      <span className="mono" style={{ fontSize: 10, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>{label}</span>
      <span style={{ fontSize: 13, fontWeight: strong ? 700 : 500, color: strong ? 'var(--accent)' : 'var(--text-primary)', textAlign: 'right' }}>{value}</span>
    </div>
  );
}

// Full document view (delivery-note style) — customer, SO ref, warehouse, all lines
function DocDetailModal({ doc, state, onClose }) {
  const itemName = (sku) => state.items.find(i => i.sku === sku)?.name_th || sku;
  const typeLabel = { IN: 'รับเข้า · Goods Receipt', OUT: 'จ่ายออก · Goods Issue', TRANSFER: 'โอนระหว่าง Location', COUNT: 'ตรวจนับ · Stock Count' }[doc.type] || doc.type;
  const first = doc.lines[0] || {};
  const isOut = doc.type === 'OUT';
  const custName = isOut ? (first.customer || first.to || '') : '';
  const custCode = custName ? (state.customers.find(c => c.name === custName)?.id || '') : '';
  const whSet = new Set(doc.lines.map(m => {
    const loc = state.locations.find(l => l.id === m.from);
    return loc?.whName || loc?.wh;
  }).filter(Boolean));
  const totalQty = doc.lines.reduce((a, b) => a + (b.qty || 0), 0);
  const dt = new Date(doc.ts).toLocaleString('th-TH', { dateStyle: 'medium', timeStyle: 'short' });
  const exportCsv = () => {
    const rows = doc.lines.map(m => [m.sku, itemName(m.sku), m.lot || '', m.from || '', isOut ? (m.customer || m.to || '') : (m.to || ''), m.qty, new Date(m.ts).toLocaleString('th-TH')]);
    downloadCSV(`${doc.ref}.csv`, ['SKU', 'ชื่อสินค้า', 'LOT', 'จาก', 'ปลายทาง', 'จำนวน', 'เวลา'], rows);
  };
  return (
    <Modal open onClose={onClose} title={`เอกสาร ${doc.ref}`}
      footer={(<>
        <button className="btn btn-ghost no-print" onClick={onClose}>ปิด</button>
        <button className="btn btn-ghost no-print" onClick={exportCsv}><Ic name="export" size={14}/>Export CSV</button>
        <button className="btn btn-primary no-print" onClick={() => window.print()}><Ic name="report" size={14}/>พิมพ์</button>
      </>)}>
      <div className="doc-print">
        <div className="row" style={{ gap: 10, marginBottom: 14, flexWrap: 'wrap', alignItems: 'center' }}>
          <MovementBadge type={doc.type}/>
          <span className="mono" style={{ fontSize: 17, fontWeight: 700, color: 'var(--accent)' }}>{doc.ref}</span>
          <span className="badge neutral">{typeLabel}</span>
        </div>
        <div className="grid-2" style={{ gap: '0 24px', marginBottom: 16 }}>
          {isOut && <DocMetaRow label="ลูกค้า / ปลายทาง" value={custName ? `${custName}${custCode ? '  ·  ' + custCode : ''}` : '—'} strong/>}
          {first.soRef && <DocMetaRow label="SO Reference" value={first.soRef}/>}
          {first.docNo && <DocMetaRow label="เลขที่เอกสาร" value={first.docNo}/>}
          <DocMetaRow label="คลัง" value={[...whSet].join(', ') || '—'}/>
          <DocMetaRow label="วันที่ · เวลา" value={dt}/>
          <DocMetaRow label="ผู้ทำรายการ" value={doc.user || '—'}/>
        </div>
        <div className="table-wrap" style={{ border: '1px solid var(--border)' }}>
          <table className="data">
            <thead>
              <tr>
                <th>SKU</th><th>ชื่อสินค้า</th><th>LOT</th><th>จาก</th>
                {isOut && <th>ปลายทาง</th>}
                <th style={{ textAlign: 'right' }}>จำนวน</th>
              </tr>
            </thead>
            <tbody>
              {doc.lines.map(m => (
                <tr key={m.id}>
                  <td className="mono" style={{ color: 'var(--accent)' }}>{m.sku}</td>
                  <td>{itemName(m.sku)}</td>
                  <td className="mono" style={{ fontSize: 10.5, color: 'var(--text-muted)' }}>{m.lot || '—'}</td>
                  <td className="mono" style={{ fontSize: 11 }}>{m.from || '—'}</td>
                  {isOut && <td className="mono" style={{ fontSize: 11 }}>{m.customer || m.to || '—'}</td>}
                  <td className="num">{m.delta !== undefined ? (m.delta > 0 ? `+${m.delta}` : m.delta) : m.qty}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
        <div className="row" style={{ justifyContent: 'flex-end', gap: 20, marginTop: 12 }}>
          <span className="mono" style={{ fontSize: 12, color: 'var(--text-muted)' }}>{doc.lines.length} รายการ</span>
          <span className="mono" style={{ fontSize: 14, fontWeight: 700 }}>รวม {totalQty.toLocaleString()} หน่วย</span>
        </div>
      </div>
    </Modal>
  );
}

// ========= Life Shelf Monitor =========
// Days until a lot's remaining shelf-life % drops to the retailer's minimum.
// >=0 means the lot currently passes; the value counts down to the fail date.
function daysToThreshold(s, item, minPct) {
  if (!s.expiry) return null; // non-perishable → never fails
  const total = (s.mfgDate && s.expiry > s.mfgDate)
    ? s.expiry - s.mfgDate
    : (item?.shelfLife > 0 ? item.shelfLife * 86400000 : null);
  if (!total) return null;
  const thresholdMs = s.expiry - (minPct / 100) * total;
  return Math.floor((thresholdMs - Date.now()) / 86400000);
}
const dayColor = (d) => d == null ? 'var(--success)' : d <= 30 ? 'var(--danger)' : d <= 90 ? 'var(--warning)' : 'var(--success)';
// distinct base hues per category; SKUs inside a category get a lightness gradient
const CAT_HUES = [214, 152, 32, 275, 340, 190, 96, 258, 12, 48, 168, 300];
// perceptual luminance (0..1) of an HSL color — picks readable text on shaded chips
function hslLum(h, s, l) {
  s /= 100; l /= 100;
  const k = n => (n + h / 30) % 12;
  const a = s * Math.min(l, 1 - l);
  const f = n => l - a * Math.max(-1, Math.min(k(n) - 3, 9 - k(n), 1));
  return 0.2126 * f(0) + 0.7152 * f(8) + 0.0722 * f(4);
}

function RetailerLogo({ r, size = 34 }) {
  const [err, setErr] = useRS(false);
  if (!r.logo || err) {
    return <div style={{ width: size, height: size, borderRadius: 8, background: 'var(--surface-2)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, fontSize: size * 0.38, color: 'var(--text-secondary)', flexShrink: 0 }}>{(r.name || '?').slice(0, 2).toUpperCase()}</div>;
  }
  return <img key={r.logo} src={`Logo/${r.logo}`} alt={r.name} onError={() => setErr(true)}
    style={{ width: size, height: size, objectFit: 'contain', borderRadius: 8, background: '#fff', border: '1px solid var(--border)', flexShrink: 0 }}/>;
}

function PageLifeShelf() {
  const store = useStore();
  const { state } = store;
  const retailers = state.retailers || [];
  const [selId, setSelId] = useRS(retailers[0]?.id || null);
  const [q, setQ] = useRS('');
  const [statusF, setStatusF] = useRS('all');
  const [skuModal, setSkuModal] = useRS(false);
  const [editModal, setEditModal] = useRS(null); // retailer object (edit) or 'new'
  const [view, setView] = useRS('retailer'); // 'retailer' | 'overview'
  const [ovCat, setOvCat] = useRS('all'); // overview category filter
  const [ovQ, setOvQ] = useRS('');        // overview search

  const sel = retailers.find(r => r.id === selId) || null;

  // qty passing the threshold for one SKU under a given minimum %
  const passQtyOf = (sku, minPct) => {
    const item = state.items.find(i => i.sku === sku);
    let pass = 0;
    state.stock.filter(s => s.sku === sku && s.qty > 0).forEach(s => {
      const days = daysToThreshold(s, item, minPct);
      if (days === null || days >= 0) pass += s.qty; // null = non-perishable → passes
    });
    return pass;
  };

  // glanceable summary for every retailer card
  const summaries = useRM(() => {
    const m = {};
    retailers.forEach(r => {
      let ready = 0, notReady = 0;
      (r.skus || []).forEach(sku => { (passQtyOf(sku, r.minLife || 0) > 0 ? ready++ : notReady++); });
      m[r.id] = { reg: (r.skus || []).length, ready, notReady };
    });
    return m;
  }, [retailers, state.stock, state.items]);

  // detailed per-SKU rows for the selected retailer
  const rows = useRM(() => {
    if (!sel) return [];
    const minPct = sel.minLife || 0;
    return (sel.skus || []).map(sku => {
      const item = state.items.find(i => i.sku === sku);
      const lots = state.stock.filter(s => s.sku === sku && s.qty > 0);
      let passQty = 0, failQty = 0, minDays = null, minPassPct = null, perishablePass = false;
      const lotList = lots.map(s => {
        const loc = state.locations.find(l => l.id === s.locId);
        const pct = lifePct(s, item);
        const days = daysToThreshold(s, item, minPct);
        const pass = days === null || days >= 0; // exact — stays consistent with the countdown
        if (pass) {
          passQty += s.qty;
          if (days !== null) { // perishable & still above threshold
            perishablePass = true;
            if (minDays === null || days < minDays) minDays = days;
            if (pct !== null && (minPassPct === null || pct < minPassPct)) minPassPct = pct;
          }
        } else failQty += s.qty;
        return { locId: s.locId, whName: loc?.whName || loc?.wh, qty: s.qty, lot: s.lot, expiry: s.expiry, pct, days, pass };
      }).sort((a, b) => (a.pct == null ? Infinity : a.pct) - (b.pct == null ? Infinity : b.pct)); // Life% น้อย → มาก
      const totalQty = passQty + failQty;
      const status = totalQty === 0 ? 'nostock' : passQty === 0 ? 'fail' : 'ready';
      return { sku, name: item?.name_th || sku, barcode: item?.barcode, totalQty, passQty, failQty, minDays, minPassPct, perishablePass, status, lotList };
    });
  }, [sel, state.items, state.stock]);

  const filtered = rows.filter(r => {
    const mq = !q || r.sku.toLowerCase().includes(q.toLowerCase()) || (r.name || '').toLowerCase().includes(q.toLowerCase());
    const mf = statusF === 'all' || (statusF === 'ready' && r.status === 'ready') || (statusF === 'notready' && r.status !== 'ready');
    return mq && mf;
  }).sort((a, b) => String(a.name).localeCompare(String(b.name), 'th', { numeric: true }));

  const kpiReady = rows.filter(r => r.status === 'ready').length;
  const kpiNot = rows.filter(r => r.status !== 'ready').length;
  const kpiPassUnits = rows.reduce((a, b) => a + b.passQty, 0);

  // Overview matrix: only SKUs registered to >=1 retailer, grouped/colored by category
  const overview = useRM(() => {
    const registered = new Set();
    retailers.forEach(r => (r.skus || []).forEach(sku => registered.add(sku)));
    const items = state.items.filter(i => registered.has(i.sku))
      .sort((a, b) => String(a.category || '~').localeCompare(String(b.category || '~'), 'th')
        || String(a.name_th || '').localeCompare(String(b.name_th || ''), 'th', { numeric: true }));
    const cats = [...new Set(items.map(i => i.category || '—'))];
    const hueOf = {}; cats.forEach((c, i) => { hueOf[c] = CAT_HUES[i % CAT_HUES.length]; });
    const catN = {}; items.forEach(i => { const c = i.category || '—'; catN[c] = (catN[c] || 0) + 1; });
    const catI = {};
    const rowsO = items.map(i => {
      const c = i.category || '—';
      const idx = (catI[c] = (catI[c] == null ? 0 : catI[c] + 1));
      const n = catN[c];
      const L = n > 1 ? 90 - (idx / (n - 1)) * 24 : 74; // lightness gradient within category (kept light for readability)
      const total = state.stock.filter(s => s.sku === i.sku && s.qty > 0).reduce((a, b) => a + b.qty, 0);
      const sold = {};
      retailers.forEach(r => { if ((r.skus || []).includes(i.sku)) sold[r.id] = passQtyOf(i.sku, r.minLife || 0); });
      return { sku: i.sku, name: i.name_th || i.sku, category: c, hue: hueOf[c], shade: `hsl(${hueOf[c]}, 52%, ${L}%)`, textOn: hslLum(hueOf[c], 52, L) < 0.5 ? '#fff' : '#0f172a', total, sold };
    });
    const used = retailers.filter(r => (r.skus || []).length > 0)
      .sort((a, b) => (b.minLife || 0) - (a.minLife || 0) || String(a.name || '').localeCompare(String(b.name || ''), 'th')); // % มาก → น้อย
    return { rows: rowsO, used };
  }, [retailers, state.items, state.stock]);

  return (
    <div className="page-enter">
      <div className="page-header">
        <div>
          <h2 className="page-title">Life Shelf Monitor <span className="muted mono" style={{ fontSize: 14, marginLeft: 8 }}>// พร้อมส่งเข้าห้าง</span></h2>
          <div className="page-subtitle">ตรวจว่าสินค้าที่ขายแต่ละห้างมี Shelf Life เหลือผ่านเกณฑ์ขั้นต่ำหรือไม่</div>
        </div>
        <div className="row">
          <div className="segmented">
            <button className={view === 'retailer' ? 'active' : ''} onClick={() => setView('retailer')}>รายห้าง</button>
            <button className={view === 'overview' ? 'active' : ''} onClick={() => setView('overview')}>ภาพรวมรายสินค้า</button>
          </div>
          <button className="btn btn-ghost" onClick={() => setEditModal('new')}><Ic name="plus" size={14}/>เพิ่มห้าง</button>
        </div>
      </div>

      {view === 'retailer' && (<>
      {/* Retailer selector strip */}
      <div className="card tech" style={{ padding: 12, marginBottom: 14 }}>
        <div className="row" style={{ gap: 10, flexWrap: 'wrap' }}>
          {[...retailers].sort((a, b) => (b.minLife || 0) - (a.minLife || 0) || String(a.name || '').localeCompare(String(b.name || ''), 'th')).map(r => {
            const sm = summaries[r.id] || { reg: 0, ready: 0, notReady: 0 };
            const on = r.id === selId;
            return (
              <button key={r.id} onClick={() => setSelId(r.id)}
                style={{
                  display: 'flex', alignItems: 'center', gap: 10, padding: '8px 12px', borderRadius: 10, cursor: 'pointer',
                  border: `1.5px solid ${on ? 'var(--accent)' : 'var(--border)'}`,
                  background: on ? 'var(--accent-soft)' : 'var(--surface)', minWidth: 172, textAlign: 'left',
                }}>
                <RetailerLogo r={r} size={36}/>
                <div style={{ minWidth: 0 }}>
                  <div style={{ fontWeight: 600, fontSize: 12.5, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{r.name}</div>
                  <div className="mono" style={{ fontSize: 10, color: 'var(--text-muted)' }}>ขั้นต่ำ {r.minLife}% · {sm.reg} SKU</div>
                  {sm.reg > 0 && (
                    <div className="mono" style={{ fontSize: 10, marginTop: 1 }}>
                      <span style={{ color: 'var(--success)' }}>พร้อม {sm.ready}</span>
                      {sm.notReady > 0 && <span style={{ color: 'var(--danger)' }}> · ไม่พร้อม {sm.notReady}</span>}
                    </div>
                  )}
                </div>
              </button>
            );
          })}
          {retailers.length === 0 && <div className="mono" style={{ fontSize: 12, color: 'var(--text-muted)', padding: 8 }}>ยังไม่มีห้าง — กด “เพิ่มห้าง” เพื่อเริ่ม</div>}
        </div>
      </div>

      {sel && (
        <>
          <div className="grid-4" style={{ marginBottom: 14 }}>
            <ReportKPI label="SKU ที่ขึ้นทะเบียน" value={rows.length} color="var(--accent)"/>
            <ReportKPI label="SKU พร้อมส่ง" value={kpiReady} suffix=" SKU" color="var(--success)"/>
            <ReportKPI label="SKU ไม่พร้อมส่ง" value={kpiNot} suffix=" SKU" color="var(--danger)"/>
            <ReportKPI label="ชิ้นพร้อมส่งรวม" value={kpiPassUnits} suffix=" u" color="var(--accent)"/>
          </div>

          <div className="card tech" style={{ padding: 12, marginBottom: 14 }}>
            <div className="row" style={{ gap: 10, flexWrap: 'wrap', alignItems: 'center' }}>
              <RetailerLogo r={sel} size={30}/>
              <div style={{ marginRight: 'auto' }}>
                <div style={{ fontWeight: 700 }}>{sel.name}</div>
                <div className="mono" style={{ fontSize: 10.5, color: 'var(--text-muted)' }}>เกณฑ์ Shelf Life ขั้นต่ำ {sel.minLife}%</div>
              </div>
              <div className="searchbar" style={{ borderRadius: 8, minWidth: 200 }}>
                <Ic name="search" size={14}/>
                <input placeholder="ค้นหา SKU / ชื่อสินค้า..." value={q} onChange={e => setQ(e.target.value)}/>
              </div>
              <div className="segmented">
                <button className={statusF === 'all' ? 'active' : ''} onClick={() => setStatusF('all')}>ทั้งหมด</button>
                <button className={statusF === 'ready' ? 'active' : ''} onClick={() => setStatusF('ready')}>พร้อมส่ง</button>
                <button className={statusF === 'notready' ? 'active' : ''} onClick={() => setStatusF('notready')}>ไม่พร้อม</button>
              </div>
              <button className="btn btn-ghost" onClick={() => setEditModal(sel)}><Ic name="edit" size={14}/>แก้ไขห้าง</button>
              <button className="btn btn-primary" onClick={() => setSkuModal(true)}><Ic name="items" size={14}/>จัดการ SKU ที่ขาย</button>
            </div>
          </div>

          <div className="card tech" style={{ padding: 0 }}>
            {rows.length === 0 ? (
              <div className="empty" style={{ padding: '40px 20px' }}>
                <Ic name="truck" size={28}/>
                ยังไม่ได้ขึ้นทะเบียน SKU สำหรับ {sel.name}
                <div style={{ marginTop: 12 }}>
                  <button className="btn btn-primary btn-sm" onClick={() => setSkuModal(true)}><Ic name="plus" size={13}/>เลือก SKU ที่ขายห้างนี้</button>
                </div>
              </div>
            ) : (
              <div className="table-wrap" style={{ border: 'none', maxHeight: 'calc(100vh - 360px)' }}>
                <table className="data sticky-head">
                  <thead>
                    <tr>
                      <th>SKU</th>
                      <th>ชื่อสินค้า</th>
                      <th style={{ textAlign: 'right' }}>คงเหลือรวม</th>
                      <th style={{ textAlign: 'right' }}>ผ่านเกณฑ์</th>
                      <th style={{ textAlign: 'right' }}>ไม่ผ่าน</th>
                      <th style={{ textAlign: 'right' }}>Life% ต่ำสุดที่ผ่าน</th>
                      <th style={{ textAlign: 'right' }}>นับถอยหลัง</th>
                      <th>สถานะ</th>
                    </tr>
                  </thead>
                  <tbody>
                    {filtered.map(r => (
                        <React.Fragment key={r.sku}>
                          <tr>
                            <td className="mono" style={{ color: 'var(--accent)', whiteSpace: 'nowrap' }}>{r.sku}</td>
                            <td>{r.name}</td>
                            <td className="num">{r.totalQty.toLocaleString()}</td>
                            <td className="num" style={{ color: r.passQty > 0 ? 'var(--success)' : 'var(--text-dim)', fontWeight: 700 }}>{r.passQty.toLocaleString()}</td>
                            <td className="num" style={{ color: r.failQty > 0 ? 'var(--danger)' : 'var(--text-dim)' }}>{r.failQty.toLocaleString()}</td>
                            <td className="num">{r.minPassPct != null ? <span style={{ color: lifeColor(r.minPassPct), fontWeight: 700 }}>{r.minPassPct}%</span> : <span style={{ color: 'var(--text-dim)' }}>—</span>}</td>
                            <td className="num">
                              {r.status !== 'ready' ? <span style={{ color: 'var(--text-dim)' }}>—</span>
                                : !r.perishablePass ? <span className="mono" style={{ fontSize: 10.5, color: 'var(--success)' }}>ไม่มีวันหมดอายุ</span>
                                : <span style={{ color: dayColor(r.minDays), fontWeight: 700 }}>เหลือ {r.minDays.toLocaleString()} วัน</span>}
                            </td>
                            <td>
                              {r.status === 'ready' ? <span className="badge ok">พร้อมส่ง</span>
                                : r.status === 'fail' ? <span className="badge crit">ไม่มีสินค้าพร้อมส่งตามเกณฑ์</span>
                                : <span className="badge neutral">ไม่มีสต็อก</span>}
                            </td>
                          </tr>
                          {r.lotList.length > 0 && (() => {
                            // group passing lots by countdown days → total qty per group
                            const gmap = {};
                            let nonPerish = 0;
                            r.lotList.forEach(L => {
                              if (!L.pass) return;
                              if (L.pct == null || L.days == null) { nonPerish += L.qty; return; }
                              if (!gmap[L.days]) gmap[L.days] = { days: L.days, pct: L.pct, qty: 0 };
                              gmap[L.days].qty += L.qty;
                              if (L.pct < gmap[L.days].pct) gmap[L.days].pct = L.pct;
                            });
                            const groups = Object.values(gmap).sort((a, b) => a.days - b.days);
                            return (
                              <tr>
                                <td colSpan={8} style={{ background: 'var(--surface-2)', padding: '8px 12px 12px' }}>
                                  <div className="mono" style={{ fontSize: 10, color: 'var(--text-muted)', padding: '2px 0 8px' }}>
                                    จำนวนพร้อมส่ง แยกตามวันคงเหลือก่อนตกเกณฑ์ {sel.minLife}% · เรียงน้อย → มาก
                                  </div>
                                  <div className="row" style={{ gap: 10, flexWrap: 'wrap' }}>
                                    {groups.map((g, i) => (
                                      <div key={i} style={{ display: 'flex', flexDirection: 'column', gap: 3, padding: '8px 14px', borderRadius: 10, border: `1px solid var(--border)`, borderLeft: `3px solid ${dayColor(g.days)}`, background: 'var(--surface)', minWidth: 118 }}>
                                        <div className="row" style={{ gap: 6, alignItems: 'baseline' }}>
                                          <span style={{ fontWeight: 800, fontSize: 17, color: dayColor(g.days), fontFamily: 'var(--font-mono)' }}>{g.days.toLocaleString()}</span>
                                          <span className="mono" style={{ fontSize: 10, color: 'var(--text-muted)' }}>วัน</span>
                                          <span className="mono" style={{ fontSize: 10, fontWeight: 700, color: lifeColor(g.pct), marginLeft: 'auto' }}>{g.pct}%</span>
                                        </div>
                                        <div className="mono" style={{ fontSize: 14, fontWeight: 700 }}>{g.qty.toLocaleString()} <span style={{ fontSize: 10, color: 'var(--text-muted)', fontWeight: 400 }}>ชิ้น</span></div>
                                      </div>
                                    ))}
                                    {nonPerish > 0 && (
                                      <div style={{ display: 'flex', flexDirection: 'column', gap: 3, padding: '8px 14px', borderRadius: 10, border: '1px solid var(--border)', borderLeft: '3px solid var(--success)', background: 'var(--surface)', minWidth: 118 }}>
                                        <div className="mono" style={{ fontSize: 11, color: 'var(--success)' }}>ไม่มีวันหมดอายุ</div>
                                        <div className="mono" style={{ fontSize: 14, fontWeight: 700 }}>{nonPerish.toLocaleString()} <span style={{ fontSize: 10, color: 'var(--text-muted)', fontWeight: 400 }}>ชิ้น</span></div>
                                      </div>
                                    )}
                                  </div>
                                  {r.failQty > 0 && (
                                    <div className="mono" style={{ fontSize: 10.5, color: 'var(--danger)', marginTop: 8 }}>ต่ำกว่าเกณฑ์ (ไม่พร้อมส่ง): {r.failQty.toLocaleString()} ชิ้น</div>
                                  )}
                                </td>
                              </tr>
                            );
                          })()}
                        </React.Fragment>
                      ))}
                    {filtered.length === 0 && (
                      <tr><td colSpan={8} className="mono" style={{ textAlign: 'center', color: 'var(--text-muted)', padding: 20 }}>ไม่มีรายการในเงื่อนไขที่เลือก</td></tr>
                    )}
                  </tbody>
                </table>
              </div>
            )}
          </div>
        </>
      )}
      </>)}

      {view === 'overview' && (() => {
        const ovCats = ['all', ...[...new Set(overview.rows.map(r => r.category))]];
        const ovRows = overview.rows.filter(r =>
          (ovCat === 'all' || r.category === ovCat) &&
          (!ovQ || r.sku.toLowerCase().includes(ovQ.toLowerCase()) || (r.name || '').toLowerCase().includes(ovQ.toLowerCase())));
        return (<>
        {overview.used.length > 0 && (
          <div className="card tech" style={{ padding: 12, marginBottom: 14 }}>
            <div className="row" style={{ gap: 10, flexWrap: 'wrap', alignItems: 'center' }}>
              <div className="searchbar" style={{ borderRadius: 8, minWidth: 220, flex: 1, maxWidth: 360 }}>
                <Ic name="search" size={14}/>
                <input placeholder="ค้นหา SKU / ชื่อสินค้า..." value={ovQ} onChange={e => setOvQ(e.target.value)}/>
              </div>
              <select className="select" style={{ width: 200 }} value={ovCat} onChange={e => setOvCat(e.target.value)}>
                {ovCats.map(c => <option key={c} value={c}>{c === 'all' ? 'ทุกหมวด' : c}</option>)}
              </select>
              <span className="mono" style={{ fontSize: 11, color: 'var(--text-muted)', marginLeft: 'auto' }}>{ovRows.length} / {overview.rows.length} SKU</span>
            </div>
          </div>
        )}
        <div className="card tech" style={{ padding: 0 }}>
          {overview.rows.length === 0 || overview.used.length === 0 ? (
            <div className="empty" style={{ padding: '40px 20px' }}>
              <Ic name="truck" size={28}/>
              ยังไม่มี SKU ที่ลงทะเบียนขายกับห้างใด — ไปที่มุมมอง “รายห้าง” แล้วกด “จัดการ SKU ที่ขาย”
            </div>
          ) : (
            <div className="table-wrap" style={{ border: 'none', maxHeight: 'calc(100vh - 360px)' }}>
              <table className="data sticky-head">
                <thead>
                  <tr>
                    <th style={{ position: 'sticky', left: 0, top: 0, zIndex: 5, background: 'var(--surface)', minWidth: 220 }}>สินค้า · หมวด</th>
                    <th style={{ textAlign: 'right' }}>คงเหลือรวม</th>
                    {overview.used.map(r => (
                      <th key={r.id} style={{ textAlign: 'center', whiteSpace: 'nowrap' }}>
                        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2 }}>
                          <RetailerLogo r={r} size={22}/>
                          <span style={{ fontSize: 10, fontWeight: 600 }}>{r.name}</span>
                          <span className="mono" style={{ fontSize: 9, color: 'var(--text-muted)' }}>≥{r.minLife}%</span>
                        </div>
                      </th>
                    ))}
                  </tr>
                </thead>
                <tbody>
                  {ovRows.length === 0 && (
                    <tr><td colSpan={overview.used.length + 2} className="mono" style={{ textAlign: 'center', color: 'var(--text-muted)', padding: 20 }}>ไม่มีสินค้าในเงื่อนไขที่เลือก</td></tr>
                  )}
                  {ovRows.map(row => (
                    <tr key={row.sku}>
                      <td style={{ position: 'sticky', left: 0, zIndex: 2, background: 'var(--surface)', borderLeft: `4px solid hsl(${row.hue}, 62%, 52%)` }}>
                        <span style={{ display: 'inline-block', padding: '3px 9px', borderRadius: 6, background: row.shade, color: row.textOn, fontWeight: 600, fontSize: 12 }}>{row.name}</span>
                        <div className="mono" style={{ fontSize: 9, color: 'var(--text-muted)', marginTop: 3 }}>{row.sku} · {row.category}</div>
                      </td>
                      <td className="num" style={{ color: 'var(--text-secondary)' }}>{row.total.toLocaleString()}</td>
                      {overview.used.map(r => {
                        const has = Object.prototype.hasOwnProperty.call(row.sold, r.id);
                        if (!has) return <td key={r.id} style={{ textAlign: 'center', color: 'var(--text-dim)' }}>·</td>;
                        const pass = row.sold[r.id];
                        return <td key={r.id} className="num" style={{ color: pass > 0 ? 'var(--success)' : 'var(--danger)', fontWeight: 700 }}>{pass.toLocaleString()}</td>;
                      })}
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
        </>);
      })()}

      {skuModal && sel && <RetailerSkuModal retailer={sel} onClose={() => setSkuModal(false)}/>}
      {editModal && <RetailerEditModal retailer={editModal === 'new' ? null : editModal}
        onClose={() => setEditModal(null)}
        onDeleted={(id) => { if (id === selId) setSelId((state.retailers || []).find(r => r.id !== id)?.id || null); }}/>}
    </div>
  );
}

// Register which SKUs a retailer sells (checkbox list)
function RetailerSkuModal({ retailer, onClose }) {
  const store = useStore();
  const { state } = store;
  const [picked, setPicked] = useRS(() => new Set(retailer.skus || []));
  const [q, setQ] = useRS('');
  const items = [...state.items].sort((a, b) => String(a.name_th || '').localeCompare(String(b.name_th || ''), 'th', { numeric: true }));
  const shown = items.filter(i => !q || i.sku.toLowerCase().includes(q.toLowerCase()) || (i.name_th || '').toLowerCase().includes(q.toLowerCase()) || (i.name || '').toLowerCase().includes(q.toLowerCase()));
  const toggle = (sku) => setPicked(p => { const n = new Set(p); n.has(sku) ? n.delete(sku) : n.add(sku); return n; });
  const allShown = shown.length > 0 && shown.every(i => picked.has(i.sku));
  const toggleAll = () => setPicked(p => { const n = new Set(p); shown.forEach(i => allShown ? n.delete(i.sku) : n.add(i.sku)); return n; });

  return (
    <Modal open onClose={onClose} title={`เลือก SKU ที่ขาย · ${retailer.name}`}
      footer={(<>
        <span className="mono" style={{ fontSize: 11, color: 'var(--text-muted)', marginRight: 'auto' }}>เลือกแล้ว {picked.size} SKU</span>
        <button className="btn btn-ghost" onClick={onClose}>ยกเลิก</button>
        <button className="btn btn-primary" onClick={() => { store.setRetailerSkus(retailer.id, [...picked]); onClose(); }}><Ic name="check" size={14}/>บันทึก</button>
      </>)}>
      <div className="searchbar" style={{ borderRadius: 8, marginBottom: 10, maxWidth: 'none' }}>
        <Ic name="search" size={14}/>
        <input placeholder="ค้นหา SKU / ชื่อสินค้า..." value={q} onChange={e => setQ(e.target.value)} autoFocus/>
      </div>
      <div className="row" style={{ justifyContent: 'space-between', marginBottom: 6 }}>
        <button className="btn btn-ghost btn-sm" onClick={toggleAll}>{allShown ? 'ยกเลิกทั้งหมด' : 'เลือกทั้งหมด'} ({shown.length})</button>
      </div>
      <div style={{ maxHeight: 380, overflowY: 'auto', display: 'flex', flexDirection: 'column', gap: 2 }}>
        {shown.map(i => (
          <label key={i.sku} className="row" style={{ gap: 10, padding: '7px 8px', borderRadius: 7, cursor: 'pointer', background: picked.has(i.sku) ? 'var(--accent-soft)' : 'transparent' }}>
            <input type="checkbox" checked={picked.has(i.sku)} onChange={() => toggle(i.sku)}/>
            <span style={{ flex: 1, minWidth: 0 }}>
              <span style={{ fontSize: 12.5 }}>{i.name_th}</span>
              <span className="mono" style={{ fontSize: 10, color: 'var(--text-muted)', marginLeft: 8 }}>{i.sku}</span>
            </span>
            <span className="badge neutral" style={{ fontSize: 9 }}>{i.category}</span>
          </label>
        ))}
        {shown.length === 0 && <div className="mono" style={{ fontSize: 11, color: 'var(--text-muted)', padding: 14, textAlign: 'center' }}>ไม่พบสินค้า</div>}
      </div>
    </Modal>
  );
}

// Add / edit a retailer (name, minimum life %, logo)
function RetailerEditModal({ retailer, onClose, onDeleted }) {
  const store = useStore();
  const isNew = !retailer;
  const [form, setForm] = useRS(() => retailer ? { ...retailer } : { name: '', minLife: 70, logo: '' });
  const logos = (window.WMSData && window.WMSData.LOGO_FILES) || [];
  const save = () => {
    if (!String(form.name).trim()) return;
    if (isNew) store.addRetailer(form); else store.updateRetailer(retailer.id, { name: form.name, minLife: form.minLife, logo: form.logo });
    onClose();
  };
  const remove = () => {
    if (!confirm(`ลบห้าง ${retailer.name}? (SKU ที่ขึ้นทะเบียนไว้จะถูกลบด้วย)`)) return;
    store.deleteRetailer(retailer.id);
    onDeleted && onDeleted(retailer.id);
    onClose();
  };
  return (
    <Modal open onClose={onClose} title={isNew ? 'เพิ่มห้างใหม่' : `แก้ไขห้าง · ${retailer.name}`}
      footer={(<>
        {!isNew && <button className="btn btn-ghost" style={{ color: 'var(--danger)', marginRight: 'auto' }} onClick={remove}><Ic name="trash" size={14}/>ลบห้าง</button>}
        <button className="btn btn-ghost" onClick={onClose}>ยกเลิก</button>
        <button className="btn btn-primary" onClick={save} disabled={!String(form.name).trim()}><Ic name="check" size={14}/>บันทึก</button>
      </>)}>
      <div className="grid-2">
        <div className="field"><label>ชื่อห้าง</label><input className="input" value={form.name} onChange={e => setForm({ ...form, name: e.target.value })} placeholder="เช่น WATSON"/></div>
        <div className="field"><label>Shelf Life ขั้นต่ำ (%)</label><input className="input mono" type="number" min="0" max="100" value={form.minLife} onChange={e => setForm({ ...form, minLife: e.target.value })}/></div>
        <div className="field" style={{ gridColumn: '1 / 3' }}>
          <label>โลโก้ห้าง</label>
          <div className="row" style={{ gap: 10 }}>
            <RetailerLogo r={form} size={44}/>
            <select className="select" style={{ flex: 1 }} value={form.logo} onChange={e => setForm({ ...form, logo: e.target.value })}>
              <option value="">— ไม่มีโลโก้ (ใช้ตัวอักษรย่อ) —</option>
              {logos.map(f => <option key={f} value={f}>{f}</option>)}
            </select>
          </div>
        </div>
      </div>
    </Modal>
  );
}

Object.assign(window, { PageStockReport, PageMovement, PageDocuments, PageLifeShelf });
