// Reports — Loyverse-style admin reporting (summary, by item/category/employee/
// payment, receipts, shifts) with a date-range filter and bar chart.
// Tabular sections support per-column sort + text filter.

function AdminReports() {
  const { db } = usePOS();
  const [section, setSection] = React.useState("summary");
  const [range, setRange] = React.useState(() => {
    const from = new Date(); from.setDate(from.getDate() - 6);
    return { from: dayKey(from), to: dayKey() };
  });
  const [orders, setOrders] = React.useState([]);
  const [shifts, setShifts] = React.useState([]);
  const [movements, setMovements] = React.useState([]);
  const [loading, setLoading] = React.useState(true);

  // Fetch once per range change.
  React.useEffect(() => {
    let cancelled = false;
    setLoading(true);
    Promise.all([
      db.collection("orders")
        .where("dateKey", ">=", range.from)
        .where("dateKey", "<=", range.to).get(),
      db.collection("shifts")
        .where("dateKey", ">=", range.from)
        .where("dateKey", "<=", range.to).get(),
      db.collection("stock_movements")
        .where("dateKey", ">=", range.from)
        .where("dateKey", "<=", range.to).get(),
    ]).then(([o, s, m]) => {
      if (cancelled) return;
      setOrders(o.docs.map((d) => ({ id: d.id, ...d.data() })));
      setShifts(s.docs.map((d) => ({ id: d.id, ...d.data() })));
      setMovements(m.docs.map((d) => ({ id: d.id, ...d.data() })));
      setLoading(false);
    });
    return () => { cancelled = true; };
  }, [range.from, range.to]);

  const paid = orders.filter((o) => o.paid !== false);

  const grossSales = paid.reduce((s, o) =>
    s + o.items.reduce((a, i) => a + (i.price + (i.modPrice || 0)) * i.qty, 0), 0);
  const discounts = paid.reduce((s, o) => s + (o.discount || 0) + (o.depositApplied || 0), 0);
  const refunds = paid.reduce((s, o) => s + (o.refundedAmount || 0), 0);
  const netSales = paid.reduce((s, o) => s + (o.total || 0), 0) - refunds;
  const cogsTotal = paid.reduce((s, o) =>
    s + o.items.reduce((a, i) => a + (i.cogs || 0) * i.qty, 0), 0);
  const grossProfit = netSales - cogsTotal;

  const presetRange = (days) => {
    const from = new Date(); from.setDate(from.getDate() - (days - 1));
    setRange({ from: dayKey(from), to: dayKey() });
  };

  const SECTIONS = [
    { id: "summary",  label: "Sales summary" },
    { id: "byItem",   label: "Sales by item" },
    { id: "byCat",    label: "Sales by category" },
    { id: "byEmp",    label: "Sales by employee" },
    { id: "byPay",    label: "Sales by payment type" },
    { id: "byMod",    label: "Sales by modifier" },
    { id: "receipts", label: "Receipts" },
    { id: "shifts",   label: "Shifts" },
    { id: "inventory", label: "Inventory valuation" },
    { id: "movements", label: "Stock movements" },
  ];

  return (
    <div className="admin-pane reports-pane">
      <header className="pane-head">
        <div>
          <div className="eyebrow mono">REPORTS · {range.from} → {range.to}</div>
          <h2>{SECTIONS.find((s) => s.id === section).label}</h2>
        </div>
        <div className="row" style={{ gap: 6, flexWrap: "wrap", justifyContent: "flex-end" }}>
          {[
            { label: "Today",     d: 1 },
            { label: "Last 7d",   d: 7 },
            { label: "Last 30d",  d: 30 },
            { label: "Last 90d",  d: 90 },
          ].map((p) => <button key={p.d} className="btn-ghost-sm" onClick={() => presetRange(p.d)}>{p.label}</button>)}
          <input type="date" value={range.from} max={range.to} onChange={(e) => setRange({ ...range, from: e.target.value })} style={{ maxWidth: 160 }} />
          <input type="date" value={range.to} min={range.from} onChange={(e) => setRange({ ...range, to: e.target.value })} style={{ maxWidth: 160 }} />
        </div>
      </header>

      <div className="reports-layout">
        <aside className="reports-rail">
          {SECTIONS.map((s) => (
            <button key={s.id} className={"reports-tab" + (section === s.id ? " active" : "")}
              onClick={() => setSection(s.id)}>{s.label}</button>
          ))}
        </aside>

        <main className="reports-main">
          {loading && <div className="muted mono" style={{ padding: 24 }}>Loading…</div>}

          {!loading && section === "summary" && (
            <SummaryView paid={paid} range={range}
              grossSales={grossSales} refunds={refunds} discounts={discounts}
              netSales={netSales} grossProfit={grossProfit} />
          )}
          {!loading && section === "byItem"   && <ByItemView   paid={paid} />}
          {!loading && section === "byCat"    && <ByCategoryView paid={paid} />}
          {!loading && section === "byEmp"    && <ByEmployeeView paid={paid} />}
          {!loading && section === "byPay"    && <ByPaymentView paid={paid} />}
          {!loading && section === "byMod"    && <ByModifierView paid={paid} />}
          {!loading && section === "receipts" && <ReceiptsView orders={orders} />}
          {!loading && section === "shifts"   && <ShiftsView shifts={shifts} />}
          {!loading && section === "inventory" && <InventoryValuationView />}
          {!loading && section === "movements" && <StockMovementsView movements={movements} />}
        </main>
      </div>
    </div>
  );
}

function KPI2({ label, value, sub, accent }) {
  return (
    <div className={"kpi-card" + (accent ? " accent" : "")}>
      <div className="kpi-label mono">{label.toUpperCase()}</div>
      <Mono className="kpi-value">{value}</Mono>
      {sub && <div className="kpi-delta mono">{sub}</div>}
    </div>
  );
}

function SummaryView({ paid, range, grossSales, refunds, discounts, netSales, grossProfit }) {
  const days = [];
  const start = new Date(range.from), end = new Date(range.to);
  for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
    days.push(d.toISOString().slice(0, 10));
  }
  const byDay = Object.fromEntries(days.map((d) => [d, { h: d.slice(5), orders: 0, revenue: 0 }]));
  paid.forEach((o) => { if (byDay[o.dateKey]) { byDay[o.dateKey].orders++; byDay[o.dateKey].revenue += (o.total || 0); } });
  const chartData = days.map((d) => byDay[d]);
  const marginPct = netSales > 0 ? Math.round(grossProfit / netSales * 100) : 0;

  return (
    <React.Fragment>
      <div className="kpis kpis-5">
        <KPI2 label="Gross sales" value={money(grossSales)} sub={`${paid.length} orders`} />
        <KPI2 label="Refunds" value={money(refunds)} sub="—" />
        <KPI2 label="Discounts" value={money(discounts)} sub="deposits & promos" />
        <KPI2 label="Net sales" value={money(netSales)} sub={`avg ${paid.length ? money(Math.round(netSales / paid.length)) : "—"}`} accent />
        <KPI2 label="Gross profit" value={money(grossProfit)} sub={`${marginPct}% margin`} />
      </div>

      <section className="panel-card" style={{ marginTop: 16 }}>
        <h3>Net sales · per day</h3>
        {paid.length === 0
          ? <div className="muted mono" style={{ padding: 24 }}>No paid orders in this range.</div>
          : <BarChart data={chartData} />}
      </section>
    </React.Fragment>
  );
}

function aggregateItems(paid, keyFn) {
  const m = {};
  paid.forEach((o) => o.items.forEach((i) => {
    const k = keyFn(i, o); if (!k) return;
    const row = m[k] || (m[k] = { key: k, qty: 0, gross: 0, cogs: 0 });
    row.qty += i.qty;
    row.gross += (i.price + (i.modPrice || 0)) * i.qty;
    row.cogs += (i.cogs || 0) * i.qty;
  }));
  return Object.values(m).map((r) => ({
    ...r,
    margin: r.gross - r.cogs,
    marginPct: r.gross > 0 ? Math.round((r.gross - r.cogs) / r.gross * 100) : 0,
  }));
}

function sumBy(rows, k) { return rows.reduce((s, r) => s + (Number(r[k]) || 0), 0); }

function ByItemView({ paid }) {
  const rows = aggregateItems(paid, (i) => i.name);
  return <DataReport
    columns={[
      { label: "Item",   key: "key" },
      { label: "Qty",    key: "qty",    numeric: true },
      { label: "Gross",  key: "gross",  numeric: true, format: money },
      { label: "COGS",   key: "cogs",   numeric: true, format: money },
      { label: "Margin", key: "margin", numeric: true,
        render: (r) => `${money(r.margin)} · ${r.marginPct}%` },
    ]}
    rows={rows}
    defaultSort={{ key: "gross", dir: "desc" }}
    totalRow={{ key: "Total", qty: sumBy(rows, "qty"), gross: sumBy(rows, "gross"),
                cogs: sumBy(rows, "cogs"), margin: sumBy(rows, "margin"),
                marginPct: sumBy(rows, "gross") > 0
                  ? Math.round(sumBy(rows, "margin") / sumBy(rows, "gross") * 100) : 0 }}
  />;
}

function ByCategoryView({ paid }) {
  const { products } = usePOS();
  const catByProductId = Object.fromEntries(products.map((p) => [p.id, p.category]));
  const rows = aggregateItems(paid, (i) => catByProductId[i.id] || "Uncategorised");
  return <DataReport
    columns={[
      { label: "Category", key: "key" },
      { label: "Qty",      key: "qty",    numeric: true },
      { label: "Gross",    key: "gross",  numeric: true, format: money },
      { label: "COGS",     key: "cogs",   numeric: true, format: money },
      { label: "Margin",   key: "margin", numeric: true,
        render: (r) => `${money(r.margin)} · ${r.marginPct}%` },
    ]}
    rows={rows}
    defaultSort={{ key: "gross", dir: "desc" }}
    totalRow={{ key: "Total", qty: sumBy(rows, "qty"), gross: sumBy(rows, "gross"),
                cogs: sumBy(rows, "cogs"), margin: sumBy(rows, "margin"),
                marginPct: sumBy(rows, "gross") > 0
                  ? Math.round(sumBy(rows, "margin") / sumBy(rows, "gross") * 100) : 0 }}
  />;
}

function ByEmployeeView({ paid }) {
  const m = {};
  paid.forEach((o) => {
    const k = o.cashierEmail || "(unknown)";
    const row = m[k] || (m[k] = { key: k, orders: 0, gross: 0 });
    row.orders++;
    row.gross += (o.total || 0);
  });
  const rows = Object.values(m).map((r) => ({
    ...r,
    avg: r.orders ? Math.round(r.gross / r.orders) : 0,
  }));
  return <DataReport
    columns={[
      { label: "Cashier",   key: "key" },
      { label: "Orders",    key: "orders", numeric: true },
      { label: "Net sales", key: "gross",  numeric: true, format: money },
      { label: "Avg ticket", key: "avg",   numeric: true, format: money },
    ]}
    rows={rows}
    defaultSort={{ key: "gross", dir: "desc" }}
    totalRow={{ key: "Total", orders: sumBy(rows, "orders"),
                gross: sumBy(rows, "gross"), avg: "" }}
  />;
}

function ByPaymentView({ paid }) {
  const m = {};
  paid.forEach((o) => {
    const splits = (o.payments && o.payments.length) ? o.payments : [{ method: o.paymentMethod || "—", amount: o.total }];
    splits.forEach((p) => {
      const k = (p.method || "—").toUpperCase();
      const row = m[k] || (m[k] = { key: k, count: 0, total: 0 });
      row.count++; row.total += Number(p.amount) || 0;
    });
  });
  const allTotal = Object.values(m).reduce((s, r) => s + r.total, 0) || 1;
  const rows = Object.values(m).map((r) => ({
    ...r,
    share: Math.round(r.total / allTotal * 100),
  }));
  return <DataReport
    columns={[
      { label: "Payment", key: "key" },
      { label: "Count",   key: "count", numeric: true },
      { label: "Total",   key: "total", numeric: true, format: money },
      { label: "Share",   key: "share", numeric: true, render: (r) => `${r.share}%` },
    ]}
    rows={rows}
    defaultSort={{ key: "total", dir: "desc" }}
    totalRow={{ key: "Total", count: sumBy(rows, "count"),
                total: sumBy(rows, "total"), share: 100 }}
  />;
}

function ByModifierView({ paid }) {
  const m = {};
  paid.forEach((o) => o.items.forEach((i) => (i.mods || []).forEach((mod) => {
    const row = m[mod] || (m[mod] = { key: mod, qty: 0, revenue: 0 });
    row.qty += i.qty;
    row.revenue += (i.modPrice || 0) * i.qty;
  })));
  const rows = Object.values(m);
  return <DataReport
    columns={[
      { label: "Modifier",         key: "key" },
      { label: "Times used",       key: "qty",     numeric: true },
      { label: "Modifier revenue", key: "revenue", numeric: true, format: money },
    ]}
    rows={rows}
    defaultSort={{ key: "qty", dir: "desc" }}
    totalRow={rows.length ? { key: "Total", qty: sumBy(rows, "qty"), revenue: sumBy(rows, "revenue") } : null}
  />;
}

function ReceiptsView({ orders }) {
  const [refundFor, setRefundFor] = React.useState(null);
  const rows = orders.map((o) => ({
    id: o.id,
    when: o.createdAt?.toDate ? o.createdAt.toDate() : null,
    shortId: o.shortId || "",
    customer: o.customer || "—",
    table: o.table || "—",
    items: o.items.reduce((a, i) => a + i.qty, 0),
    payment: (o.paymentMethod || "—").toUpperCase(),
    status: o.paid === false ? "OPEN" : (o.status || "open").toUpperCase(),
    total: o.total || 0,
    refunded: o.refundedAmount || 0,
    _order: o,
  }));
  return (
    <React.Fragment>
      <DataReport
        columns={[
          { label: "When",     key: "when",     render: (r) => r.when ? r.when.toLocaleString("en-GB") : "—" },
          { label: "#",        key: "shortId" },
          { label: "Customer", key: "customer" },
          { label: "Table",    key: "table" },
          { label: "Items",    key: "items",    numeric: true },
          { label: "Payment",  key: "payment" },
          { label: "Status",   key: "status" },
          { label: "Total",    key: "total",    numeric: true, format: money },
          { label: "Refunded", key: "refunded", numeric: true,
            render: (r) => r.refunded > 0
              ? <Mono className="stock-low">−{money(r.refunded)}</Mono>
              : <Mono className="muted">—</Mono> },
          { label: "",         key: "_act", skipSort: true,
            cell: (r) => r._order.paid !== false && r._order.status !== "refunded"
              ? <button className="btn-ghost-sm" onClick={() => setRefundFor(r._order)}>Refund</button>
              : null },
        ]}
        rows={rows}
        defaultSort={{ key: "when", dir: "desc" }}
      />
      {refundFor && <RefundSheet order={refundFor} onClose={() => setRefundFor(null)} />}
    </React.Fragment>
  );
}

function ShiftsView({ shifts }) {
  const rows = shifts.map((s) => ({
    id: s.id,
    dateKey: s.dateKey || "—",
    cashier: s.cashierEmail || "—",
    opened: s.openedAt?.toDate ? s.openedAt.toDate() : null,
    closed: s.closedAt?.toDate ? s.closedAt.toDate() : null,
    float: s.openingCash || 0,
    cashSales: s.cashSales || 0,
    expected: s.expectedCash || 0,
    counted: s.status === "closed" ? (s.closingCash || 0) : null,
    variance: s.status === "closed" ? (s.variance || 0) : null,
  }));
  const fmtTime = (d) => d ? d.toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" }) : "—";
  return <DataReport
    columns={[
      { label: "Date",       key: "dateKey" },
      { label: "Cashier",    key: "cashier" },
      { label: "Opened",     key: "opened",   render: (r) => fmtTime(r.opened) },
      { label: "Closed",     key: "closed",   render: (r) => fmtTime(r.closed) },
      { label: "Float",      key: "float",    numeric: true, format: money },
      { label: "Cash sales", key: "cashSales", numeric: true, format: money },
      { label: "Expected",   key: "expected", numeric: true, format: money },
      { label: "Counted",    key: "counted",  numeric: true,
        render: (r) => r.counted == null ? "—" : money(r.counted) },
      { label: "Variance",   key: "variance", numeric: true,
        render: (r) => r.variance == null ? "—" :
          `${r.variance >= 0 ? "+" : "−"}${money(Math.abs(r.variance))}` },
    ]}
    rows={rows}
    defaultSort={{ key: "opened", dir: "desc" }}
  />;
}

// Current stock value (live snapshot, not date-ranged) — products priced at
// their cogs, ingredients at their weighted-average receiving cost. Items
// with stock >= 999 ("unlimited", the admin convention) are excluded, same
// as everywhere else stock is aggregated, or the total would be nonsense.
function InventoryValuationView() {
  const { products, ingredients } = usePOS();
  const rows = [
    ...products.filter((p) => (Number(p.stock) || 0) < 999).map((p) => ({
      key: p.name, type: "Product", stock: Number(p.stock) || 0, unit: "pcs",
      unitCost: Number(p.cogs) || 0, value: (Number(p.stock) || 0) * (Number(p.cogs) || 0),
      low: (Number(p.stock) || 0) <= (Number(p.stockThreshold) || 5),
    })),
    ...ingredients.filter((i) => (Number(i.stock) || 0) < 999).map((i) => ({
      key: i.name, type: "Ingredient", stock: Number(i.stock) || 0, unit: i.unit || "",
      unitCost: Number(i.avgCost) || 0, value: (Number(i.stock) || 0) * (Number(i.avgCost) || 0),
      low: (Number(i.stock) || 0) <= (Number(i.stockThreshold) || 5),
    })),
  ];
  const totalValue = sumBy(rows, "value");
  const lowCount = rows.filter((r) => r.low).length;
  return (
    <React.Fragment>
      <div className="kpis kpis-5">
        <KPI2 label="Total stock value" value={money(totalValue)} sub={`${rows.length} tracked items`} accent />
        <KPI2 label="Low stock" value={String(lowCount)} sub="at or below alert threshold" />
      </div>
      <DataReport
        columns={[
          { label: "Item",      key: "key" },
          { label: "Type",      key: "type" },
          { label: "Stock",     key: "stock",    numeric: true, render: (r) => `${r.stock} ${r.unit}`.trim() },
          { label: "Unit cost", key: "unitCost", numeric: true, format: money },
          { label: "Value",     key: "value",    numeric: true, format: money },
          { label: "Low stock", key: "low",      render: (r) => r.low ? <span className="stock-low">⚠️ Yes</span> : "—" },
        ]}
        rows={rows}
        defaultSort={{ key: "value", dir: "desc" }}
        totalRow={rows.length ? { key: "Total", type: "", stock: "", unit: "", unitCost: "", value: totalValue, low: "" } : null}
      />
    </React.Fragment>
  );
}

// Unified audit log of every stock change (sale, refund, recipe consumption,
// receiving, manual adjustment) across both products and ingredients, over
// the report's date range.
function StockMovementsView({ movements }) {
  const [typeFilter, setTypeFilter] = React.useState("all"); // all | product | ingredient
  const filtered = typeFilter === "all" ? movements : movements.filter((m) => m.targetType === typeFilter);
  const rows = filtered.map((m) => ({
    id: m.id,
    when: m.createdAt?.toDate ? m.createdAt.toDate() : null,
    type: m.targetType === "ingredient" ? "Ingredient" : "Product",
    name: m.targetName || "—",
    change: m.qtyChange || 0,
    after: m.qtyAfter,
    reason: (m.reason || "").replace(/_/g, " "),
    note: m.note || "",
    by: m.userEmail || "system",
  }));
  return (
    <React.Fragment>
      <div className="row" style={{ gap: 6, marginBottom: 8 }}>
        {[["all", "All"], ["product", "Products"], ["ingredient", "Ingredients"]].map(([id, label]) => (
          <button key={id} className={"reports-tab" + (typeFilter === id ? " active" : "")}
            onClick={() => setTypeFilter(id)}>{label}</button>
        ))}
      </div>
      <DataReport
        columns={[
          { label: "When",   key: "when", render: (r) => r.when ? r.when.toLocaleString("en-GB") : "—" },
          { label: "Type",   key: "type" },
          { label: "Item",   key: "name" },
          { label: "Change", key: "change", numeric: true, render: (r) => `${r.change >= 0 ? "+" : ""}${r.change}` },
          { label: "After",  key: "after",  numeric: true },
          { label: "Reason", key: "reason" },
          { label: "Note",   key: "note" },
          { label: "By",     key: "by" },
        ]}
        rows={rows}
        defaultSort={{ key: "when", dir: "desc" }}
      />
    </React.Fragment>
  );
}

// Generic data table with sortable headers + free-text filter. Rows are objects
// keyed by column.key. Column shape:
//   { label, key, numeric?, format?, render?, cell?, skipSort? }
//   format(value) → string         — wrapped in <Mono>
//   render(row)   → ReactNode      — wrapped in <Mono>
//   cell(row)     → ReactNode      — used raw (no wrap); also opts out of sort
function DataReport({ columns, rows, totalRow, defaultSort }) {
  const [sort, setSort] = React.useState(defaultSort || { key: columns[0]?.key, dir: "asc" });
  const [query, setQuery] = React.useState("");

  const toggleSort = (col) => {
    if (col.skipSort) return;
    setSort((s) => s.key === col.key
      ? { key: col.key, dir: s.dir === "asc" ? "desc" : "asc" }
      : { key: col.key, dir: col.numeric ? "desc" : "asc" });
  };

  const renderCellValue = (col, row) => {
    if (col.cell) return col.cell(row);
    if (col.render) return col.render(row);
    const v = row[col.key];
    return col.format ? col.format(v) : v;
  };

  const filtered = React.useMemo(() => {
    const q = query.trim().toLowerCase();
    if (!q) return rows;
    return rows.filter((r) => columns.some((c) => {
      const v = c.cell ? "" : (c.render ? c.render(r) : r[c.key]);
      return String(v == null ? "" : v).toLowerCase().includes(q);
    }));
  }, [rows, query, columns]);

  const sorted = React.useMemo(() => {
    if (!sort.key) return filtered;
    const dir = sort.dir === "asc" ? 1 : -1;
    return [...filtered].sort((a, b) => {
      let va = a[sort.key];
      let vb = b[sort.key];
      // Dates: compare via getTime()
      if (va instanceof Date) va = va.getTime();
      if (vb instanceof Date) vb = vb.getTime();
      if (va == null) return 1;
      if (vb == null) return -1;
      if (typeof va === "number" && typeof vb === "number") return (va - vb) * dir;
      return String(va).localeCompare(String(vb), "en", { numeric: true, sensitivity: "base" }) * dir;
    });
  }, [filtered, sort]);

  const exportCSV = () => {
    const toCsv = (v) => `"${String(v == null ? "" : v).replace(/"/g, '""')}"`;
    const renderForCsv = (col, row) => {
      if (col.cell) return ""; // skip action cells
      if (col.render) {
        const out = col.render(row);
        return typeof out === "string" ? out : (row[col.key] ?? "");
      }
      const v = row[col.key];
      return col.format ? col.format(v) : v;
    };
    const lines = [columns.filter((c) => !c.cell).map((c) => c.label).join(",")];
    sorted.forEach((r) => {
      lines.push(columns.filter((c) => !c.cell).map((c) => toCsv(renderForCsv(c, r))).join(","));
    });
    if (totalRow) {
      lines.push(columns.filter((c) => !c.cell).map((c) => toCsv(renderForCsv(c, totalRow))).join(","));
    }
    const blob = new Blob([lines.join("\n")], { type: "text/csv" });
    const a = document.createElement("a");
    a.href = URL.createObjectURL(blob);
    a.download = "report-" + Date.now() + ".csv";
    a.click();
    URL.revokeObjectURL(a.href);
  };

  return (
    <div className="panel-card no-pad" style={{ marginTop: 8 }}>
      <div className="row" style={{ justifyContent: "space-between", padding: "10px 14px", gap: 8 }}>
        <input
          type="search"
          placeholder="Filter rows…"
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          style={{ padding: "6px 10px", border: "1px solid var(--line)", borderRadius: 6, fontSize: 13, maxWidth: 260, flex: 1 }}
        />
        <button className="btn-ghost-sm" onClick={exportCSV} disabled={!sorted.length}>Export CSV</button>
      </div>
      <table className="data-table">
        <thead>
          <tr>
            {columns.map((c) => (
              <th key={c.key}
                onClick={() => toggleSort(c)}
                style={{ cursor: c.skipSort ? "default" : "pointer", userSelect: "none", whiteSpace: "nowrap" }}
                title={c.skipSort ? "" : ("Sort by " + c.label)}
              >
                {c.label}
                {!c.skipSort && (
                  <span className="mono muted" style={{ fontSize: 10, marginLeft: 4 }}>
                    {sort.key === c.key ? (sort.dir === "asc" ? "▲" : "▼") : ""}
                  </span>
                )}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {sorted.length === 0 && (
            <tr><td colSpan={columns.length} className="muted mono" style={{ textAlign: "center", padding: 24 }}>
              {rows.length === 0 ? "No data in range." : "No rows match your filter."}
            </td></tr>
          )}
          {sorted.map((r, i) => (
            <tr key={r.id || i}>
              {columns.map((c) => (
                <td key={c.key}>
                  {c.cell ? c.cell(r) : <Mono>{renderCellValue(c, r)}</Mono>}
                </td>
              ))}
            </tr>
          ))}
          {totalRow && sorted.length > 0 && (
            <tr style={{ fontWeight: 600, background: "var(--paper-soft)" }}>
              {columns.map((c) => (
                <td key={c.key}>
                  {c.cell ? "" : <Mono>{renderCellValue(c, totalRow)}</Mono>}
                </td>
              ))}
            </tr>
          )}
        </tbody>
      </table>
    </div>
  );
}

Object.assign(window, { AdminReports, DataReport });
