/* =============================================================
   CoreIQ Office — Product Detail screen
   Opens when a row is clicked from Stock overview. Shows the full
   picture for one SKU across all stores: stock + locations,
   batches/expiries, pricing, demand, movements, supplier, flags.
   ============================================================= */
const { useState: pdUseState, useMemo: pdUseMemo } = React;

// Small synthetic but deterministic data helpers so each product
// has a plausible 30-day sales curve and a movement history without
// us inflating office-data.js.
function pdSeed(p) {
  const s = (p.id || "").split("").reduce((a, c) => a + c.charCodeAt(0), 0);
  return s * 7 + 11;
}
function pdDemand30(p) {
  const seed = pdSeed(p);
  return Array.from({ length: 30 }, (_, i) => {
    const base = 2 + ((seed * 3 + i * 13) % 8);
    const wave = Math.round(Math.sin((i / 30) * Math.PI * 2 + seed) * 2);
    return Math.max(0, base + wave);
  });
}

// -----------------------------------------------------------------
// PRODUCT DETAIL — LIVE · this pharmacy (read + WRITE), Z-parity tabs
// Full product master from /products/:id/detail: Details (identity, class,
// stock rules, behaviour + clinical flags), Pricing (cost/avg/prev/markup/GP/
// sell/RRP), Barcodes, Stock-by-site, and History (movements). Editable fields
// PATCH to Aurora → sync to the POS.
// -----------------------------------------------------------------
const pdMoney = (c) => "$" + (Number(c || 0) / 100).toLocaleString("en-AU", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const pdPct = (bp) => (bp == null ? "—" : (bp / 100).toFixed(2) + "%");

function ProductDetail() {
  const { screenState, setScreen } = window.useOffice();
  const id = screenState.activeProductId;
  const backTo = screenState.productFrom || "stockOverview";

  const [detail, setDetail] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [err, setErr] = React.useState(null);
  const [edits, setEdits] = React.useState({});
  const [saving, setSaving] = React.useState(false);
  const [tab, setTab] = React.useState("details");

  React.useEffect(() => {
    if (!id) return undefined;
    let alive = true;
    setLoading(true); setErr(null); setEdits({}); setTab("details");
    window.OfficeAPI.getProductDetail(id)
      .then((d) => { if (alive) { setDetail(d); setLoading(false); } })
      .catch((e) => { if (alive) { setErr(String((e && e.message) || e)); setLoading(false); } });
    return () => { alive = false; };
  }, [id]);

  const back = <button className="btn btn--ghost btn--sm" style={{padding:0, height:'auto', fontSize:'var(--fs-12)', verticalAlign:'baseline'}} onClick={() => setScreen(backTo)}>‹ Back</button>;

  if (!id) return <div className="off-screen"><div className="off-screen-body"><div className="off-empty" style={{padding:40}}><div className="empty-title">No product selected.</div></div></div></div>;
  if (loading) return <div className="off-screen"><header className="off-screen-header"><div className="off-screen-header-meta"><span className="off-screen-eyebrow">{back}</span><h2 className="off-screen-title">Loading…</h2></div></header></div>;
  if (err || !detail) return <div className="off-screen"><div className="off-screen-body"><div className="off-card" style={{borderColor:'var(--void-border)',background:'var(--void-bg)',color:'var(--void-text)',padding:'12px 16px',flexDirection:'row',alignItems:'center',gap:10}}><window.OIcon name="alert"/> <span>Couldn't load product: {err || "not found"}</span></div></div></div>;

  const p = detail.product;
  const E = (k, base) => (edits[k] !== undefined ? edits[k] : base);
  const setE = (k, v) => setEdits((e) => ({ ...e, [k]: v }));
  const dirty = Object.keys(edits).length;

  const cost = Number(E("cost", p.costPriceCents == null ? 0 : p.costPriceCents / 100));
  const sell = Number(E("sell", (p.sellPriceCents || 0) / 100));
  const rrp = E("rrp", p.comparePriceCents == null ? "" : (p.comparePriceCents / 100).toFixed(2));
  const margin = sell > 0 ? Math.round(((sell - cost) / sell) * 100) : 0;
  const onHandTotal = detail.stock.reduce((s, x) => s + x.quantityOnHand, 0);

  async function save() {
    setSaving(true);
    const patch = {};
    const numEdit = (k, conv) => { if (edits[k] !== undefined) patch[k.replace(/^x_/, "")] = conv(edits[k]); };
    if (edits.cost !== undefined) patch.costPriceCents = Math.max(0, Math.round(Number(edits.cost) * 100));
    if (edits.sell !== undefined) patch.sellPriceCents = Math.max(0, Math.round(Number(edits.sell) * 100));
    if (edits.rrp !== undefined) patch.comparePriceCents = edits.rrp === "" ? null : Math.max(0, Math.round(Number(edits.rrp) * 100));
    if (edits.fullName !== undefined) patch.fullName = edits.fullName || null;
    if (edits.cartonSize !== undefined) patch.cartonSize = Math.max(0, parseInt(edits.cartonSize, 10) || 0);
    if (edits.minOrderQty !== undefined) patch.minOrderQty = Math.max(0, parseInt(edits.minOrderQty, 10) || 0);
    if (edits.reorderMin !== undefined) patch.reorderMin = Math.max(0, parseInt(edits.reorderMin, 10) || 0);
    if (edits.reorderMax !== undefined) patch.reorderMax = Math.max(0, parseInt(edits.reorderMax, 10) || 0);
    if (edits.tillMessage !== undefined) patch.tillMessage = edits.tillMessage || null;
    ["ethical","discountable","authRequired","autoOrders","orderOnlyBelowMin","descOnDisplay","storedInRobot","markedOnline","soldByWeight"].forEach((f) => { if (edits[f] !== undefined) patch[f] = !!edits[f]; });
    try { await window.OfficeAPI.updateProduct(id, patch); } catch (e) { /* surfaced by reload diff */ }
    setSaving(false);
    setEdits({});
    const d = await window.OfficeAPI.getProductDetail(id).catch(() => null);
    if (d) setDetail(d);
  }

  const reloadDetail = async () => {
    const d = await window.OfficeAPI.getProductDetail(id).catch(() => null);
    if (d) setDetail(d);
  };

  const TABS = [
    { k: "details", label: "Details" },
    { k: "pricing", label: "Pricing" },
    { k: "priceHistory", label: "Price history", n: (detail.priceHistory || []).length },
    { k: "pdes", label: "Suppliers (PDEs)", n: (detail.pdes || []).length },
    { k: "barcodes", label: "Barcodes", n: detail.barcodes.length },
    { k: "stock", label: "Stock", n: detail.stock.length },
    { k: "history", label: "History" },
  ];

  return (
    <div className="off-screen">
      <header className="off-screen-header">
        <div className="off-screen-header-meta">
          <span className="off-screen-eyebrow">{back} · live from Aurora · {detail.supplierName || "no supplier"}{p.schedule ? " · " + p.schedule : ""}</span>
          <h2 className="off-screen-title">{p.name && p.name !== "-" ? p.name : p.sku}</h2>
        </div>
        <div className="off-screen-actions">
          {dirty > 0 && <button className="btn btn--ghost btn--sm" onClick={() => setEdits({})}>Discard</button>}
          <button className="btn btn--primary btn--sm" disabled={dirty === 0 || saving} onClick={save}>
            {saving ? <span className="spinner"/> : <window.OIcon name="check"/>} {saving ? "Saving…" : (dirty ? `Save ${dirty} change${dirty > 1 ? "s" : ""}` : "Save")}
          </button>
        </div>
      </header>

      <div className="off-screen-body">
        <div className="off-kpi-grid">
          <KpiCard label="Sell price" value={pdMoney(sell * 100)} delta={{ text: p.isTaxInclusive ? "GST inclusive" : "ex GST" }}/>
          <KpiCard label="Cost (ex GST)" value={pdMoney(cost * 100)} delta={{ text: "avg " + (p.avgCostCents != null ? pdMoney(p.avgCostCents) : "—") }}/>
          <KpiCard label="Margin" value={margin + "%"} delta={{ dir: margin >= 30 ? "up" : "down", text: "markup " + pdPct(p.markupBp) }}/>
          <KpiCard label="On hand" value={Number(onHandTotal).toLocaleString("en-AU")} delta={{ text: detail.stock.length ? detail.stock.length + " site" + (detail.stock.length > 1 ? "s" : "") : "not stocked" }}/>
        </div>

        {/* Tab bar — DS tabset */}
        <div className="tabset" style={{marginTop:18, marginBottom:16}}>
          {TABS.map((t) => (
            <button key={t.k} className="tabset-item" aria-selected={tab === t.k} onClick={() => setTab(t.k)}>
              {t.label}{t.n != null ? <span style={{marginLeft:6, fontSize:'var(--fs-11)', color:'var(--text-subtle)'}}>{t.n}</span> : null}
            </button>
          ))}
        </div>

        {tab === "details" && (
          <div className="dash-grid">
            <div className="off-card">
              <div className="off-card-header"><h3 className="off-card-title">Basic details</h3></div>
              <PdEdit label="Full stock name" value={E("fullName", p.fullName || "")} dirty={edits.fullName !== undefined} onChange={(v) => setE("fullName", v)} wide/>
              <PdField label="SKU / GTIN" value={p.sku} mono/>
              <PdField label="Supplier" value={detail.supplierName}/>
              <PdField label="Manufacturer" value={detail.manufacturerName}/>
              <PdField label="Category" value={detail.categoryName}/>
              <PdSelect label="Schedule" value={E("schedule", p.schedule || "")} dirty={edits.schedule !== undefined} onChange={(v) => setE("schedule", v)} options={["", "S2", "S3", "S4", "S8"]}/>
              <div className="off-card-header" style={{marginTop:8}}><h3 className="off-card-title" style={{fontSize:'var(--fs-13)'}}>Stock rules</h3></div>
              <PdNum label="Carton size (units)" value={E("cartonSize", p.cartonSize ?? 1)} dirty={edits.cartonSize !== undefined} onChange={(v) => setE("cartonSize", v)}/>
              <PdNum label="Min order qty (cartons)" value={E("minOrderQty", p.minOrderQty ?? 0)} dirty={edits.minOrderQty !== undefined} onChange={(v) => setE("minOrderQty", v)}/>
              <PdNum label="Min stock level" value={E("reorderMin", p.reorderMin ?? 0)} dirty={edits.reorderMin !== undefined} onChange={(v) => setE("reorderMin", v)}/>
              <PdNum label="Max stock level" value={E("reorderMax", p.reorderMax ?? 0)} dirty={edits.reorderMax !== undefined} onChange={(v) => setE("reorderMax", v)}/>
            </div>
            <div className="off-card">
              <div className="off-card-header"><h3 className="off-card-title">Behaviour</h3><span style={{fontSize:'var(--fs-11)',color:'var(--text-subtle)'}}>edit + Save → Aurora → POS</span></div>
              <PdCheck label="Stock line active" v={E("isActive_ro", p.isActive)} readOnly/>
              <PdCheck label="Available for sale" v={E("isSellable_ro", p.isSellable)} readOnly/>
              <PdCheck label="Auto orders" v={!!E("autoOrders", p.autoOrders)} dirty={edits.autoOrders !== undefined} onToggle={(b) => setE("autoOrders", b)}/>
              <PdCheck label="Order only if SOH < min" v={!!E("orderOnlyBelowMin", p.orderOnlyBelowMin)} dirty={edits.orderOnlyBelowMin !== undefined} onToggle={(b) => setE("orderOnlyBelowMin", b)}/>
              <PdCheck label="Ethical line" v={!!E("ethical", p.ethical)} dirty={edits.ethical !== undefined} onToggle={(b) => setE("ethical", b)}/>
              <PdCheck label="Discountable" v={!!E("discountable", p.discountable)} dirty={edits.discountable !== undefined} onToggle={(b) => setE("discountable", b)}/>
              <PdCheck label="Auth. required to sell" v={!!E("authRequired", p.authRequired)} dirty={edits.authRequired !== undefined} onToggle={(b) => setE("authRequired", b)}/>
              <PdCheck label="Bulk food pricing (by weight)" v={!!E("soldByWeight", p.soldByWeight)} dirty={edits.soldByWeight !== undefined} onToggle={(b) => setE("soldByWeight", b)}/>
              <PdCheck label="Desc. on customer display" v={!!E("descOnDisplay", p.descOnDisplay)} dirty={edits.descOnDisplay !== undefined} onToggle={(b) => setE("descOnDisplay", b)}/>
              <PdCheck label="Stored in robot" v={!!E("storedInRobot", p.storedInRobot)} dirty={edits.storedInRobot !== undefined} onToggle={(b) => setE("storedInRobot", b)}/>
              <PdCheck label="Marked for online" v={!!E("markedOnline", p.markedOnline)} dirty={edits.markedOnline !== undefined} onToggle={(b) => setE("markedOnline", b)}/>
              <div className="off-card-header" style={{marginTop:8}}><h3 className="off-card-title" style={{fontSize:'var(--fs-13)'}}>Clinical</h3></div>
              <PdCheck label="S8 controlled drug" v={p.schedule === "S8"} readOnly/>
              <PdCheck label="NDSS subsidised" v={p.isNdssProduct} readOnly/>
            </div>
          </div>
        )}

        {tab === "pricing" && (
          <div className="dash-grid">
            <div className="off-card">
              <div className="off-card-header"><h3 className="off-card-title">Cost</h3></div>
              <PdEdit label="Cost (ex GST)" prefix="$" value={cost.toFixed(2)} dirty={edits.cost !== undefined} onChange={(v) => setE("cost", parseFloat(v.replace(/[^\d.]/g, "")) || 0)}/>
              <PdField label="Average cost" value={p.avgCostCents != null ? pdMoney(p.avgCostCents) : null}/>
              <PdField label="Previous cost" value={p.prevCostCents != null ? pdMoney(p.prevCostCents) : null}/>
              <PdField label="Last invoice cost" value={p.lastInvCostCents != null ? pdMoney(p.lastInvCostCents) : null}/>
            </div>
            <div className="off-card">
              <div className="off-card-header"><h3 className="off-card-title">Sell</h3></div>
              <PdEdit label="Retail sell price" prefix="$" value={sell.toFixed(2)} dirty={edits.sell !== undefined} onChange={(v) => setE("sell", parseFloat(v.replace(/[^\d.]/g, "")) || 0)}/>
              <PdEdit label="RRP" prefix="$" value={rrp} dirty={edits.rrp !== undefined} onChange={(v) => setE("rrp", v.replace(/[^\d.]/g, ""))}/>
              <PdField label="Markup %" value={pdPct(p.markupBp)}/>
              <PdField label="Gross profit %" value={margin + "%"}/>
              <PdField label="Expected discount %" value={pdPct(p.expectedDiscountBp)}/>
              <PdField label="GST" value={p.isTaxInclusive ? "Inclusive" : "Free / ex"}/>
            </div>
          </div>
        )}

        {tab === "priceHistory" && <PdPriceHistory points={detail.priceHistory || []} changes={detail.priceChanges || []} competitors={detail.competitorPrices || []} product={p} onCaptured={reloadDetail}/>}

        {tab === "pdes" && (
          <div className="off-card">
            <div className="off-card-header"><h3 className="off-card-title">Supplier PDEs</h3><span style={{fontSize:'var(--fs-11)',color:'var(--text-subtle)'}}>{(detail.pdes || []).length} · the same product across wholesalers</span></div>
            {(detail.pdes || []).length === 0 && <div style={{padding:'12px 4px',fontSize:'var(--fs-13)',color:'var(--text-subtle)'}}>No supplier PDEs on file.</div>}
            {(detail.pdes || []).length > 0 && (
              <div className="off-grid" style={{gridTemplateColumns:'110px minmax(150px,1fr) minmax(220px,1.4fr) 90px 60px'}}>
                <div className="off-grid-headcell">PDE #</div>
                <div className="off-grid-headcell">Supplier</div>
                <div className="off-grid-headcell">Supplier description</div>
                <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>Trade $</div>
                <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>Pref</div>
                {detail.pdes.map((pde, i) => (
                  <div key={i} className="off-grid-row">
                    <div className="off-grid-cell off-cell-mono">{pde.pde}</div>
                    <div className="off-grid-cell off-cell-strong">{pde.supplierName || "—"}</div>
                    <div className="off-grid-cell off-muted" style={{overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{pde.supplierDescription || "—"}</div>
                    <div className="off-grid-cell off-cell-num off-cell-strong">{pde.tradePriceExGstCents == null ? "—" : pdMoney(pde.tradePriceExGstCents)}</div>
                    <div className="off-grid-cell off-cell-num">{pde.isPreferred ? <span style={{color:'var(--brand)',fontWeight:700}}>★</span> : ""}</div>
                  </div>
                ))}
              </div>
            )}
          </div>
        )}

        {tab === "barcodes" && (
          <div className="off-card">
            <div className="off-card-header"><h3 className="off-card-title">Barcodes</h3><span style={{fontSize:'var(--fs-11)',color:'var(--text-subtle)'}}>{detail.barcodes.length}</span></div>
            {detail.barcodes.length === 0 && <div style={{padding:'12px 4px',fontSize:'var(--fs-13)',color:'var(--text-subtle)'}}>No barcodes on file.</div>}
            {detail.barcodes.map((b, i) => (
              <div key={i} className="alert-row" style={{cursor:'default'}}>
                <div style={{flex:1,fontFamily:'var(--font-mono)',fontSize:'var(--fs-13)'}}>{b.value}</div>
                <div style={{fontSize:'var(--fs-11)',color:'var(--text-subtle)'}}>{b.barcodeType || "—"}</div>
                {b.isPrimary && <span style={{fontSize:10,fontWeight:700,color:'var(--brand)',marginLeft:8}}>PRIMARY</span>}
              </div>
            ))}
          </div>
        )}

        {tab === "stock" && (
          <div className="off-card">
            <div className="off-card-header"><h3 className="off-card-title">Stock on hand by site</h3></div>
            {detail.stock.length === 0 && <div style={{padding:'12px 4px',fontSize:'var(--fs-13)',color:'var(--text-subtle)'}}>Not stocked at any site.</div>}
            {detail.stock.map((s) => (
              <div key={s.siteId} className="alert-row" style={{cursor:'default'}}>
                <div style={{flex:1,fontSize:'var(--fs-13)'}}>{s.siteId}</div>
                <div style={{fontSize:'var(--fs-14)',fontWeight:600,color: s.quantityOnHand < 0 ? 'var(--void)' : 'inherit'}}>{s.quantityOnHand}</div>
              </div>
            ))}
          </div>
        )}

        {tab === "history" && (<>
          <PdShortages shortages={detail.shortages}/>
          <div className="off-card">
            <div className="off-card-header"><h3 className="off-card-title">Stock movements</h3><span style={{fontSize:'var(--fs-11)',color:'var(--text-subtle)'}}>{(detail.movements || []).length} most recent · old → new SOH</span></div>
            {(detail.movements || []).length === 0 && <div style={{padding:'12px 4px',fontSize:'var(--fs-13)',color:'var(--text-subtle)'}}>No recorded movements.</div>}
            {(detail.movements || []).length > 0 && (
              <div className="off-grid" style={{gridTemplateColumns:'150px 130px 70px 110px minmax(150px,1fr)'}}>
                <div className="off-grid-headcell">Date</div>
                <div className="off-grid-headcell">Type</div>
                <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>Qty</div>
                <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>SOH</div>
                <div className="off-grid-headcell">By / note</div>
                {detail.movements.map((m, i) => {
                  const d = String(m.occurredAt || "").replace("T", " ").slice(0, 16);
                  const up = m.quantityDelta >= 0;
                  return (
                    <div key={i} className="off-grid-row">
                      <div className="off-grid-cell off-cell-mono" style={{fontSize:'var(--fs-12)'}}>{d}</div>
                      <div className="off-grid-cell">{String(m.movementType || "").replace(/_/g, " ")}</div>
                      <div className="off-grid-cell off-cell-num off-cell-strong" style={{color: up ? "var(--paid-text)" : "var(--void)"}}>{up ? "+" : ""}{m.quantityDelta}</div>
                      <div className="off-grid-cell off-cell-num off-cell-mono" style={{fontSize:'var(--fs-12)'}}>{m.quantityBefore}→{m.quantityAfter}</div>
                      <div className="off-grid-cell off-muted" style={{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}}>{m.notes || "—"}</div>
                    </div>
                  );
                })}
              </div>
            )}
          </div>
        </>)}
      </div>
    </div>
  );
}

function PdField({ label, value, mono }) {
  return (
    <div className="alert-row" style={{cursor:'default'}}>
      <div style={{flex:'0 0 170px',fontSize:'var(--fs-12)',color:'var(--text-subtle)'}}>{label}</div>
      <div style={{flex:1,fontSize:'var(--fs-13)', fontFamily: mono ? 'var(--font-mono)' : 'inherit', color: value ? 'var(--text)' : 'var(--text-subtle)'}}>{value || "—"}</div>
    </div>
  );
}
function PdEdit({ label, value, onChange, dirty, prefix, wide }) {
  return (
    <div className="alert-row" style={{cursor:'default'}}>
      <div style={{flex:'0 0 170px',fontSize:'var(--fs-12)',color:'var(--text-subtle)'}}>{label}</div>
      <div style={{flex:1, display:'flex', alignItems:'center', gap:4}}>
        {prefix && <span style={{color:'var(--text-subtle)',fontSize:'var(--fs-13)'}}>{prefix}</span>}
        <input className="input" type="text" value={value} onChange={(e) => onChange(e.target.value)}
          style={{width: wide ? 320 : 130, height:32, fontSize:'var(--fs-13)', padding:'0 10px',
                  borderColor: dirty ? 'var(--brand)' : undefined, background: dirty ? 'var(--brand-bg)' : undefined}}/>
      </div>
    </div>
  );
}
function PdNum({ label, value, onChange, dirty }) {
  return <PdEdit label={label} value={String(value)} dirty={dirty} onChange={(v) => onChange(parseInt(v.replace(/[^\d]/g, ""), 10) || 0)}/>;
}
function PdSelect({ label, value, onChange, dirty, options }) {
  return (
    <div className="alert-row" style={{cursor:'default'}}>
      <div style={{flex:'0 0 170px',fontSize:'var(--fs-12)',color:'var(--text-subtle)'}}>{label}</div>
      <div style={{flex:1}}>
        <select className="input" value={value} onChange={(e) => onChange(e.target.value)}
          style={{width:130, height:32, fontSize:'var(--fs-13)', padding:'0 8px',
                  borderColor: dirty ? 'var(--brand)' : undefined, background: dirty ? 'var(--brand-bg)' : undefined}}>
          {options.map((o) => <option key={o} value={o}>{o === "" ? "—" : o}</option>)}
        </select>
      </div>
    </div>
  );
}
function PdCheck({ label, v, onToggle, dirty, readOnly }) {
  return (
    <label className="checkbox" style={{padding:'8px 0', width:'100%', cursor: readOnly ? 'default' : 'pointer', opacity: readOnly ? 0.65 : 1}}
      onClick={readOnly ? undefined : (e) => { e.preventDefault(); onToggle(!v); }}>
      <input type="checkbox" checked={!!v} readOnly/>
      <span className="checkbox-box"><window.OIcon name="check" width={12} height={12}/></span>
      <span style={{fontSize:'var(--fs-13)'}}>{label}
        {readOnly && <span style={{fontSize:'var(--fs-11)',color:'var(--text-subtle)',marginLeft:6}}>read-only</span>}
        {dirty && <span style={{fontSize:'var(--fs-11)',color:'var(--brand)',marginLeft:6}}>edited</span>}
      </span>
    </label>
  );
}

function PdChip({ children, tone, compact }) {
  const cls = "pd-chip" + (tone ? ` pd-chip--${tone}` : "") + (compact ? " pd-chip--compact" : "");
  return <span className={cls}>{children}</span>;
}

function PdRow({ label, value, strong, mono, tone }) {
  return (
    <div className="pd-row">
      <span className="pd-row-label">{label}</span>
      <span className={"pd-row-value" + (strong ? " is-strong" : "") + (mono ? " is-mono" : "") + (tone === "warn" ? " is-warn" : "") + (tone === "good" ? " is-good" : "")}>{value}</span>
    </div>
  );
}

function PdStockBar({ onHand, min, max }) {
  const ceiling = Math.max(max, onHand, min * 2, 1);
  const pct = Math.min(100, (onHand / ceiling) * 100);
  const minPct = ceiling ? (min / ceiling) * 100 : 0;
  const low = min > 0 && onHand <= min;
  return (
    <div className="pd-bar">
      <div className="pd-bar-track">
        <div className={"pd-bar-fill" + (low ? " is-low" : "")} style={{width: pct + "%"}}/>
        {min > 0 && <div className="pd-bar-min" style={{left: minPct + "%"}} title={`Min ${min}`}/>}
      </div>
      <div className="pd-bar-legend">
        <span>{min || 0}</span>
        <span className="off-muted">min · max</span>
        <span>{max || "—"}</span>
      </div>
    </div>
  );
}

function PdSpark({ data }) {
  const max = Math.max(...data, 1);
  const W = 100, H = 60;
  const step = W / (data.length - 1);
  const points = data.map((v, i) => [i * step, H - (v / max) * H]);
  const path = points.map((pt, i) => (i === 0 ? "M" : "L") + pt[0].toFixed(1) + " " + pt[1].toFixed(1)).join(" ");
  const area = path + ` L ${W} ${H} L 0 ${H} Z`;
  return (
    <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" className="pd-spark">
      <path d={area} className="pd-spark-area"/>
      <path d={path} className="pd-spark-line"/>
      {data.map((v, i) => (
        <circle key={i} cx={i * step} cy={H - (v / max) * H} r={v === max ? 1.6 : 0} className="pd-spark-peak"/>
      ))}
    </svg>
  );
}

// A change-journal field label + colour.
const PD_FIELD = { cost: "Cost", sell: "Sell price", compare: "RRP" };

// Recent price/cost changes journalled in the Office (the forward moat signal).
function PdPriceChanges({ changes }) {
  if (!changes || changes.length === 0) return null;
  return (
    <div className="off-card" style={{marginBottom:16}}>
      <div className="off-card-header"><h3 className="off-card-title">Recent price changes</h3><span style={{fontSize:'var(--fs-11)',color:'var(--text-subtle)'}}>{changes.length} · journalled for analytics</span></div>
      <div className="off-grid" style={{gridTemplateColumns:'150px 100px 110px 110px 90px minmax(120px,1fr)'}}>
        <div className="off-grid-headcell">When</div>
        <div className="off-grid-headcell">Field</div>
        <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>From</div>
        <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>To</div>
        <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>Change</div>
        <div className="off-grid-headcell">Reason</div>
        {changes.map((c, i) => {
          const up = (c.deltaCents || 0) > 0;
          return (
            <div key={i} className="off-grid-row">
              <div className="off-grid-cell off-cell-mono" style={{fontSize:'var(--fs-12)'}}>{String(c.date || "").replace("T", " ").slice(0, 16)}</div>
              <div className="off-grid-cell off-cell-strong">{PD_FIELD[c.field] || c.field}</div>
              <div className="off-grid-cell off-cell-num off-muted">{c.oldCents == null ? "—" : pdMoney(c.oldCents)}</div>
              <div className="off-grid-cell off-cell-num off-cell-strong">{c.newCents == null ? "—" : pdMoney(c.newCents)}</div>
              <div className="off-grid-cell off-cell-num" style={{color: c.deltaCents == null ? "var(--text-subtle)" : up ? "var(--void)" : "var(--paid-text)"}}>{c.deltaCents == null ? "—" : (up ? "+" : "") + pdMoney(c.deltaCents)}</div>
              <div className="off-grid-cell off-muted" style={{overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"}}>{c.reason || c.source || "—"}</div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// Cost & sell price over time, from the legacy z_archive.stock_price_history.
// Latest competitor observation per competitor + our current sell, with the gap.
// A script line's competitor price is the patient co-pay (not a like-for-like
// retail price), so the gap is labelled a monitor rather than a recommendation.
function PdCompetitorPrices({ competitors, product, onCaptured }) {
  const [adding, setAdding] = React.useState(false);
  const rows = competitors || [];
  const ourSell = product ? product.sellPriceCents : null;
  const isRx = !!(product && (product.ethical || product.schedule || product.isScheduleProduct));
  return (
    <div className="off-card">
      <div className="off-card-header">
        <div>
          <h3 className="off-card-title">Competitor prices</h3>
          <div className="off-card-meta">{rows.length ? rows.length + " competitor" + (rows.length > 1 ? "s" : "") + " · latest observed" : "no observations yet"}{isRx ? " · script (co-pay basis — monitor)" : ""}</div>
        </div>
        {window.CompetitorCaptureForm && <button className="btn btn--ghost btn--sm" onClick={() => setAdding((v) => !v)}><window.OIcon name="plus"/> Add</button>}
      </div>
      {adding && window.CompetitorCaptureForm && (
        <div style={{marginBottom:12}}>
          <window.CompetitorCaptureForm fixedProduct={product} onSaved={() => { setAdding(false); onCaptured && onCaptured(); }} onCancel={() => setAdding(false)}/>
        </div>
      )}
      {rows.length === 0 && !adding && (
        <div style={{padding:'12px 4px', fontSize:'var(--fs-13)', color:'var(--text-subtle)'}}>No competitor price observed for this line yet.</div>
      )}
      {rows.length > 0 && (
        <div className="off-grid" style={{gridTemplateColumns:'1fr 110px 110px 100px'}}>
          <div className="off-grid-headcell">Competitor</div>
          <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>Their price</div>
          <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>Our sell</div>
          <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>Gap</div>
          {rows.map((c, i) => {
            const theirs = c.priceCents;
            const gapPct = (theirs != null && theirs > 0 && ourSell != null) ? ((ourSell - theirs) / theirs) * 100 : null;
            return (
              <div key={i} className="off-grid-row">
                <div className="off-grid-cell off-cell-strong" style={{display:'flex',alignItems:'center',gap:6,minWidth:0}}>
                  {c.url ? <a href={c.url} target="_blank" rel="noopener noreferrer" style={{color:'var(--brand)',textDecoration:'none',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{c.competitor}</a> : <span>{c.competitor}</span>}
                  <span style={{fontSize:9,color:'var(--text-subtle)',fontFamily:'var(--font-mono)'}}>{String(c.observedAt || '').slice(0,10)}</span>
                </div>
                <div className="off-grid-cell off-cell-num">{theirs == null ? '—' : pdMoney(theirs)}</div>
                <div className="off-grid-cell off-cell-num off-muted">{ourSell == null ? '—' : pdMoney(ourSell)}</div>
                <div className="off-grid-cell off-cell-num" style={{fontWeight:600, color: gapPct == null ? 'var(--text-subtle)' : isRx ? 'var(--text-subtle)' : gapPct > 0.05 ? 'var(--void-text)' : gapPct < -0.05 ? 'var(--paid)' : 'var(--text-subtle)'}}>
                  {theirs === 0 ? 'n/a' : gapPct == null ? '—' : (gapPct > 0 ? '+' : '') + gapPct.toFixed(1) + '%'}
                </div>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// Wholesaler shortage history for this product — only shown when present (the OOS
// events resolve a product on ~5% of lines, so most products have none).
function PdShortages({ shortages }) {
  const rows = shortages || [];
  if (rows.length === 0) return null;
  return (
    <div className="off-card">
      <div className="off-card-header">
        <div>
          <h3 className="off-card-title">Wholesaler shortages</h3>
          <div className="off-card-meta">{rows.length} time{rows.length > 1 ? "s" : ""} a wholesaler couldn't fully supply this line</div>
        </div>
      </div>
      <div className="off-grid" style={{gridTemplateColumns:'110px 1fr 70px 110px'}}>
        <div className="off-grid-headcell">Date</div>
        <div className="off-grid-headcell">Supplier</div>
        <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>Short</div>
        <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>Status</div>
        {rows.map((e, i) => (
          <div key={i} className="off-grid-row">
            <div className="off-grid-cell off-cell-mono" style={{fontSize:'var(--fs-12)'}}>{String(e.orderedAt || "").slice(0, 10)}</div>
            <div className="off-grid-cell" style={{overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{e.supplierName || "—"}</div>
            <div className="off-grid-cell off-cell-num">{Number((e.qtyBackordered ?? 0) > 0 ? e.qtyBackordered : (e.qtyOutOfStock ?? 0)).toLocaleString('en-AU')}</div>
            <div className="off-grid-cell" style={{justifyContent:'flex-end'}}>
              <span style={{fontSize:9,fontWeight:700,padding:'2px 7px',borderRadius:10,background: e.status === 'backordered' ? 'var(--hold-bg)' : e.status === 'out_of_stock' ? 'var(--void-bg)' : 'var(--bg-subtle)',color: e.status === 'backordered' ? 'var(--hold-text)' : e.status === 'out_of_stock' ? 'var(--void-text)' : 'var(--text-subtle)'}}>{e.status === 'backordered' ? 'Backordered' : e.status === 'out_of_stock' ? 'Out of stock' : (e.status || 'Other')}</span>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function PdPriceHistory({ points, changes, competitors, product, onCaptured }) {
  if (!points || points.length === 0) {
    return (
      <>
        <PdCompetitorPrices competitors={competitors} product={product} onCaptured={onCaptured}/>
        <PdPriceChanges changes={changes}/>
        <div className="off-card">
          <div className="off-card-header"><h3 className="off-card-title">Price history</h3></div>
          <div style={{padding:'12px 4px', fontSize:'var(--fs-13)', color:'var(--text-subtle)'}}>No long-run cost/price history on file for this line.</div>
        </div>
      </>
    );
  }
  const chron = points.slice().reverse(); // oldest → newest for the chart
  const all = chron.flatMap((p) => [p.sellPriceCents, p.costExCents]).filter((v) => v != null);
  const mn = Math.min(...all), mx = Math.max(...all), span = Math.max(1, mx - mn);
  const W = 720, H = 190, padL = 6, padR = 6, padT = 10, padB = 16;
  const iw = W - padL - padR, ih = H - padT - padB;
  const x = (i) => padL + (chron.length === 1 ? iw / 2 : (i / (chron.length - 1)) * iw);
  const y = (c) => padT + ih - ((c - mn) / span) * ih;
  const path = (key) => chron.map((p, i) => (p[key] == null ? null : [x(i), y(p[key])])).filter(Boolean)
    .map((pt, i) => (i === 0 ? "M" : "L") + pt[0].toFixed(1) + " " + pt[1].toFixed(1)).join(" ");
  const ym = (s) => String(s || "").slice(0, 7);
  return (
    <>
    <PdCompetitorPrices competitors={competitors} product={product} onCaptured={onCaptured}/>
    <PdPriceChanges changes={changes}/>
    <div className="off-card">
      <div className="off-card-header">
        <div>
          <h3 className="off-card-title">Cost &amp; price over time</h3>
          <div className="off-card-meta">{points.length} change points · {ym(chron[0].date)} → {ym(chron[chron.length - 1].date)}</div>
        </div>
        <div style={{display:'flex', gap:14, fontSize:'var(--fs-12)', color:'var(--text-subtle)'}}>
          <span style={{display:'inline-flex', alignItems:'center', gap:6}}><span style={{width:14, height:3, background:'var(--brand)', borderRadius:2}}/>Sell</span>
          <span style={{display:'inline-flex', alignItems:'center', gap:6}}><span style={{width:14, height:3, background:'var(--hold)', borderRadius:2}}/>Cost ex-GST</span>
        </div>
      </div>
      <div style={{position:'relative', marginTop:4}}>
        <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" style={{width:'100%', height:190}}>
          <line x1={padL} y1={padT + ih} x2={padL + iw} y2={padT + ih} stroke="var(--line)" strokeWidth="1" vectorEffect="non-scaling-stroke"/>
          <path d={path("costExCents")} fill="none" stroke="var(--hold)" strokeWidth="1.5" vectorEffect="non-scaling-stroke"/>
          <path d={path("sellPriceCents")} fill="none" stroke="var(--brand)" strokeWidth="1.5" vectorEffect="non-scaling-stroke"/>
        </svg>
        <div style={{position:'absolute', top:0, right:6, fontSize:'var(--fs-11)', color:'var(--text-subtle)', fontFamily:'var(--font-mono)'}}>{pdMoney(mx)}</div>
        <div style={{position:'absolute', bottom:16, right:6, fontSize:'var(--fs-11)', color:'var(--text-subtle)', fontFamily:'var(--font-mono)'}}>{pdMoney(mn)}</div>
      </div>
      <div className="off-grid" style={{gridTemplateColumns:'120px 110px 110px 110px 80px', marginTop:14}}>
        <div className="off-grid-headcell">Date</div>
        <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>Cost ex</div>
        <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>Avg cost</div>
        <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>Sell</div>
        <div className="off-grid-headcell" style={{justifyContent:'flex-end'}}>SOH</div>
        {points.slice(0, 120).map((p, i) => (
          <div key={i} className="off-grid-row">
            <div className="off-grid-cell off-cell-mono" style={{fontSize:'var(--fs-12)'}}>{String(p.date || "").slice(0, 10)}</div>
            <div className="off-grid-cell off-cell-num off-cell-strong">{p.costExCents == null ? "—" : pdMoney(p.costExCents)}</div>
            <div className="off-grid-cell off-cell-num off-muted">{p.avgCostExCents == null ? "—" : pdMoney(p.avgCostExCents)}</div>
            <div className="off-grid-cell off-cell-num">{p.sellPriceCents == null ? "—" : pdMoney(p.sellPriceCents)}</div>
            <div className="off-grid-cell off-cell-num off-muted">{p.shopSoh == null ? "—" : p.shopSoh}</div>
          </div>
        ))}
      </div>
      {points.length > 120 && <div style={{padding:'10px 2px', fontSize:'var(--fs-12)', color:'var(--text-subtle)'}}>Showing the 120 most recent of {points.length} change points.</div>}
    </div>
    </>
  );
}

function PdAdjustModal({ product, stores, onClose, onSave }) {
  const [storeId, setStoreId] = pdUseState(stores[0]?.store.id || "");
  const [qty, setQty] = pdUseState(0);
  const [reason, setReason] = pdUseState("count");
  const [note, setNote] = pdUseState("");
  const REASONS = [
    { id: "count",    label: "Cycle count correction" },
    { id: "damaged",  label: "Damaged / wastage" },
    { id: "expired",  label: "Expired" },
    { id: "return",   label: "Patient return" },
    { id: "found",    label: "Found / mis-shelved" },
  ];
  return (
    <div className="pd-modal-back" onClick={onClose}>
      <div className="pd-modal" onClick={e => e.stopPropagation()}>
        <header className="pd-modal-head">
          <div>
            <div style={{fontSize:'var(--fs-11)', textTransform:'uppercase', letterSpacing:'0.08em', color:'var(--text-subtle)', fontWeight:600}}>Adjust stock</div>
            <div style={{fontSize:'var(--fs-16)', fontWeight:600, marginTop:2, letterSpacing:'-0.012em'}}>{product.name} <span style={{color:'var(--text-subtle)', fontWeight:400}}>· {product.strength}</span></div>
          </div>
          <button className="off-topbar-btn" onClick={onClose}><OIcon name="x"/></button>
        </header>
        <div className="pd-modal-body">
          <div className="field">
            <label className="field-label">Store</label>
            <select className="input select" value={storeId} onChange={e => setStoreId(e.target.value)}>
              {stores.map(r => <option key={r.store.id} value={r.store.id}>{r.store.name} · on hand {r.stock?.onHand || 0}</option>)}
            </select>
          </div>
          <div className="field">
            <label className="field-label">Reason</label>
            <select className="input select" value={reason} onChange={e => setReason(e.target.value)}>
              {REASONS.map(r => <option key={r.id} value={r.id}>{r.label}</option>)}
            </select>
          </div>
          <div className="field">
            <label className="field-label">Change (units)</label>
            <div className="pd-qty">
              <button className="pd-qty-btn" onClick={() => setQty(q => q - 1)}><OIcon name="minus"/></button>
              <input className="input tabular" type="number" value={qty} onChange={e => setQty(parseInt(e.target.value) || 0)} style={{textAlign:'center', fontSize:'var(--fs-18)', fontWeight:600}}/>
              <button className="pd-qty-btn" onClick={() => setQty(q => q + 1)}><OIcon name="plus"/></button>
            </div>
            <div style={{fontSize:'var(--fs-11)', color:'var(--text-subtle)', marginTop:6}}>
              Use negative numbers to remove stock. {reason === "damaged" || reason === "expired" ? "Will write off at cost." : ""}
            </div>
          </div>
          <div className="field">
            <label className="field-label">Note (optional)</label>
            <input className="input" value={note} onChange={e => setNote(e.target.value)} placeholder="Visible in audit log"/>
          </div>
        </div>
        <footer className="pd-modal-foot">
          <button className="btn btn--ghost btn--sm" onClick={onClose}>Cancel</button>
          <button className="btn btn--primary btn--sm" onClick={() => onSave(qty)} disabled={qty === 0}>
            <OIcon name="check"/> Post adjustment
          </button>
        </footer>
      </div>
    </div>
  );
}

// -----------------------------------------------------------------
// Pure helpers
// -----------------------------------------------------------------
function parseExpiry(str) {
  if (!str || str === "—") return null;
  const [m, y] = str.split("/").map(Number);
  if (!m || !y) return null;
  return new Date(y, m - 1, 28);
}
function mvIcon(kind) {
  return { sale:"cart", receive:"truck", transfer:"transfer", adjust:"edit", stocktake:"clipboard" }[kind] || "info";
}
function mvLabel(kind) {
  return { sale:"Sale", receive:"Receipt", transfer:"Transfer", adjust:"Adjustment", stocktake:"Stocktake" }[kind] || kind;
}

// -----------------------------------------------------------------
// Register
// -----------------------------------------------------------------
window.OFFICE_SCREENS = Object.assign(window.OFFICE_SCREENS || {}, {
  productDetail: ProductDetail,
});
