/* Internal STOCK — our own blank-garment inventory (hoodies, tees, sweatshirts,
   caps…) that we press transfers onto. CRM-owned, hand-editable. Separate from
   the Shopify inventory (that one is fed by the Rallawise supplier integration).

   Phase 1 (this view): list, add, edit any field inline, bump quantities up/down
   or type an exact count, delete. Talks to /api/stock via DTF.stock* helpers.
   Phase 2 (later): quantities move on their own — deduct on dispatch, top up when
   we buy from Rallawise. The adjust endpoint already records a reason for that. */

const STOCK_LOW = 2; // qty <= this flags "low"; 0 flags "out"

// One editable text cell: uncontrolled input that saves onBlur / Enter only when
// the value actually changed. Keyed by id+field so React keeps the DOM node.
function StockCell({ value, placeholder, onSave, width, mono }) {
  const [busy, setBusy] = useState(false);
  const commit = async (el) => {
    const next = el.value.trim();
    if (next === (value || "")) return;
    setBusy(true);
    try { await onSave(next); } catch (e) { el.value = value || ""; alert(e.message); }
    finally { setBusy(false); }
  };
  return (
    <input
      className="stk-in"
      defaultValue={value || ""}
      placeholder={placeholder || ""}
      disabled={busy}
      onBlur={e => commit(e.target)}
      onKeyDown={e => { if (e.key === "Enter") e.target.blur(); if (e.key === "Escape") { e.target.value = value || ""; e.target.blur(); } }}
      style={{ width: width || "100%", fontFamily: mono ? "var(--font-mono)" : "inherit" }}
    />
  );
}

// Debounce a changing value by `ms` (for the SKU type-ahead).
function useDebounced(value, ms) {
  const [v, setV] = useState(value);
  useEffect(() => { const t = setTimeout(() => setV(value), ms); return () => clearTimeout(t); }, [value, ms]);
  return v;
}

// Dynamic Shopify-SKU picker. Type to search the live catalogue; results are
// grouped by product with colour/size variants (and live stock) as a sublist.
// If nothing matches, offer to import that style code from Ralawise (which adds
// it to Shopify, then the daily sync — or a manual sync — fills its stock).
function SkuSelect({ value, seed, onPick }) {
  const [open, setOpen] = useState(false);
  const [q, setQ] = useState("");
  const [res, setRes] = useState(null);      // null = idle, [] = searched-empty
  const [busy, setBusy] = useState(false);
  const [imp, setImp] = useState(null);      // import feedback message
  const dq = useDebounced(q, 250);
  useEffect(() => {
    if (!open) return;
    if (dq.trim().length < 2) { setRes(null); return; }
    let alive = true; setBusy(true);
    DTF.stockSkuSearch(dq).then(r => { if (alive) { setRes(r); setBusy(false); } })
      .catch(() => { if (alive) { setRes([]); setBusy(false); } });
    return () => { alive = false; };
  }, [dq, open]);
  const groups = useMemo(() => {
    const m = new Map();
    (res || []).forEach(v => { if (!m.has(v.product)) m.set(v.product, []); m.get(v.product).push(v); });
    return [...m.entries()];
  }, [res]);
  // The dropdown is rendered fixed (escapes the table's overflow clipping) and
  // anchored to the cell; flips upward when there isn't room below.
  const boxRef = useRef(null);
  const [pos, setPos] = useState(null);
  const computePos = () => {
    const el = boxRef.current; if (!el) return;
    const r = el.getBoundingClientRect();
    const below = window.innerHeight - r.bottom;
    const flip = below < 300 && r.top > below;
    const left = Math.max(8, Math.min(r.left, window.innerWidth - 368));
    setPos(flip
      ? { left, bottom: window.innerHeight - r.top + 3, maxHeight: Math.min(340, r.top - 12) }
      : { left, top: r.bottom + 3, maxHeight: Math.min(340, below - 12) });
  };
  useEffect(() => {
    if (!open) { setPos(null); return; }
    computePos();
    const h = () => computePos();
    window.addEventListener("scroll", h, true);
    window.addEventListener("resize", h);
    return () => { window.removeEventListener("scroll", h, true); window.removeEventListener("resize", h); };
  }, [open]);
  // Seed the search from the current SKU, else the first token of the internal
  // code (e.g. "BY087 ONR18-052" → "BY087"). Codes often differ from the Shopify
  // SKU prefix, so this is just a starting point — the product/brand search below
  // finds it either way.
  const openNow = () => { setImp(null); setQ(value || (seed || "").split(/\s+/)[0] || ""); setOpen(true); };
  const pick = async (sku) => { setBusy(true); try { await onPick(sku); setOpen(false); } catch (e) { alert(e.message); } finally { setBusy(false); } };
  const doImport = async () => {
    const code = q.trim(); if (!code) return;
    setImp("Starting import…");
    try { await DTF.ralawiseImport(code, false); setImp(`Importing “${code}” from Ralawise (with its stock) — a few minutes on GitHub. Search again once it’s done.`); }
    catch (e) { setImp("Failed: " + e.message); }
  };
  return (
    <div ref={boxRef} style={{ position: "relative" }}>
      {!open
        ? <button className="stk-in" style={{ textAlign: "left", cursor: "pointer", fontFamily: "var(--font-mono)", color: value ? "inherit" : "#b7b2a6" }} onClick={openNow}>{value || "— link SKU —"}</button>
        : <input autoFocus className="stk-in" style={{ fontFamily: "var(--font-mono)" }} value={q} placeholder="Search SKU / colour / size…"
                 onChange={e => setQ(e.target.value)} onKeyDown={e => { if (e.key === "Escape") setOpen(false); }}
                 onBlur={() => setTimeout(() => setOpen(false), 200)} />}
      {open && pos && (
        <div className="stk-skus" onMouseDown={e => e.preventDefault()}
             style={{ position: "fixed", left: pos.left, top: pos.top, bottom: pos.bottom, maxHeight: pos.maxHeight, margin: 0, zIndex: 60 }}>
          {value && <div className="stk-skus__row stk-skus__unlink" onClick={() => pick("")}>✕ Unlink {value}</div>}
          {q.trim().length < 2 && <div className="stk-skus__hint">Type at least 2 characters…</div>}
          {busy && <div className="stk-skus__hint">Searching…</div>}
          {!busy && res && res.length === 0 && q.trim().length >= 2 && (
            <div className="stk-skus__none">
              <div>No Shopify SKU matches “{q.trim()}”.</div>
              <button className="btn btn--primary btn--sm" style={{ marginTop: 8 }} onClick={doImport}>Import “{q.trim()}” from Ralawise</button>
              {imp && <div className="stk-skus__hint" style={{ marginTop: 6 }}>{imp}</div>}
            </div>
          )}
          {!busy && groups.map(([prod, vars]) => (
            <div key={prod} className="stk-skus__grp">
              <div className="stk-skus__ghead">{prod}</div>
              {vars.map(v => (
                <div key={v.sku} className="stk-skus__row" onClick={() => pick(v.sku)}>
                  <span className="stk-skus__opt">{[v.colour, v.size].filter(Boolean).join(" · ") || "—"}</span>
                  <span className="stk-skus__sku">{v.sku}</span>
                  <span className="stk-skus__stk" title="Rallawise stock in Shopify">{v.qty}</span>
                </div>
              ))}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// − [n] +  quantity stepper. Optimistic + focus-stable: the number updates in the
// DOM immediately (uncontrolled input, no remount) and a single save is debounced,
// so rapid ± clicks or typing in a big list never lose focus or block. The absolute
// count is pushed via stockUpdate (which also mirrors to the DTF.UK Shopify location).
function QtyStepper({ item, onChange }) {
  const ref = useRef(null);
  const timer = useRef(null);
  const [display, setDisplay] = useState(item.qty); // drives only the low/out colour
  // Sync from outside (reload) — but never clobber the field while it's focused.
  useEffect(() => {
    if (ref.current && document.activeElement !== ref.current) { ref.current.value = item.qty; setDisplay(item.qty); }
  }, [item.id, item.qty]);
  const save = (n) => {
    if (timer.current) clearTimeout(timer.current);
    timer.current = setTimeout(async () => {
      try { const it = await DTF.stockUpdate(item.id, { qty: n }); onChange(it); }
      catch (e) { alert(e.message); if (ref.current) ref.current.value = item.qty; setDisplay(item.qty); }
    }, 400);
  };
  const cur = () => Math.max(0, Math.round(Number(ref.current && ref.current.value) || 0));
  const bump = (d) => { const n = Math.max(0, cur() + d); if (ref.current) ref.current.value = n; setDisplay(n); save(n); };
  const commit = () => { const n = cur(); if (ref.current) ref.current.value = n; setDisplay(n); if (n !== item.qty) save(n); };
  const out = display <= 0, low = display > 0 && display <= STOCK_LOW;
  return (
    <div style={{ display: "inline-flex", alignItems: "center", gap: 4 }}>
      <button className="btn btn--ghost btn--sm stk-step" disabled={display <= 0} title="−1" onClick={() => bump(-1)}>−</button>
      <input ref={ref} className="stk-in stk-qty" type="number" min="0" defaultValue={item.qty}
             onChange={() => setDisplay(cur())} onBlur={commit}
             onKeyDown={e => { if (e.key === "Enter") e.target.blur(); }}
             style={{ color: out ? "var(--danger, #b91c1c)" : low ? "#b45309" : "inherit", fontWeight: 600 }} />
      <button className="btn btn--ghost btn--sm stk-step" title="+1" onClick={() => bump(1)}>+</button>
    </div>
  );
}

function StockView() {
  const [items, setItems] = useState(null);
  const [err, setErr] = useState(null);
  const [query, setQuery] = useState("");
  const [typeFilter, setTypeFilter] = useState("all");
  const [lowOnly, setLowOnly] = useState(false);
  const [adding, setAdding] = useState(false);
  const [pendingDel, setPendingDel] = useState(null);
  const [reloading, setReloading] = useState(false);
  const [levels, setLevels] = useState(null);   // { sku: {available, product} } from Shopify
  const [syncing, setSyncing] = useState(false);

  const load = async () => {
    try { const r = await DTF.stockList(); setItems(r.items || []); setErr(null); }
    catch (e) { setErr(e.message); }
  };
  const reloadLevels = () => DTF.stockShopify().then(setLevels).catch(() => setLevels({}));
  useEffect(() => { load(); reloadLevels(); }, []);

  const syncRalawise = async () => {
    if (syncing) return;
    setSyncing(true);
    try { await DTF.ralawiseSync(); alert("Ralawise → Shopify stock sync started on GitHub. It takes a few minutes; reload after to see updated Rallawise numbers."); }
    catch (e) { alert(e.message); }
    finally { setSyncing(false); }
  };

  // Replace one item in place (after an inline edit / stepper), or drop it (null).
  const patchLocal = (id, next) => setItems(list => {
    if (!list) return list;
    if (next === null) return list.filter(i => i.id !== id);
    return list.map(i => i.id === id ? next : i);
  });

  const types = useMemo(() => {
    const set = new Map(); // preserve order, count
    (items || []).forEach(i => set.set(i.type || "—", (set.get(i.type || "—") || 0) + 1));
    return [...set.entries()].sort((a, b) => b[1] - a[1]);
  }, [items]);

  const q = query.trim().toLowerCase();
  const rows = useMemo(() => {
    let list = (items || []).slice();
    if (typeFilter !== "all") list = list.filter(i => (i.type || "—") === typeFilter);
    if (lowOnly) list = list.filter(i => i.qty <= STOCK_LOW);
    if (q) list = list.filter(i => [i.type, i.sex, i.colour, i.size, i.brand, i.code, i.notes]
      .filter(Boolean).join(" ").toLowerCase().includes(q));
    return list;
  }, [items, typeFilter, lowOnly, q]);

  const totalUnits = (items || []).reduce((n, i) => n + (i.qty || 0), 0);
  const lowCount = (items || []).filter(i => i.qty <= STOCK_LOW).length;
  const outCount = (items || []).filter(i => i.qty <= 0).length;
  const shownUnits = rows.reduce((n, i) => n + (i.qty || 0), 0);

  const doReload = async () => { setReloading(true); await load(); setReloading(false); };

  const addItem = async (draft) => {
    const it = await DTF.stockCreate(draft);
    setItems(list => [it, ...(list || [])]);
    setAdding(false);
  };
  const del = async (id) => {
    try { await DTF.stockDelete(id); patchLocal(id, null); setPendingDel(null); }
    catch (e) { alert(e.message); }
  };

  if (err) return <div className="board"><div className="warn-banner"><Icon name="spark" /> Couldn’t load stock — {err}. <button className="linklike" onClick={doReload}>Retry</button></div></div>;

  return (
    <div className="board">
      <div className="stats">
        <div className="stat"><div className="stat__k">Stock lines</div><div className="stat__v">{items ? items.length : "—"}</div><div className="stat__d">{types.length} garment type{types.length===1?"":"s"}</div></div>
        <div className="stat"><div className="stat__k">Total units</div><div className="stat__v">{items ? totalUnits : "—"}</div><div className="stat__d">on the shelf</div></div>
        <div className="stat stat--clickable" onClick={() => setLowOnly(v => !v)} style={{cursor:"pointer"}} title="Show only low / out-of-stock lines">
          <div className="stat__k">Low stock</div>
          <div className="stat__v" style={lowCount?{color:"#b45309"}:null}>{items ? lowCount : "—"}</div>
          <div className="stat__d">≤ {STOCK_LOW} left{outCount?` · ${outCount} out`:""}</div>
        </div>
        <div className="stat"><div className="stat__k">Linked to Ralawise</div><div className="stat__v">{items ? items.filter(i=>i.sku).length : "—"}</div><div className="stat__d">of {items?items.length:"—"} lines carry a SKU</div></div>
      </div>

      <div className="toolbar">
        <div className="seg">
          <button className={typeFilter==="all"?"is-active":""} onClick={()=>setTypeFilter("all")}>All{items?` · ${items.length}`:""}</button>
          {types.map(([t, n]) => (
            <button key={t} className={typeFilter===t?"is-active":""} onClick={()=>setTypeFilter(t)}>{t} · {n}</button>
          ))}
        </div>
        <div className="ord-search">
          <Icon name="search" />
          <input type="search" value={query} onChange={e=>setQuery(e.target.value)} placeholder="Search brand, colour, size, code…" />
          {query && <button className="ord-search__clear" title="Clear" onClick={()=>setQuery("")}>×</button>}
        </div>
        <span className="kick right">{rows.length} line{rows.length===1?"":"s"} · {shownUnits} unit{shownUnits===1?"":"s"}{lowOnly?" · low only":""}</span>
        <button className="btn btn--primary btn--sm" onClick={()=>setAdding(a=>!a)}><Icon name="plus" /> Add item</button>
        <button className="btn btn--ghost btn--sm" onClick={syncRalawise} disabled={syncing} title="Trigger the Ralawise → Shopify stock sync (GitHub Action)"><Icon name="download" /> {syncing?"…":"Sync Ralawise"}</button>
        <button className="btn btn--ghost btn--sm" onClick={()=>{doReload(); reloadLevels();}} disabled={reloading} title="Reload stock"><Icon name="refresh" /> {reloading?"…":"Reload"}</button>
      </div>

      <div className="tbl-wrap">
      <table className="tbl stk-tbl">
        <thead>
          <tr>
            <th>Type</th><th>Sex</th><th>Colour</th><th>Size</th><th>Brand</th><th>Code</th>
            <th title="Linked Shopify variant SKU — the Ralawise catalogue product / colour / size">Shopify SKU</th>
            <th className="num" style={{width:120}} title="Our own shelf — editable; pushed to the DTF.UK Shopify location">DTF.UK</th>
            <th className="num" title="Rallawise dropship stock (Rallawise Shopify location)">Rallawise</th>
            <th className="num" title="DTF.UK + Rallawise">Total</th>
            <th>Notes</th><th style={{width:70}}></th>
          </tr>
        </thead>
        <tbody>
          {adding && <StockAddRow onAdd={addItem} onCancel={()=>setAdding(false)} />}
          {items == null
            ? Array.from({length:8}).map((_,i)=>(<tr key={"sk"+i}><td colSpan={12} style={{padding:"10px 12px"}}><div className="skel skel-bar" style={{height:18,opacity:Math.max(0.35,1-i*0.08)}} /></td></tr>))
            : rows.length === 0
              ? <tr><td colSpan={12}><div className="empty" style={{padding:"28px 12px"}}><Icon name="tag" /><div className="t">{items.length?"No matches":"No stock yet"}</div><div className="m">{items.length?"Try another filter or search.":"Add your first garment with “Add item”."}</div></div></td></tr>
              : rows.map(it => {
                  const out = it.qty <= 0, low = it.qty>0 && it.qty<=STOCK_LOW;
                  return (
                    <tr key={it.id} style={out?{background:"#fdf2f2"}:low?{background:"#fdf7ee"}:null}>
                      <td><StockCell value={it.type} placeholder="Type" onSave={v=>DTF.stockUpdate(it.id,{type:v}).then(x=>patchLocal(it.id,x))} /></td>
                      <td><StockCell value={it.sex} placeholder="—" width={72} onSave={v=>DTF.stockUpdate(it.id,{sex:v}).then(x=>patchLocal(it.id,x))} /></td>
                      <td><StockCell value={it.colour} placeholder="—" onSave={v=>DTF.stockUpdate(it.id,{colour:v}).then(x=>patchLocal(it.id,x))} /></td>
                      <td><StockCell value={it.size} placeholder="—" width={64} mono onSave={v=>DTF.stockUpdate(it.id,{size:v}).then(x=>patchLocal(it.id,x))} /></td>
                      <td><StockCell value={it.brand} placeholder="Brand" onSave={v=>DTF.stockUpdate(it.id,{brand:v}).then(x=>patchLocal(it.id,x))} /></td>
                      <td><StockCell value={it.code} placeholder="—" mono onSave={v=>DTF.stockUpdate(it.id,{code:v}).then(x=>patchLocal(it.id,x))} /></td>
                      <td>
                        <div style={{display:"flex",alignItems:"center",gap:4}}>
                          <div style={{flex:1,minWidth:0}}><SkuSelect value={it.sku} seed={it.code} onPick={sku=>DTF.stockUpdate(it.id,{sku}).then(x=>{patchLocal(it.id,x); reloadLevels();})} /></div>
                          {it.sku && levels && levels[it.sku] && levels[it.sku].adminUrl &&
                            <a className="icon-btn" href={levels[it.sku].adminUrl} target="_blank" rel="noopener noreferrer" title="View product in Shopify" style={{flex:"none"}}><Icon name="external" /></a>}
                        </div>
                      </td>
                      <td className="num"><QtyStepper item={it} onChange={x=>patchLocal(it.id,x)} /></td>
                      <td className="num">{!it.sku ? <span style={{color:"#c3bdb2"}}>—</span> : (levels && levels[it.sku]) ? (levels[it.sku].ralawise ?? 0) : <span style={{color:"#c3bdb2"}} title="loading…">·</span>}</td>
                      <td className="num" style={{fontWeight:600}}>{(() => { const rw = it.sku && levels ? levels[it.sku] : null; return (it.qty||0) + (rw ? (rw.ralawise||0) : 0); })()}</td>
                      <td><StockCell value={it.notes} placeholder="—" onSave={v=>DTF.stockUpdate(it.id,{notes:v}).then(x=>patchLocal(it.id,x))} /></td>
                      <td style={{textAlign:"right"}}>
                        {pendingDel===it.id
                          ? <span style={{whiteSpace:"nowrap"}}>
                              <button className="btn btn--sm" style={{background:"#b91c1c",color:"#fff"}} onClick={()=>del(it.id)}>Delete</button>
                              <button className="linklike" style={{marginLeft:6}} onClick={()=>setPendingDel(null)}>no</button>
                            </span>
                          : <button className="icon-btn" title="Remove line" onClick={()=>setPendingDel(it.id)}><Icon name="x" /></button>}
                      </td>
                    </tr>
                  );
                })}
        </tbody>
      </table>
      </div>
    </div>
  );
}

// Inline "add a garment" row shown at the top of the table.
function StockAddRow({ onAdd, onCancel }) {
  const [d, setD] = useState({ type:"", sex:"unisex", colour:"", size:"", brand:"", code:"", sku:"", qty:1, notes:"" });
  const [busy, setBusy] = useState(false);
  const set = (k) => (e) => setD(x => ({ ...x, [k]: e.target.value }));
  const submit = async () => {
    if (!d.type.trim() && !d.brand.trim()) { alert("Give at least a type or brand."); return; }
    setBusy(true);
    try { await onAdd({ ...d, qty: Math.max(0, Math.round(Number(d.qty)||0)) }); }
    catch (e) { alert(e.message); setBusy(false); }
  };
  const cell = (k, ph, w, mono) => (
    <input className="stk-in" placeholder={ph} value={d[k]} onChange={set(k)}
           onKeyDown={e=>{ if(e.key==="Enter") submit(); if(e.key==="Escape") onCancel(); }}
           style={{ width: w||"100%", fontFamily: mono?"var(--font-mono)":"inherit" }} />
  );
  return (
    <tr style={{ background: "var(--mint-wash, #f0fdf7)" }}>
      <td>{cell("type","Type e.g. T-shirt")}</td>
      <td>{cell("sex","sex",72)}</td>
      <td>{cell("colour","colour")}</td>
      <td>{cell("size","size",64,true)}</td>
      <td>{cell("brand","Brand")}</td>
      <td>{cell("code","code",undefined,true)}</td>
      <td>{cell("sku","Shopify SKU",undefined,true)}</td>
      <td className="num"><input className="stk-in stk-qty" type="number" min="0" value={d.qty} onChange={set("qty")} onKeyDown={e=>{ if(e.key==="Enter") submit(); }} style={{fontWeight:600}} title="Local (shelf) qty" /></td>
      <td className="num"><span style={{color:"#c3bdb2"}}>—</span></td>
      <td className="num"><span style={{color:"#c3bdb2"}}>—</span></td>
      <td>{cell("notes","notes")}</td>
      <td style={{textAlign:"right",whiteSpace:"nowrap"}}>
        <button className="btn btn--primary btn--sm" disabled={busy} onClick={submit}>{busy?"…":"Add"}</button>
        <button className="linklike" style={{marginLeft:6}} onClick={onCancel}>cancel</button>
      </td>
    </tr>
  );
}

// Re-Stock forecast: garments that sold recently (7/14/30d) vs DTF.UK shelf +
// Rallawise stock, with an AI-projected demand + suggested order qty to cover the
// next 14 days. Multi-select and append the chosen lines to the Rallawise basket.
const FORECAST_COVER = 14;
function ForecastView() {
  const [data, setData] = useState(null);      // { rows, cover, ai } | { rows:[], error }
  const [sel, setSel] = useState(() => new Set());
  const [qty, setQty] = useState({});
  const [busy, setBusy] = useState(false);
  const [msg, setMsg] = useState(null);
  const load = () => {
    setData(null); setMsg(null);
    DTF.stockForecast(FORECAST_COVER).then(r => {
      setData(r);
      const q = {}, s = new Set();
      (r.rows || []).forEach(row => { q[row.sku] = row.suggest; if (row.suggest > 0) s.add(row.sku); });
      setQty(q); setSel(s);
    }).catch(e => setData({ rows: [], error: e.message }));
  };
  useEffect(() => { load(); }, []);
  const rows = (data && data.rows) || [];
  const toggle = (sku) => setSel(s => { const n = new Set(s); n.has(sku) ? n.delete(sku) : n.add(sku); return n; });
  const allSel = rows.length > 0 && rows.every(r => sel.has(r.sku));
  const toggleAll = () => setSel(allSel ? new Set() : new Set(rows.map(r => r.sku)));
  const setQ = (sku, v) => setQty(m => ({ ...m, [sku]: Math.max(0, Math.round(Number(v) || 0)) }));
  const chosen = rows.filter(r => sel.has(r.sku) && (qty[r.sku] || 0) > 0).map(r => ({ sku: r.sku, qty: qty[r.sku] }));
  const totalUnits = chosen.reduce((n, l) => n + l.qty, 0);
  const addBasket = async () => {
    if (!chosen.length) return;
    setBusy(true); setMsg(null);
    try { const r = await DTF.ralawiseBasket(chosen); setMsg(`Added ${r.count} line${r.count === 1 ? "" : "s"} (${totalUnits} units) to the Rallawise basket — finish payment on ralawise.com.`); }
    catch (e) { setMsg("Failed: " + e.message); }
    finally { setBusy(false); }
  };
  return (
    <div className="board">
      <div className="toolbar">
        <span className="kick">Projected to cover the next <b>{FORECAST_COVER} days</b> · {data && data.ai ? <span className="badge badge--mint">AI plan</span> : data && !data.ai && rows.length ? <span className="badge badge--ghost" title="AI unavailable — using sales run-rate">run-rate</span> : ""}</span>
        <span className="kick right">{rows.length} garment{rows.length===1?"":"s"} sold (30d) · {chosen.length} selected · {totalUnits} to order</span>
        <button className="btn btn--primary btn--sm" disabled={busy || !chosen.length} onClick={addBasket}
                title="Append the selected SKUs + qtys to the Rallawise basket (pay on their site)">
          <Icon name="download" /> {busy ? "…" : `Add ${chosen.length} to Rallawise basket`}
        </button>
        <button className="btn btn--ghost btn--sm" onClick={load} disabled={data===null}><Icon name="refresh" /> Reload</button>
      </div>
      {msg && <div className="warn-banner" style={msg.startsWith("Failed")?null:{background:"var(--mint-wash)",borderColor:"var(--mint)"}}><Icon name="spark" /> {msg}</div>}
      {data && data.error && <div className="warn-banner"><Icon name="spark" /> {data.error}</div>}
      <div className="tbl-wrap">
        <table className="tbl stk-tbl">
          <thead><tr>
            <th style={{width:32}}><input type="checkbox" checked={allSel} onChange={toggleAll} title="Select all" /></th>
            <th>Garment</th><th>Colour</th><th>Size</th><th>SKU</th>
            <th className="num" title="Units sold in the last 7 days">7d</th>
            <th className="num" title="Units sold in the last 14 days">14d</th>
            <th className="num" title="Units sold in the last 30 days">30d</th>
            <th className="num" title="Our DTF.UK shelf">DTF.UK</th>
            <th className="num" title="Rallawise dropship">Rallawise</th>
            <th className="num" title={`AI-projected demand over the next ${FORECAST_COVER} days`}>Proj</th>
            <th className="num" style={{width:110}} title="Suggested order (AI) — editable">Order</th>
            <th style={{width:44}}></th>
          </tr></thead>
          <tbody>
            {data === null
              ? Array.from({length:6}).map((_,i)=>(<tr key={i}><td colSpan={13} style={{padding:"10px 12px"}}><div className="skel skel-bar" style={{height:18,opacity:Math.max(0.35,1-i*0.1)}} /></td></tr>))
              : rows.length === 0 && !data.error
                ? <tr><td colSpan={13}><div className="empty" style={{padding:"28px 12px"}}><Icon name="tag" /><div className="t">No garment sales in the last 30 days</div><div className="m">Sales with a Shopify SKU show up here as reorder candidates.</div></div></td></tr>
                : rows.map(r => {
                    const low = r.local > 0 && r.local <= STOCK_LOW, out = r.local <= 0;
                    return (
                      <tr key={r.sku} style={out?{background:"#fdf2f2"}:low?{background:"#fdf7ee"}:null}>
                        <td><input type="checkbox" checked={sel.has(r.sku)} onChange={()=>toggle(r.sku)} /></td>
                        <td>{r.product || r.sku}</td>
                        <td>{r.colour || "—"}</td>
                        <td style={{fontFamily:"var(--font-mono)"}}>{r.size || "—"}</td>
                        <td style={{fontFamily:"var(--font-mono)",fontSize:11,color:"#6b6559"}}>{r.sku}</td>
                        <td className="num" style={{fontWeight:600}}>{r.sold7}</td>
                        <td className="num">{r.sold14}</td>
                        <td className="num" style={{color:"#8a857a"}}>{r.sold30}</td>
                        <td className="num" style={{color: out?"#b91c1c":low?"#b45309":"inherit", fontWeight:600}}>{r.local}</td>
                        <td className="num">{r.ralawise}</td>
                        <td className="num" title={`Projected demand over ${FORECAST_COVER}d`}>{r.projected != null ? r.projected : "—"}</td>
                        <td className="num"><input className="stk-in stk-qty" type="number" min="0" value={qty[r.sku] ?? 0} onChange={e=>setQ(r.sku, e.target.value)} title="Quantity to add to the Rallawise basket" /></td>
                        <td>{r.adminUrl && <a className="icon-btn" href={r.adminUrl} target="_blank" rel="noopener noreferrer" title="View in Shopify"><Icon name="external" /></a>}</td>
                      </tr>
                    );
                  })}
          </tbody>
        </table>
      </div>
    </div>
  );
}
