/* =============================================================
   CoreIQ Office — Reports, LIVE.
   Profit analysis and the open-items re-order list over the WHOLE
   sales history (live tills + migrated years, one seamless union),
   with the rule that every row is one click from its action:
   a product row resolves its preferred supplier from the store's
   ranked line-up and "Add to order" builds a draft PO per supplier;
   the Drafts tray sends through the existing PharmX transmit path.

   Uses shared bare globals: OIcon, Screen, StatusPill, window.KpiCard,
   window.useOffice, window.OfficeAPI.
   ============================================================= */
const { useState: rptUseState, useEffect: rptUseEffect, useCallback: rptUseCallback, useMemo: rptUseMemo } = React;

const rptMoney = (c) => "$" + (Number(c || 0) / 100).toLocaleString("en-AU", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const rptNum = (v) => Number(v || 0).toLocaleString("en-AU", { maximumFractionDigits: 1 });
const rptDate = (s) => (s ? String(s).slice(0, 10) : "—");
/* Expired reads differently from expiring soon: one comes off the shelf today. */
function rptExpiryTone(iso) {
  const days = Math.round((Date.parse(iso + "T00:00:00Z") - Date.now()) / 86400000);
  if (days < 0) return { color: "var(--void-text)", fontWeight: 600 };
  if (days <= 60) return { color: "var(--hold-text)", fontWeight: 600 };
  return undefined;
}
const rptIso = (d) => d.toISOString().slice(0, 10);

/* Period presets — from/to are inclusive ISO dates. */
function rptPresets() {
  const now = new Date();
  const today = rptIso(now);
  const monthStart = today.slice(0, 8) + "01";
  const d30 = new Date(now); d30.setDate(d30.getDate() - 29);
  const q = Math.floor(now.getMonth() / 3) * 3;
  const qStart = rptIso(new Date(Date.UTC(now.getFullYear(), q, 1)));
  const yStart = now.getFullYear() + "-01-01";
  return [
    { k: "month", l: "This month", from: monthStart, to: today },
    { k: "30d", l: "Last 30 days", from: rptIso(d30), to: today },
    { k: "quarter", l: "This quarter", from: qStart, to: today },
    { k: "year", l: "This year", from: yStart, to: today },
    { k: "all", l: "All history", from: "2000-01-01", to: today },
  ];
}

/* ---- CSV download (client-side, from the rows on screen) ---------- */
function rptCsv(filename, header, rows) {
  const esc = (v) => {
    const s = String(v == null ? "" : v);
    return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
  };
  const text = [header, ...rows].map((r) => r.map(esc).join(",")).join("\n");
  const url = URL.createObjectURL(new Blob([text], { type: "text/csv" }));
  const a = document.createElement("a");
  a.href = url; a.download = filename; a.click();
  setTimeout(() => URL.revokeObjectURL(url), 5000);
}

/* ---- The per-row supplier + Add-to-order cell --------------------- */
function RptOrderCell({ row, pref, onAdded, pushToast }) {
  const [qty, setQty] = rptUseState(String(Math.max(1, Math.ceil(row.quantity || 1))));
  const [supplierId, setSupplierId] = rptUseState(null); // null = preferred
  const [busy, setBusy] = rptUseState(false);
  if (!row.productId) {
    return <span style={{ fontSize: "var(--fs-12)", color: "var(--text-subtle)" }}>not ranged — no product to order</span>;
  }
  if (pref === undefined) return <span style={{ fontSize: "var(--fs-12)", color: "var(--text-subtle)" }}>…</span>;
  if (!pref) {
    return <span style={{ fontSize: "var(--fs-12)", color: "var(--text-subtle)" }} title="No reorder code on file with any supplier — open the product's Market tab to find one.">no supplier on file</span>;
  }
  const usingCheaper = supplierId !== null;
  const chosenName = usingCheaper ? pref.cheaperAt.supplierName : pref.supplierName;
  const chosenPrice = usingCheaper ? pref.cheaperAt.unitCostCents : pref.unitCostCents;

  function add() {
    const n = Number(qty);
    if (!(n > 0)) { pushToast({ kind: "hold", icon: "alert", title: "Quantity needed", meta: "Enter how many to order" }); return; }
    if (busy) return;
    setBusy(true);
    window.OfficeAPI.addOrderDraftLine({ productId: row.productId, quantity: n, supplierId: supplierId || undefined })
      .then((r) => {
        setBusy(false);
        pushToast({ kind: "paid", icon: "check", title: "Added to " + r.supplierName + " draft", meta: row.label + " × " + n });
        onAdded();
      })
      .catch((e) => { setBusy(false); pushToast({ kind: "hold", icon: "alert", title: "Couldn't add", meta: String((e && e.message) || e) }); });
  }

  return (
    <span style={{ display: "flex", gap: 6, alignItems: "center", justifyContent: "flex-end", flexWrap: "wrap" }}>
      <span style={{ fontSize: "var(--fs-12)", color: "var(--text-subtle)", whiteSpace: "nowrap" }} title={"Preferred by the store's supplier line-up" + (chosenPrice != null ? " · " + rptMoney(chosenPrice) + "/u ex GST" : "")}>
        {chosenName}{chosenPrice != null ? " · " + rptMoney(chosenPrice) : ""}
      </span>
      {pref.cheaperAt && !usingCheaper && (
        <button
          className="btn btn--ghost btn--sm"
          style={{ fontSize: "var(--fs-11)" }}
          title={"Cheaper than " + pref.supplierName + " — click to order from " + pref.cheaperAt.supplierName + " instead"}
          onClick={() => setSupplierId(pref.cheaperAt.supplierId)}
        >
          {pref.cheaperAt.supplierName} {rptMoney(pref.cheaperAt.unitCostCents)}
        </button>
      )}
      {usingCheaper && (
        <button className="btn btn--ghost btn--sm" style={{ fontSize: "var(--fs-11)" }} onClick={() => setSupplierId(null)}>
          back to {pref.supplierName}
        </button>
      )}
      <input
        className="input" style={{ width: 56, height: 30, textAlign: "right" }}
        value={qty} inputMode="numeric"
        onChange={(e) => setQty(e.target.value.replace(/[^0-9]/g, ""))}
        onKeyDown={(e) => { if (e.key === "Enter") add(); }}
      />
      <button className="btn btn--primary btn--sm" disabled={busy} onClick={add}>
        {busy ? "Adding…" : "Add to order"}
      </button>
    </span>
  );
}

/* ---- Drafts tray — one card per supplier draft PO ----------------- */
function RptDraftsTray({ drafts, onChanged, onClose, pushToast }) {
  const [busyId, setBusyId] = rptUseState(null);

  function send(d) {
    if (busyId) return;
    setBusyId(d.id);
    window.OfficeAPI.sendOrderDraft(d.id)
      .then((r) => {
        setBusyId(null);
        pushToast({ kind: "paid", icon: "check", title: "Order transmitted", meta: r.orderNumber + " → " + d.supplierName });
        onChanged();
      })
      .catch((e) => { setBusyId(null); pushToast({ kind: "hold", icon: "alert", title: "Couldn't send", meta: String((e && e.message) || e) }); });
  }
  function discard(d) {
    if (busyId) return;
    setBusyId(d.id);
    window.OfficeAPI.discardOrderDraft(d.id)
      .then(() => { setBusyId(null); onChanged(); })
      .catch((e) => { setBusyId(null); pushToast({ kind: "hold", icon: "alert", title: "Couldn't discard", meta: String((e && e.message) || e) }); });
  }
  function setQty(line, q) {
    window.OfficeAPI.setOrderDraftLineQty(line.id, q)
      .then(onChanged)
      .catch((e) => pushToast({ kind: "hold", icon: "alert", title: "Couldn't update", meta: String((e && e.message) || e) }));
  }

  return (
    <div style={{ position: "fixed", top: 0, right: 0, bottom: 0, width: 420, maxWidth: "94vw", background: "var(--surface, #fff)", borderLeft: "1px solid var(--line, #e3e6eb)", boxShadow: "-12px 0 32px rgba(15,23,42,.12)", zIndex: 60, display: "flex", flexDirection: "column" }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "14px 16px", borderBottom: "1px solid var(--line, #e3e6eb)" }}>
        <span style={{ fontWeight: 600 }}>Order drafts</span>
        <button className="btn btn--ghost btn--sm" onClick={onClose}>Close</button>
      </div>
      <div style={{ flex: 1, overflowY: "auto", padding: 12, display: "flex", flexDirection: "column", gap: 12 }}>
        {drafts.length === 0 && (
          <div style={{ fontSize: "var(--fs-13)", color: "var(--text-subtle)", padding: 8 }}>
            Nothing drafted yet — use "Add to order" on a report row.
          </div>
        )}
        {drafts.map((d) => (
          <div key={d.id} className="sup-sec" style={{ padding: 12 }}>
            <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 6 }}>
              <span style={{ fontWeight: 600 }}>{d.supplierName}</span>
              <span style={{ fontSize: "var(--fs-12)", color: "var(--text-subtle)" }}>{d.lineCount} lines · {rptMoney(d.totalCents)}</span>
            </div>
            {d.lines.map((l) => (
              <div key={l.id} style={{ display: "flex", gap: 8, alignItems: "center", padding: "4px 0", fontSize: "var(--fs-12)" }}>
                <span style={{ flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={l.name}>{l.name}</span>
                <input
                  className="input" style={{ width: 52, height: 28, textAlign: "right" }}
                  defaultValue={l.quantity} inputMode="numeric"
                  onBlur={(e) => { const q = Number(e.target.value); if (Number.isFinite(q) && q !== l.quantity && q >= 0) setQty(l, q); }}
                />
                <span style={{ width: 70, textAlign: "right" }} className="sup-grid-mono">{rptMoney(l.lineTotalCents)}</span>
                <button className="btn btn--ghost btn--sm" title="Remove line" onClick={() => setQty(l, 0)}>×</button>
              </div>
            ))}
            <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 8 }}>
              <button className="btn btn--ghost btn--sm" disabled={busyId === d.id} onClick={() => discard(d)}>Discard</button>
              {d.pharmxReady ? (
                <button className="btn btn--primary btn--sm" disabled={busyId === d.id} onClick={() => send(d)}>
                  {busyId === d.id ? "Sending…" : "Send order"}
                </button>
              ) : (
                <span style={{ fontSize: "var(--fs-11)", color: "var(--text-subtle)", alignSelf: "center" }}
                      title="This supplier has no electronic ordering channel connected — the draft stays here; order by phone or email and discard it when done.">
                  no electronic channel
                </span>
              )}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

/* ---- The Reports screen ------------------------------------------ */
function ReportsLive() {
  const { pushToast } = window.useOffice();
  const presets = rptUseMemo(rptPresets, []);
  const [tab, setTab] = rptUseState("profit");
  const [preset, setPreset] = rptUseState("month");
  const [from, setFrom] = rptUseState(presets[0].from);
  const [to, setTo] = rptUseState(presets[0].to);
  const [groupBy, setGroupBy] = rptUseState("product");
  const [stockFilter, setStockFilter] = rptUseState("all"); // all | out (SOH <= 0)
  const [data, setData] = rptUseState(null); // { rows, totals }
  const [openItems, setOpenItems] = rptUseState(null);
  const [prefs, setPrefs] = rptUseState({});
  const [drafts, setDrafts] = rptUseState([]);
  const [trayOpen, setTrayOpen] = rptUseState(false);
  const [err, setErr] = rptUseState(null);
  const [loading, setLoading] = rptUseState(false);
  // Range report — one brand / one supplier's whole range (self-serve version
  // of the hand-built MooGoo / Bare Medical / Herbs of Gold reports).
  const [brands, setBrands] = rptUseState([]);
  const [rangeSuppliers, setRangeSuppliers] = rptUseState([]);
  const [rangeBrand, setRangeBrand] = rptUseState("");
  const [rangeSupplier, setRangeSupplier] = rptUseState("");
  const [rangeCategory, setRangeCategory] = rptUseState("");
  const [rangeCategories, setRangeCategories] = rptUseState([]);
  const rptRangeLabel = () =>
    rangeBrand
    || (rangeSuppliers.find((x) => x.id === rangeSupplier) || {}).name
    || (rangeCategory === "(uncategorised)"
          ? "Uncategorised"
          : (rangeCategories.find((c) => c.id === rangeCategory) || {}).name)
    || "";
  const [range, setRange] = rptUseState(null);
  const [rangeLoading, setRangeLoading] = rptUseState(false);

  const loadDrafts = rptUseCallback(() => {
    window.OfficeAPI.listOrderDrafts().then(setDrafts).catch(() => {});
  }, []);

  const load = rptUseCallback(() => {
    setLoading(true); setErr(null);
    const q = { from, to };
    const profit = window.OfficeAPI.reportProfit(Object.assign({ groupBy }, q))
      .then((d) => {
        setData(d);
        const ids = d.rows.filter((r) => r.productId).slice(0, 400).map((r) => r.productId);
        return ids.length
          ? window.OfficeAPI.preferredSuppliers(ids).then(setPrefs)
          : setPrefs({});
      });
    const open = window.OfficeAPI.reportOpenItems(q).then(setOpenItems);
    Promise.all([profit, open])
      .then(() => setLoading(false))
      .catch((e) => { setLoading(false); setErr(String((e && e.message) || e)); });
  }, [from, to, groupBy]);

  rptUseEffect(() => { load(); }, [load]);
  rptUseEffect(() => { loadDrafts(); }, [loadDrafts]);
  rptUseEffect(() => {
    window.OfficeAPI.reportBrands().then(setBrands).catch(() => {});
    window.OfficeAPI.listSuppliers().then((ss) => setRangeSuppliers(ss || [])).catch(() => {});
    window.OfficeAPI.listCategories().then((cs) => setRangeCategories(cs || [])).catch(() => {});
  }, []);
  rptUseEffect(() => {
    const scope = rangeBrand ? { brand: rangeBrand }
      : rangeSupplier ? { supplierId: rangeSupplier }
      : rangeCategory ? { categoryId: rangeCategory } : null;
    if (!scope) { setRange(null); return; }
    // The sales columns follow the period chosen at the top of the screen;
    // stock and expiry are always "now" and the header says so.
    const q = Object.assign({ from, to }, scope);
    setRangeLoading(true);
    window.OfficeAPI.reportRange(q)
      .then((rep) => { setRange(rep); setRangeLoading(false); })
      .catch((e) => { setRangeLoading(false); setErr(String((e && e.message) || e)); });
  }, [rangeBrand, rangeSupplier, rangeCategory, from, to]);

  function pickPreset(p) {
    setPreset(p.k); setFrom(p.from); setTo(p.to);
  }

  function exportCsv() {
    if (tab === "range" && range) {
      const label = rptRangeLabel() || "range";
      const money = (c) => (c == null ? "" : (c / 100).toFixed(2));
      const rows = range.rows.map((r) => [
        r.name, r.barcode || "MISSING", r.sohUnits, r.unitsSold, r.nextExpiry || "",
        money(r.sellPriceCents), money(r.costCents),
        r.gpPercent == null ? "" : Math.round(r.gpPercent) + "%",
      ]);
      rows.push([""], ["Totals", "", range.totals.sohUnits, range.totals.unitsSold, "", "", "", ""]);
      rows.push(["Value at cost (ex GST)", "", "", "", "", "", money(range.totals.valueAtCostCents), ""]);
      rows.push(["Value at retail (inc GST)", "", "", "", "", money(range.totals.valueAtRetailCents), "", ""]);
      rptCsv(
        label + " Inventory.csv",
        ["Product", "Barcode", "Stock On Hand", "Units Sold", "Next Expiry", "Sell Price (inc GST)", "Invoice Cost (ex GST)", "GP %"],
        rows,
      );
      return;
    }
    if (tab === "profit" && data) {
      const periodExport = groupBy === "day" || groupBy === "week" || groupBy === "month";
      rptCsv(
        (periodExport ? "trading-by-" + groupBy + " " : "profit-analysis ") + from + " to " + to + ".csv",
        periodExport
          ? [groupBy === "day" ? "Day" : groupBy === "week" ? "Week" : "Month",
             "Qty", "Gross sales", "Net sales", "Cost", "Gross profit", "GP %", "Sales", "Avg basket"]
          : ["Group", "Qty", "Gross sales", "Net sales", "Cost", "Gross profit", "GP %", "SOH"],
        data.rows.map((r) => {
          const common = [
            r.label, r.quantity, (r.grossCents / 100).toFixed(2), (r.netCents / 100).toFixed(2),
            r.costCents == null ? "" : (r.costCents / 100).toFixed(2),
            r.gpCents == null ? "" : (r.gpCents / 100).toFixed(2),
            r.gpPercent == null ? "" : r.gpPercent,
          ];
          return periodExport
            ? common.concat([r.salesCount, r.averageBasketCents == null ? "" : (r.averageBasketCents / 100).toFixed(2)])
            : common.concat([r.sohUnits == null ? "" : r.sohUnits]);
        }),
      );
    } else if (openItems) {
      rptCsv(
        "open-items " + from + " to " + to + ".csv",
        ["Note", "Times sold", "Units", "Value", "First sold", "Last sold"],
        openItems.map((r) => [r.note, r.timesSold, r.units, (r.valueCents / 100).toFixed(2), rptDate(r.firstAt), rptDate(r.lastAt)]),
      );
    }
  }

  const t = (data && data.totals) || {};
  const openCount = openItems ? openItems.length : 0;
  // A trading period has no shelf and nothing to reorder, so those two columns
  // carry sales count and average basket instead of showing an empty SOH and a
  // reorder control that would make no sense against a Tuesday.
  const rptIsPeriod = groupBy === "day" || groupBy === "week" || groupBy === "month";
  const rptGroupNoun = rptIsPeriod
    ? (groupBy === "day" ? "day" : groupBy === "week" ? "week" : "month")
    : groupBy === "product" ? "product" : groupBy === "department" ? "department" : "clerk";
  const profitCols = rptIsPeriod
    ? "minmax(200px, 2fr) 60px 100px 100px 100px 100px 60px 70px 110px"
    : "minmax(200px, 2fr) 60px 100px 100px 100px 100px 60px 70px minmax(280px, 2.2fr)";
  const openCols = "minmax(240px, 2.4fr) 90px 70px 100px 110px 110px";
  const rangeCols = "minmax(240px, 2.6fr) 150px 60px 60px 110px 110px 70px 110px";

  return (
    <Screen
      eyebrow="Analysis · whole sales history · live from Aurora"
      title="Reports"
      subtabs={[
        { k: "profit", l: "Profit analysis" },
        { k: "openItems", l: "Open items", badge: openCount || null },
        { k: "range", l: "Range report" },
      ]}
      activeTab={tab}
      onTab={setTab}
      actions={
        <span style={{ display: "flex", gap: 8 }}>
          <button className="btn btn--ghost btn--sm" onClick={exportCsv}>Export CSV</button>
          <button className="btn btn--ghost btn--sm" onClick={() => { loadDrafts(); setTrayOpen(true); }}>
            Drafts{drafts.length ? " (" + drafts.length + ")" : ""}
          </button>
          <button className="btn btn--ghost btn--sm" onClick={load}>Refresh</button>
        </span>
      }
    >
      {/* Period + grouping bar */}
      {tab !== "range" && (
      <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap", marginBottom: 14 }}>
        {presets.map((p) => (
          <button key={p.k} className={"btn btn--sm " + (preset === p.k ? "btn--primary" : "btn--ghost")} onClick={() => pickPreset(p)}>
            {p.l}
          </button>
        ))}
        <span style={{ display: "inline-flex", gap: 6, alignItems: "center", marginLeft: 8, fontSize: "var(--fs-12)", color: "var(--text-subtle)" }}>
          <input type="date" className="input" style={{ height: 30 }} value={from} onChange={(e) => { setPreset("custom"); setFrom(e.target.value); }}/>
          –
          <input type="date" className="input" style={{ height: 30 }} value={to} onChange={(e) => { setPreset("custom"); setTo(e.target.value); }}/>
        </span>
        {tab === "profit" && groupBy === "product" && (
          <span style={{ display: "inline-flex", gap: 6, marginLeft: "auto" }}>
            <button className={"btn btn--sm " + (stockFilter === "all" ? "btn--primary" : "btn--ghost")} onClick={() => setStockFilter("all")}>All stock</button>
            <button className={"btn btn--sm " + (stockFilter === "out" ? "btn--primary" : "btn--ghost")}
                    title="Products sold in the period with nothing on the shelf now — the reorder list"
                    onClick={() => setStockFilter("out")}>Out of stock</button>
          </span>
        )}
        {tab === "profit" && (
          <select className="input" style={{ height: 32, marginLeft: groupBy === "product" ? 8 : "auto" }} value={groupBy} onChange={(e) => setGroupBy(e.target.value)}>
            <option value="product">By product</option>
            <option value="department">By department</option>
            <option value="staff">By clerk</option>
            <option value="day">By day</option>
            <option value="week">By week</option>
            <option value="month">By month</option>
          </select>
        )}
      </div>
      )}

      {err && <div style={{ padding: 12, fontSize: "var(--fs-13)", color: "var(--void-text)" }}>Couldn't load: {err}</div>}

      {tab === "profit" && (
        <React.Fragment>
          <div className="off-kpi-grid" style={{ marginBottom: 16 }}>
            <window.KpiCard label="Net sales" value={rptMoney(t.netCents)} delta={{ text: "ex GST, refunds netted" }}/>
            <window.KpiCard label="Gross profit" value={t.gpCents == null ? "—" : rptMoney(t.gpCents)}
              delta={{ text: t.gpCents == null ? "some lines carry no cost" : "net sales − at-sale cost" }}/>
            <window.KpiCard label="GP %" value={t.gpPercent == null ? "—" : rptNum(t.gpPercent) + "%"} delta={{ text: "on net sales" }}/>
            <window.KpiCard label="Units" value={rptNum(t.quantity)} delta={{ text: "sold in the period" }}/>
          </div>
          <div className="sup-sec">
            <div className="sup-sec-head">
              <span className="sup-sec-title">Profit by {rptGroupNoun}</span>
              {data && <span className="sup-tab-count" style={{ marginLeft: 8 }}>{data.rows.length}</span>}
            </div>
            {loading && !data && <div style={{ padding: 14, fontSize: "var(--fs-13)", color: "var(--text-subtle)" }}>Loading…</div>}
            {data && data.rows.length === 0 && (
              <div style={{ padding: 14, fontSize: "var(--fs-13)", color: "var(--text-subtle)" }}>No sales in this period.</div>
            )}
            {data && data.rows.length > 0 && (() => {
              const shown = groupBy === "product" && stockFilter === "out"
                ? data.rows.filter((r) => r.sohUnits != null && r.sohUnits <= 0)
                : data.rows;
              if (shown.length === 0) {
                return <div style={{ padding: 14, fontSize: "var(--fs-13)", color: "var(--text-subtle)" }}>Nothing sold in the period is out of stock — the shelf covers everything that moved.</div>;
              }
              return (
              <div className="sup-grid">
                <div className="sup-grid-row sup-grid-row--head" style={{ gridTemplateColumns: profitCols }}>
                  <span>{rptGroupNoun.charAt(0).toUpperCase() + rptGroupNoun.slice(1)}</span>
                  <span className="sup-grid-num">Qty</span>
                  <span className="sup-grid-num" title="Gross sales, GST included — what went through the till">Gross</span>
                  <span className="sup-grid-num" title="Net of GST — the basis for GP">Net</span>
                  <span className="sup-grid-num">Cost</span>
                  <span className="sup-grid-num">GP</span>
                  <span className="sup-grid-num">GP %</span>
                  {rptIsPeriod ? (
                    <span className="sup-grid-num" title="Baskets through the till. Refunds are not counted as sales.">Sales</span>
                  ) : (
                    <span className="sup-grid-num" title="On the shelf right now (stock ledger)">SOH</span>
                  )}
                  {rptIsPeriod ? (
                    <span className="sup-grid-num" title="Net sales ÷ sales — the average basket">Avg basket</span>
                  ) : (
                    <span className="sup-grid-num">{groupBy === "product" ? "Reorder" : ""}</span>
                  )}
                </div>
                {shown.map((r) => (
                  <div key={r.key} className="sup-grid-row sup-grid-row--body" style={{ gridTemplateColumns: profitCols }}>
                    <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={r.label}>
                      {r.isOpenItem ? <span style={{ color: "var(--hold-text)" }}>{r.label}</span> : r.label}
                    </span>
                    <span className="sup-grid-num">{rptNum(r.quantity)}</span>
                    <span className="sup-grid-num">{rptMoney(r.grossCents)}</span>
                    <span className="sup-grid-num">{rptMoney(r.netCents)}</span>
                    <span className="sup-grid-num sup-grid-muted" title={r.costCents == null ? "At least one sale of this line carried no cost — GP cannot be computed honestly." : ""}>{r.costCents == null ? "—" : rptMoney(r.costCents)}</span>
                    <span className="sup-grid-num">{r.gpCents == null ? "—" : rptMoney(r.gpCents)}</span>
                    <span className="sup-grid-num">{r.gpPercent == null ? "—" : rptNum(r.gpPercent) + "%"}</span>
                    {rptIsPeriod ? (
                      <span className="sup-grid-num">{rptNum(r.salesCount)}</span>
                    ) : (
                      <span className="sup-grid-num" style={r.sohUnits != null && r.sohUnits <= 0 ? { color: "var(--void-text)", fontWeight: 600 } : undefined}
                            title={r.sohUnits == null ? "" : r.sohUnits <= 0 ? "Sold in the period but nothing on the shelf — reorder or count" : ""}>
                        {r.sohUnits == null ? "—" : rptNum(r.sohUnits)}
                      </span>
                    )}
                    {rptIsPeriod ? (
                      <span className="sup-grid-num">{r.averageBasketCents == null ? "—" : rptMoney(r.averageBasketCents)}</span>
                    ) : (
                      <span>
                        {groupBy === "product" && (
                          <RptOrderCell row={r} pref={r.productId ? prefs[r.productId] || null : null} onAdded={loadDrafts} pushToast={pushToast}/>
                        )}
                      </span>
                    )}
                  </div>
                ))}
              </div>
              );
            })()}
          </div>
        </React.Fragment>
      )}

      {tab === "openItems" && (
        <div className="sup-sec">
          <div className="sup-sec-head">
            <span className="sup-sec-title">Open items — sold off-catalogue, by the note typed at the till</span>
            {openItems && <span className="sup-tab-count" style={{ marginLeft: 8 }}>{openItems.length}</span>}
          </div>
          <div style={{ padding: "8px 14px", fontSize: "var(--fs-12)", color: "var(--text-subtle)" }}>
            These sales moved no stock — the note is their only trace. Range the product (add it with its barcode),
            then it appears here with a Reorder button like any other line.
          </div>
          {openItems && openItems.length === 0 && (
            <div style={{ padding: 14, fontSize: "var(--fs-13)", color: "var(--text-subtle)" }}>No open items in this period — everything scanned.</div>
          )}
          {openItems && openItems.length > 0 && (
            <div className="sup-grid">
              <div className="sup-grid-row sup-grid-row--head" style={{ gridTemplateColumns: openCols }}>
                <span>Note</span>
                <span className="sup-grid-num">Times sold</span>
                <span className="sup-grid-num">Units</span>
                <span className="sup-grid-num">Value</span>
                <span>First sold</span>
                <span>Last sold</span>
              </div>
              {openItems.map((r, i) => (
                <div key={i} className="sup-grid-row sup-grid-row--body" style={{ gridTemplateColumns: openCols }}>
                  <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={r.note}>{r.note || "(no description typed)"}</span>
                  <span className="sup-grid-num">{rptNum(r.timesSold)}</span>
                  <span className="sup-grid-num">{rptNum(r.units)}</span>
                  <span className="sup-grid-num">{rptMoney(r.valueCents)}</span>
                  <span className="sup-grid-muted">{rptDate(r.firstAt)}</span>
                  <span className="sup-grid-muted">{rptDate(r.lastAt)}</span>
                </div>
              ))}
            </div>
          )}
        </div>
      )}

      {tab === "range" && (
        <React.Fragment>
          <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap", marginBottom: 14 }}>
            <span style={{ fontSize: "var(--fs-13)", fontWeight: 600 }}>Brand</span>
            {/* The three are mutually exclusive: picking one clears the others,
                so the table always says which single thing it is showing. */}
            <select className="input" style={{ height: 32, minWidth: 200 }} value={rangeBrand}
              onChange={(e) => { setRangeBrand(e.target.value); if (e.target.value) { setRangeSupplier(""); setRangeCategory(""); } }}>
              <option value="">Choose a brand…</option>
              {brands.map((b) => <option key={b} value={b}>{b}</option>)}
            </select>
            <span style={{ fontSize: "var(--fs-12)", color: "var(--text-subtle)" }}>or</span>
            <span style={{ fontSize: "var(--fs-13)", fontWeight: 600 }}>Supplier</span>
            <select className="input" style={{ height: 32, minWidth: 200 }} value={rangeSupplier}
              onChange={(e) => { setRangeSupplier(e.target.value); if (e.target.value) { setRangeBrand(""); setRangeCategory(""); } }}>
              <option value="">Choose a supplier…</option>
              {rangeSuppliers.map((sp) => <option key={sp.id} value={sp.id}>{sp.name}</option>)}
            </select>
            <span style={{ fontSize: "var(--fs-12)", color: "var(--text-subtle)" }}>or</span>
            <span style={{ fontSize: "var(--fs-13)", fontWeight: 600 }}>Category</span>
            <select className="input" style={{ height: 32, minWidth: 200 }} value={rangeCategory}
              onChange={(e) => { setRangeCategory(e.target.value); if (e.target.value) { setRangeBrand(""); setRangeSupplier(""); } }}>
              <option value="">Choose a category…</option>
              {/* Sorted by NAME here, not by the sort_order the API returns.
                  That order is the store's own merchandising sequence and is
                  right for the till and the product form — but this is a
                  find-it list of ~40 departments, and you scan those
                  alphabetically. Sorted on a copy so nothing else shifts. */}
              {rangeCategories
                .slice()
                .sort((a, b) => String(a.name).localeCompare(String(b.name), "en-AU", { sensitivity: "base", numeric: true }))
                .map((c) => <option key={c.id} value={c.id}>{c.name}</option>)}
              {/* Findable on purpose — these are the products someone needs to fix. */}
              <option value="(uncategorised)">(uncategorised)</option>
            </select>
            {rangeLoading && <span className="spinner"/>}
          </div>
          {!range && !rangeLoading && (
            <div style={{ padding: 14, fontSize: "var(--fs-13)", color: "var(--text-subtle)" }}>
              Pick a brand, a supplier or a category — you get the whole range in one table: stock on hand, sales, sell price,
              latest invoiced cost, margin and barcode status. Export CSV gives the shareable file.
              A brand appears in the list once its products carry the brand field; category covers everything else,
              including the products that have no category set at all.
            </div>
          )}
          {range && (
            <React.Fragment>
              <div className="off-kpi-grid" style={{ marginBottom: 16 }}>
                <window.KpiCard label="Products" value={rptNum(range.totals.products)} delta={{ text: rptNum(range.totals.sohUnits) + " units on hand" }}/>
                <window.KpiCard label="Value at cost" value={rptMoney(range.totals.valueAtCostCents)} delta={{ text: "ex GST, latest invoiced cost" }}/>
                <window.KpiCard label="Value at retail" value={rptMoney(range.totals.valueAtRetailCents)} delta={{ text: "inc GST, current sell" }}/>
                <window.KpiCard label="Sold to date" value={rptNum(range.totals.unitsSold) + " units"} delta={{ text: rptMoney(range.totals.salesCents) + " of sales" }}/>
              </div>
              <div className="sup-sec">
                <div className="sup-sec-head">
                  <span className="sup-sec-title">Range — {rptRangeLabel()}</span>
                  <span style={{ marginLeft: 10, fontSize: "var(--fs-12)", color: "var(--text-subtle)" }}>
                    Sold covers {from} to {to} · stock and expiry are as at now
                  </span>
                  <span className="sup-tab-count" style={{ marginLeft: 8 }}>{range.rows.length}</span>
                </div>
                {range.rows.length === 0 && (
                  <div style={{ padding: 14, fontSize: "var(--fs-13)", color: "var(--text-subtle)" }}>No products found for this selection.</div>
                )}
                {range.rows.length > 0 && (
                  <div className="sup-grid">
                    <div className="sup-grid-row sup-grid-row--head" style={{ gridTemplateColumns: rangeCols }}>
                      <span>Product</span>
                      <span>Barcode</span>
                      <span className="sup-grid-num">SOH</span>
                      <span className="sup-grid-num">Sold</span>
                      <span className="sup-grid-num">Sell (inc GST)</span>
                      <span className="sup-grid-num">Cost (ex GST)</span>
                      <span className="sup-grid-num">GP %</span>
                      <span className="sup-grid-num" title="Nearest expiry sighted on the shop floor. Blank means nobody has flagged this product — not that nothing is expiring.">Expiry</span>
                    </div>
                    {range.rows.map((r) => (
                      <div key={r.productId} className="sup-grid-row sup-grid-row--body" style={{ gridTemplateColumns: rangeCols }}>
                        <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }} title={r.name}>{r.name}</span>
                        <span className="sup-grid-mono sup-grid-muted">
                          {r.barcode || <span style={{ color: "var(--hold-text)", fontWeight: 600 }} title="No barcode on file — won't scan at the till">none</span>}
                        </span>
                        <span className="sup-grid-num">{rptNum(r.sohUnits)}</span>
                        <span className="sup-grid-num">{rptNum(r.unitsSold)}</span>
                        <span className="sup-grid-num">{rptMoney(r.sellPriceCents)}</span>
                        <span className="sup-grid-num sup-grid-muted">{r.costCents == null ? "—" : rptMoney(r.costCents)}</span>
                        <span className="sup-grid-num" style={r.gpPercent != null && r.gpPercent < 20 ? { color: "var(--void-text, #a33)", fontWeight: 600 } : undefined}>
                          {r.gpPercent == null ? "—" : Math.round(r.gpPercent) + "%"}
                        </span>
                        <span className="sup-grid-num" title={r.nextExpiry ? (r.expiryFlags > 1 ? r.expiryFlags + " sightings open, soonest shown" : "Sighted on the shop floor") : "Nobody has flagged this product"}>
                          {r.nextExpiry
                            ? <span style={rptExpiryTone(r.nextExpiry)}>{r.nextExpiry}</span>
                            : <span style={{ color: "var(--text-subtle)" }}>—</span>}
                        </span>
                      </div>
                    ))}
                  </div>
                )}
              </div>
            </React.Fragment>
          )}
        </React.Fragment>
      )}

      {trayOpen && (
        <RptDraftsTray
          drafts={drafts}
          onChanged={loadDrafts}
          onClose={() => setTrayOpen(false)}
          pushToast={pushToast}
        />
      )}
    </Screen>
  );
}

window.OFFICE_SCREENS = Object.assign(window.OFFICE_SCREENS || {}, {
  reports: ReportsLive,
});
