// NO PROD JSON API (bucket C) — mock until backend (see PROD_API_INVENTORY.md)
// Represents: GET /reports/commissions/ · CommissionsController — see docs/ISYSTEM_REFERENCE.md §Batch 2 "Commissions report"
/* Commissions — Report ▾ rebuild on the shared Hrs* report shell (src/report-shell.jsx).
   Replaces the legacy CommissionsReport in HostReports.jsx: this file loads after it, so the
   final `window.CommissionsReport` assignment below wins for app.jsx's `report-commissions` route.

   TRACEABILITY (all cites are docs/ISYSTEM_REFERENCE.md §"Commissions" unless noted; the
   serving class in code is ReportsController — `commissions()` L69-85 shell,
   `getCommissionsReport()` L1094-1308 data, routes/admin.php:1444-1471, all unnamed):
   - Page shell: resources/views/admin/reports/commissions.blade.php; AJAX partials
     commissions/all.blade.php, sport.blade.php, thridParty.blade.php (real filename typo);
     JS driver public/js/pages/reports/commissions.js.
   - Filters (form #business-report-form): Category (#tab: ALL/sport/casino/casinolive/virtual,
     default SPORT preselected, "ALL" listed first but not selected; poker hardcoded off),
     User (select2, self + descendants 0 < user_level < 20 excl. Administration/Customer care/
     Affiliate), Period type (m/w), Period = commission months from getDateCalendario()
     (first Monday -> day before next first Monday) or Mon-Sun weeks from 2020-01-06.
     Submit sends only tab, user_id, is_esporta, periodo_mese — is_esporta is never read.
   - NO auto-load: initial generaReportscommissions() call is commented out (commissions.js:53)
     -> table renders an instructional empty state until Search.
   - Layout per search: "Commissions from <start> To <end>" strip (sport adds Closed bets +
     Average bet), selected user's summary card (hidden from affiliates — demo session is the
     super admin so it renders), "Network commissions" one row per non-cashier child
     (expandable subnet drill-down = same partial with level=1, summary card suppressed),
     then the cashier table (letter badge "S", from getUserShops).
   - Row actions: info icon -> infoCommissionProfile modal; red "Costs:" link (show_costs) ->
     statsCosts modal; sport cashier turnover icon (!show_costs) -> statsTurnover modal whose
     finished weeks drill into statsWeekSport. NONE of these four endpoints re-check
     permissions or hierarchy (surfaced in the modal notes, not hidden).
   - No sortable columns, no pagination on the real page — none added here.
   - The blade also @includes admin.reports.modals.newMessage but nothing on this screen
     triggers it (see the NetWin section calling its own copy dead copy-paste) — omitted.
   - Poker exists in backend switches but is unreachable (option hardcoded off,
     getCommissionsReport has no poker case) — not rendered, per "no invented actions".

   KNOWN-BUG POLICY divergences (evident intent implemented, per CLAUDE.md Build policies):
   1) Export: real flow scrapes the rendered DOM (fnExcelReport, commissions.js:81-198) into
      query strings and posts them to ANY /reports/commissions/<tab>/excel — the server writes
      whatever the client sent, with NO permission check and Route::any; collapsed sub-levels
      are silently missing from the file. Here the export is built from the loaded dataset
      (every level + cashiers), CSV via the shared hrsCsv (real files are XLSX).
      <!-- SUGGESTION: rebuild the excel exports server-side from the same query the report
           runs, gate them like the page (support_report_commissions + support_export), and
           restrict Route::any to GET — today any role can POST arbitrary cell values. -->
   2) thridParty.blade.php renders Jackpot with a stray "%" suffix (L41, L106-107) — rendered
      here as a plain amount.
      <!-- SUGGESTION: drop the % suffix on the casino-tab Jackpot cells. -->
   3) Shop rows in sport.blade.php:241 / thridParty.blade.php:203,205 carry duplicate class=
      attributes, so the browser drops the red/green Profit box — the box is rendered here.
      <!-- SUGGESTION: merge the duplicate class attributes so cashier Profit keeps its
           red/green box. -->
   4) all.blade.php:449 prints the MAIN user's $sum_costs in the cashier row instead of the
      cashier's own costs — here each cashier's Costs link shows its own figure.
      <!-- SUGGESTION: pass the shop's own cost aggregate to the cashier row at
           all.blade.php:449. -->
   5) thridParty card "Network profit" omits the -sum_costs term its own color-class uses —
      costs are included consistently here.
      <!-- SUGGESTION: subtract sum_costs in the displayed casino-tab Network profit, matching
           the color condition. -->
   6) Weekly Period defaults to the EARLIEST entry (the first 2020 week) on the real page,
      while the monthly default is the period containing today — evident intent (current week)
      used here.
      <!-- SUGGESTION: default #periodo_weekly to the current week like the monthly select. -->
   7) Sport excel cashier detection compares scraped user-type text against
      trans('backend.usertype_shop_new') while the DOM emits usersLevels() names — moot here
      because the export reads data, not the DOM (see 1).

   LABEL POLICY ("label inferred" where translations resolve only to raw backend.* keys or
   the real string is a hardcoded Italian leak): "Period type" / "Monthly" / "Weekly"
   (backend.period_type / monthly / weekly resolve in no committed lang file); "Costs:" links
   (child rows are hardcoded Italian "Costi:"); "Turnover details" modal (real title hardcoded
   "Dettaglio Turnover"); "Bets by event count" modal (real title hardcoded "DETTAGLIO GIOCATE
   PER EVENTI DI ..."); "% cost" column (real header "% Costo"); "Not set" profile fallback
   (real prints "NON IMPOSTATO"); missing-dates guard die("seleziona le date!") is unreachable
   here because the period selects always hold a value.
   "Network tournover commissions" keeps the real platform's spelling on purpose.
   <!-- SUGGESTION: fix the "tournover" typo in the commissions card label. -->

   PERMISSION HONESTY: sidebar + controller gate = support_report + support_report_commissions,
   but checkUserBoPerm binds ONLY Affiliate/Customer care/Administration levels (returns true
   unconditionally for everyone else); the four modal data endpoints and the excel endpoints
   have no gate at all. Export button in the UI is gated by support_export.

   SHELL/DIVERGENCE NOTE: the real "+" subnet expander renders only when getCountUsers(id) > 0;
   HrsTable's rowDetail chevron renders on every row, so empty subnets state that honestly.

   DATA: deterministic PRNG (mulberry32) seeded per (username | period start | category) —
   identical rows on every load for a given filter set; different commission months/weeks give
   different volumes. Network topology (usernames continue the legacy HostReports mock set) is
   static so the User filter options never shift. Note (2) of the reference: real commissions
   figures only populate when the chosen period exactly matches a stored
   commissions_monthly_report calc period — mirrored in the explainer.

   Styling: shared hrs-* classes plus page-local hrcm-* classes (CSS block appended to
   styles/theme-iwakiri.css by the batch integrator — this file introduces no other deps). */

const { useState: hrcmUseState, useMemo: hrcmUseMemo } = React;

/* ---------------- tiny helpers ---------------- */
const hrcmNoNeg = (n) => Math.max(0, n || 0);
const hrcmR2 = (n) => Math.round((n || 0) * 100) / 100;
/* The seed hash and the deterministic PRNG lived here and are gone with the
   generator. Deleted, not left dormant: this report states what is owed to
   agents and what operators are billed, and a live generator on that path is
   one line away. */
const hrcmDMY = (iso) => { const p = (iso || "").split("-"); return p.length === 3 ? `${p[2]}/${p[1]}/${p[0]}` : ""; };
const hrcmIso = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;

/* usersLevels() display names (config/usertypes.php values; Master/Agent/Promoter/Shop are
   per-skin overridable via custom_*_name — defaults used). */
const HRCM_LEVELS = { 0: "Super Admin", 2: "Skin Access", 8: "Master", 10: "Agent", 15: "Promoter", 20: "Shop" };

/* Category select — "ALL" listed first but SPORT carries the selected attr (blade L77).
   Options are skin-gated (show_sport/show_casino/show_casinolive/show_virtual) but the demo
   session is the super admin, for whom isadmin() bypasses every flag; poker hardcoded off. */
const HRCM_TABS = [
  { value: "all", label: "ALL" },
  { value: "sport", label: "Sport" },
  { value: "casino", label: "Casino" },
  { value: "casinolive", label: "Casino live" },
  { value: "virtual", label: "Virtual" },
];
const HRCM_CATS = ["casino", "casinolive", "virtual", "sport"];
const HRCM_CATW = { casino: 0.55, sport: 0.25, casinolive: 0.13, virtual: 0.07 };

/* Provider split used by the statsCosts modal + cost aggregates (business_report x
   users_providers/profili_cobanco_providers in the real query). */
/* `HRCM_PROVIDERS` was seven invented provider names with a share each. The
   real provider mix comes from `report_user_provider_daily`, and the rate an
   account is billed from `user_providers`. */

/* THE NETWORK, FROM `networkUsers`.
   ------------------------------------------------------------------------
   `HRCM_TREE` was a hand-written topology of fifteen skin admins with their
   masters, agents and a `scale` per shop, and `HRCM_PROVIDERS` was seven
   invented provider names with a share each. Every figure on this report —
   turnover, GGR, and the commission owed on them — was that scale run through
   a mulberry32 seeded on the shop's name.

   Commissions are money owed to real people. This is the screen where an
   invented number is an invented invoice, in both directions.

   Shops are children at SHOP level (20); everything else is a sub-user. */
const HRCM_SHOP_LEVEL = 20;

const hrcmBuildTree = (users) => {
  const byId = new Map();
  (users || []).forEach(u => byId.set(String(u.id), {
    id: u.id, u: u.username, lvl: Number(u.user_level), path: String(u.path || ""),
    kids: [], shops: [],
    /* "At cost" is a commission assignment carrying special_mode — the same
       derivation NetWin uses, and the same UNCLEAR-14 attached to it. An
       at-cost row earns no commission and shows the red Costs link. */
    atCost: !!(u.commission && u.commission.length && u.commission[0].special_mode),
    profile: (u.commission && u.commission.length && u.commission[0].profile)
      ? { name: u.commission[0].profile.name } : null,
  }));
  const roots = [];
  byId.forEach(n => {
    const src = (users || []).find(x => String(x.id) === String(n.id));
    const parent = src && src.parent_id != null ? byId.get(String(src.parent_id)) : null;
    if (!parent) { roots.push(n); return; }
    (n.lvl >= HRCM_SHOP_LEVEL ? parent.shops : parent.kids).push(n);
  });
  if (roots.length === 1) return roots[0];
  return { id: null, u: "(network)", lvl: 0, path: "", kids: roots, shops: [],
           atCost: false, profile: null };
};

/* Own volumes for ONE user, out of `report_user_daily`. Own and not subtree:
   hrcmBuildRow already rolls children up, and summing a subtree at every level
   would count each shop once per ancestor.

   WAS `hrcmShopCats`, which invented bet/win/bonusBet/jackpot per category.

   THREE COLUMNS HAVE NO SOURCE AND ARE NOT INVENTED:

     casinolive, virtual   `report_type_class.vertical` is derived from
                           transaction TYPES and has three values — sport,
                           casino, exchange. Splitting live dealer and virtual
                           out of casino is a GAME-category question this ledger
                           does not answer (UNCLEAR-13, D5). Those categories
                           read zero rather than a share of casino's.
     jackpot               nothing classifies a jackpot contribution. Zero, and
                           the column still renders because upstream renders it.

   `betClosed` — settled sport stake, which sport profit is computed from — is
   real: `report_bet_type_daily` carries `stake` and `open_stake`, so closed is
   the difference. Passed in rather than derived here so it is fetched once. */
const hrcmUserCats = (userId, dayRows, closedByUser) => {
  const cats = hrcmZeroCats();
  (dayRows || []).forEach(r => {
    if (String(r.user_id) !== String(userId)) return;
    const cat = r.vertical === "sport" ? "sport" : r.vertical === "casino" ? "casino" : null;
    if (!cat) return;                       // 'exchange' has no column on this report
    const c = cats[cat];
    const stake = Number(r.stake) || 0, payout = Number(r.payout) || 0;
    if (r.funding === "bonus") { c.bonusBet += stake; }
    else { c.bet += stake; c.win += payout; c.nr += Number(r.bet_count) || 0; }
  });
  cats.sport.betClosed = Number((closedByUser || {})[String(userId)] || 0);
  return cats;
};

/* Own commission, straight out of `commission_accruals`.
   ------------------------------------------------------------------------
   WAS computed in the browser as rate% x volume. The accrual row already IS
   the commission, with the profile's tier bands applied where they were
   applied — server side, once, at calculation time. Re-deriving it here would
   mean reimplementing the tier ladder in JavaScript and quietly disagreeing
   with what the platform will actually pay.

   `commission_game_types` maps onto this screen's own structure exactly:
   sport_turnover -> t, sport_profit -> p, and one code per GGR category. */
const hrcmOwnComm = (userId, accrualRows) => {
  const own = { t: 0, p: 0, c: { casino: 0, casinolive: 0, virtual: 0 } };
  (accrualRows || []).forEach(a => {
    if (String(a.user_id) !== String(userId)) return;
    const amt = Number(a.amount) || 0;
    if (a.game_type === "sport_turnover") own.t += amt;
    else if (a.game_type === "sport_profit") own.p += amt;
    else if (own.c[a.game_type] !== undefined) own.c[a.game_type] += amt;
  });
  own.t = hrcmR2(own.t); own.p = hrcmR2(own.p);
  Object.keys(own.c).forEach(k => { own.c[k] = hrcmR2(own.c[k]); });
  return own;
};

/* Deposits and withdrawals under a node, from `report_player_daily`. Players
   sit under shops, so this is summed at the shop and rolled up — the same
   place the volumes are.

   WAS `hrcmPay`: a `payScale` per shop times a PRNG. */
const hrcmUserPay = (node, playerRows) => {
  const base = node && node.path ? String(node.path) : "";
  let dep = 0, wd = 0;
  (playerRows || []).forEach(r => {
    const p = String(r.user_path || "");
    if (!base || (p !== base && !p.startsWith(base + "."))) return;
    dep += Number(r.deposits) || 0;
    wd  += Number(r.withdrawals) || 0;
  });
  return { dep: hrcmR2(dep), wd: hrcmR2(wd) };
};

/* Provider costs. WAS seven invented provider names with an invented
   percentage per (user, provider) — the percentage being what an operator is
   BILLED. Now `report_user_provider_daily` for the volumes and
   `user_providers` for the rate; a provider with volume but no configured
   percentage yields NO cost rather than a default, because a plausible default
   here is a number somebody could pay. */
const hrcmCostRows = (node, provRows, rateRows) => {
  const base = node && node.path ? String(node.path) : "";
  const rateFor = new Map();
  (rateRows || []).forEach(r => rateFor.set(String(r.provider_id), Number(r.percentage)));
  const byProv = new Map();
  (provRows || []).forEach(r => {
    const p = String(r.user_path || "");
    if (base && p !== base && !p.startsWith(base + ".")) return;
    const k = String(r.provider_id);
    const a = byProv.get(k) || { id: r.provider_id, name: r.provider_name || `Provider ${k}`, bet: 0, win: 0 };
    a.bet += Number(r.stake) || 0;
    a.win += Number(r.payout) || 0;
    byProv.set(k, a);
  });
  return [...byProv.values()].map(a => {
    const pct = rateFor.has(String(a.id)) ? rateFor.get(String(a.id)) : null;
    const profit = hrcmR2(a.bet - a.win);
    return { name: a.name, cat: "casino", bet: hrcmR2(a.bet), win: hrcmR2(a.win), profit, pct,
             cost: pct == null ? null : hrcmR2(pct / 100 * hrcmNoNeg(profit)) };
  }).sort((x, y) => (y.cost || 0) - (x.cost || 0));
};
const hrcmCostTotal = (rows, cat) => hrcmR2((rows || [])
  .filter(r => !cat || cat === "all" || r.cat === cat)
  .reduce((s, r) => s + (r.cost || 0), 0));

/* Defaults: monthly = commission period containing today (matches getDateCalendario default);
   weekly = current week — DIVERGENCE (6) in the header: the real select defaults to the
   earliest 2020 week. */
const HRCM_PM0 = hrsPeriodOptions("commission", 2020)[0].value;
const HRCM_PW0 = hrsPeriodOptions("week", 2020)[0].value;

/* ---------------- deterministic stats ---------------- */
const hrcmZeroCat = () => ({ bet: 0, win: 0, bonusBet: 0, jackpot: 0, betClosed: 0, nr: 0 });
const hrcmZeroCats = () => ({ casino: hrcmZeroCat(), casinolive: hrcmZeroCat(), virtual: hrcmZeroCat(), sport: hrcmZeroCat() });
const hrcmAddCats = (a, b) => {
  HRCM_CATS.forEach(c => { const x = a[c], y = b[c]; x.bet += y.bet; x.win += y.win; x.bonusBet += y.bonusBet; x.jackpot += y.jackpot; x.betClosed += y.betClosed; x.nr += y.nr; });
};

/* Leaf (shop) per-category volumes for one period. Seeded by (shop | period start | cat). */
/* Category profit — casino: bet-win-bonusBet-jackpot; sport: closed bet - win; others bet-win
   (per the all.blade / sport.blade field derivations in the reference). */
const hrcmCatProfit = (cats, cat) => {
  const c = cats[cat];
  if (cat === "casino") return c.bet - c.win - c.bonusBet - c.jackpot;
  if (cat === "sport") return c.betClosed - c.win;
  return c.bet - c.win;
};
const hrcmProfitAll = (cats) => HRCM_CATS.reduce((s, cat) => s + hrcmCatProfit(cats, cat), 0);

/* Recursive row builder — every row carries its SUBTREE volumes (getUserCommInfo /
   getUserReportCommissions aggregate own + subtree), own commissions (zero for at-cost rows)
   and the subtree ("sottoalbero") commissions rolled up from descendants. */
const hrcmBuildRow = (node, period, parentU, d) => {
  const cats = hrcmZeroCats();
  const own = { t: 0, p: 0, c: { casino: 0, casinolive: 0, virtual: 0 } };
  const sotto = { t: 0, p: 0, c: { casino: 0, casinolive: 0, virtual: 0 } };
  let dep = 0, wd = 0;
  const kids = (node.kids || []).map(k => hrcmBuildRow(k, period, node.u, d));
  const shops = (node.shops || []).map(s => hrcmBuildRow(s, period, node.u, d));
  /* Volumes are counted at the SHOP, exactly as upstream aggregates them, and
     rolled up by the loop below. Counting them at every level instead would
     bill each shop once per ancestor. */
  if (node.lvl === HRCM_SHOP_LEVEL) {
    hrcmAddCats(cats, hrcmUserCats(node.id, d.days, d.closed));
    const pay = hrcmUserPay(node, d.players);
    dep = pay.dep; wd = pay.wd;
  }
  kids.concat(shops).forEach(r => {
    hrcmAddCats(cats, r.cats);
    dep = hrcmR2(dep + r.dep); wd = hrcmR2(wd + r.wd);
    sotto.t = hrcmR2(sotto.t + r.own.t + r.sotto.t);
    sotto.p = hrcmR2(sotto.p + r.own.p + r.sotto.p);
    ["casino", "casinolive", "virtual"].forEach(c => { sotto.c[c] = hrcmR2(sotto.c[c] + r.own.c[c] + r.sotto.c[c]); });
  });
  /* The accrual row IS the commission — tiers already applied, server side. An
     at-cost node earns none, which is what at-cost means. */
  if (!node.atCost) {
    const o = hrcmOwnComm(node.id, d.accruals);
    own.t = o.t; own.p = o.p; own.c = o.c;
  }
  const costRows = (node.atCost || node.lvl === 2)
    ? hrcmCostRows(node, d.prov, d.rates) : [];
  return {
    node, u: node.u, lvl: node.lvl, parent: parentU || "", cats, own, sotto, dep, wd, costRows,
    showCosts: !!node.atCost && node.lvl !== 0, profile: node.profile || null, kids, shops, _f: {},
  };
};
const hrcmFlatten = (row, out) => { const acc = out || []; acc.push(row); row.kids.concat(row.shops).forEach(r => hrcmFlatten(r, acc)); return acc; };

/* Per-tab display fields (memoized per row). Formulas per the reference "Table columns" +
   "KPIs / totals" bullets; np formulas cite the blade derivations verbatim. */
const hrcmFields = (r, tab) => {
  const cats = r.cats;
  if (tab === "all") {
    const profit = hrcmR2(hrcmProfitAll(cats));
    const ntc = hrcmR2(r.sotto.t);
    const npc = hrcmR2(hrcmNoNeg(r.sotto.p) + hrcmNoNeg(r.sotto.c.casino + r.sotto.c.casinolive + r.sotto.c.virtual));
    const ownC = r.own.c.casino + r.own.c.casinolive + r.own.c.virtual;
    const comm = hrcmR2(r.own.t + r.own.p + ownC);
    const costs = hrcmCostTotal(r.costRows);
    return {
      totBet: hrcmR2(cats.casino.bet + cats.casinolive.bet + cats.virtual.bet + cats.sport.betClosed),
      totWin: hrcmR2(cats.casino.win + cats.casinolive.win + cats.virtual.win + cats.sport.win),
      bonusBet: hrcmR2(cats.casino.bonusBet + cats.sport.bonusBet),
      jackpot: hrcmR2(cats.casino.jackpot),
      profit, ntc, npc, comm, costs, dep: r.dep, wd: r.wd,
      commT: hrcmR2(r.own.t), commG: hrcmR2(r.own.p + ownC),
      /* all.blade "Network profit" = profit - deposits + withdrawals - own_comm - costs -
         turnover_comm - profit_comm (documented formula, kept verbatim). */
      np: hrcmR2(profit - r.dep + r.wd - comm - costs - ntc - npc),
    };
  }
  if (tab === "sport") {
    const s = cats.sport;
    const profit = hrcmR2(s.betClosed - s.win);
    const ntc = hrcmR2(r.sotto.t);
    const npc = hrcmR2(hrcmNoNeg(r.sotto.p));
    const own = hrcmR2(r.own.t + r.own.p);
    const costs = hrcmCostTotal(r.costRows, "sport");
    return {
      bet: s.bet, betClosed: s.betClosed, win: s.win, bonusBet: s.bonusBet, profit,
      payin: s.betClosed ? profit / s.betClosed * 100 : 0,
      ntc, npc, own, costs, nr: s.nr, avg: s.nr ? s.bet / s.nr : 0,
      commT: hrcmR2(r.own.t), commG: hrcmR2(r.own.p),
      /* sport.blade "Network profit" = utile_rete - comm_proprie - sum_costs. */
      np: hrcmR2(profit - ntc - npc - own - costs),
    };
  }
  const c = cats[tab];
  const profit = hrcmR2(hrcmCatProfit(cats, tab));
  const own = hrcmR2(r.own.c[tab]);
  const sotto = hrcmR2(r.sotto.c[tab]);
  const costs = hrcmCostTotal(r.costRows, tab);
  return {
    bet: c.bet, win: c.win, bonusBet: c.bonusBet, jackpot: c.jackpot, profit, own, sotto, costs,
    /* thridParty np = totProfit - sottoalbero_commissions - comm_proprie; -costs included per
       divergence (5) in the header (the real card omits it while its color-class uses it). */
    np: hrcmR2(profit - sotto - own - costs),
  };
};
const hrcmF = (r, tab) => r._f[tab] || (r._f[tab] = hrcmFields(r, tab));

/* ---------------- turnover / week drill-down data (sport, profile cashiers) ---------------- */
const hrcmWeeksOf = (period) => {
  const parts = period.split("|");
  const out = [];
  let d = new Date(parts[0] + "T00:00:00");
  const end = new Date(parts[1] + "T00:00:00");
  while (d <= end) {
    const we = new Date(d); we.setDate(we.getDate() + 6);
    out.push({ start: hrcmIso(d), end: hrcmIso(we) });
    d = new Date(d); d.setDate(d.getDate() + 7);
  }
  return out;
};
/* One week of the sport drill-down. WAS the period's invented sport total
   divided by the week count and jittered by a PRNG, then multiplied by an
   invented rate — so the weeks summed to the period only by construction.

   Now the `report_user_daily` sport rows for that node's subtree, filtered to
   the week's dates, and the commission is the accrual rows whose period falls
   inside it. The weeks sum to the period because they are the same rows
   grouped differently, not because a divisor said so. */
const hrcmShopWeek = (row, wk, dayRows, accrualRows) => {
  const base = row.node && row.node.path ? String(row.node.path) : "";
  const inSubtree = (p) => !base || p === base || String(p).startsWith(base + ".");
  let bet = 0, win = 0;
  (dayRows || []).forEach(r => {
    if (r.vertical !== "sport" || r.funding === "bonus") return;
    if (!inSubtree(r.user_path)) return;
    if (r.day < wk.start || r.day > wk.end) return;
    bet += Number(r.stake) || 0;
    win += Number(r.payout) || 0;
  });
  let comm = 0;
  (accrualRows || []).forEach(a => {
    if (!a.user || !inSubtree(a.user.path === undefined ? "" : a.user.path)) {
      if (String(a.user_id) !== String(row.node.id)) return;
    }
    if (a.period_start < wk.start || a.period_start > wk.end) return;
    if (a.game_type !== "sport_turnover" && a.game_type !== "sport_profit") return;
    comm += Number(a.amount) || 0;
  });
  return {
    ...wk, bet: hrcmR2(bet), win: hrcmR2(win), profit: hrcmR2(bet - win),
    comm: hrcmR2(comm),
    finished: new Date(wk.end + "T23:59:59") < new Date(),
  };
};
/* Percentage ladder ("scaglioni" 1-30 events, scaglionisport() L2139-2174) — synthesized
   curve; the real steps come from the SportProfile ladder rows. */
const hrcmStepPct = (n) => Math.min(2 + (n - 1) * 0.5, 16);

/* ---------------- shared cell building blocks ---------------- */
const HrcmCell = ({ label, box, children }) => (
  <div className={`hrcm-cell${box ? ` hrcm-cell--box hrcm-cell--${box}` : ""}`}>
    <small>{label}</small>
    <span>{children}</span>
  </div>
);
/* Red Costs link (show_costs rows) -> statsCosts modal. Real top card says "Costs:", child
   rows leak Italian "Costi:" — English used per label policy. */
const HrcmCostLink = ({ row, tab, openModal }) => {
  const f = hrcmF(row, tab);
  if (!row.showCosts) return null;
  return (
    <button className="hrcm-costlink" title={`Costs details — ${row.u}`} onClick={() => openModal({ kind: "costs", row, tab })}>
      Costs: {hrsMoney(f.costs)}
    </button>
  );
};
const HrcmCells = ({ row, tab, openModal }) => {
  const f = hrcmF(row, tab);
  const pn = (v) => (v >= 0 ? "pos" : "neg");
  if (tab === "all") return (
    <>
      <HrcmCell label="Total bet">{hrsMoney(f.totBet)}</HrcmCell>
      <HrcmCell label="Total win">{hrsMoney(f.totWin)}</HrcmCell>
      <HrcmCell label="Bonus bet">{hrsMoney(f.bonusBet)}</HrcmCell>
      <HrcmCell label="Jackpot">{hrsMoney(f.jackpot)}</HrcmCell>
      <HrcmCell label="Profit" box={pn(f.profit)}>{hrsMoney(f.profit)}</HrcmCell>
      <HrcmCell label="Network tournover commissions">{hrsMoney(f.ntc)}</HrcmCell>
      <HrcmCell label="Network profit commissions">{hrsMoney(f.npc)}</HrcmCell>
      <HrcmCell label="Commissions">{hrsMoney(f.comm)}<HrcmCostLink row={row} tab={tab} openModal={openModal} /></HrcmCell>
      <HrcmCell label="Deposits">{hrsMoney(f.dep)}</HrcmCell>
      <HrcmCell label="Withdrawals">{hrsMoney(f.wd)}</HrcmCell>
      <HrcmCell label="Network profit" box={pn(f.np)}>{hrsMoney(f.np)}</HrcmCell>
    </>
  );
  if (tab === "sport") return (
    <>
      <HrcmCell label="Bet (issued)">{hrsMoney(f.bet)}</HrcmCell>
      <HrcmCell label="Bet (closed)">{hrsMoney(f.betClosed)}</HrcmCell>
      <HrcmCell label="Total win">{hrsMoney(f.win)}</HrcmCell>
      <HrcmCell label="Bonus bet">{hrsMoney(f.bonusBet)}</HrcmCell>
      <HrcmCell label="Profit" box={pn(f.profit)}>{hrsMoney(f.profit)}</HrcmCell>
      <HrcmCell label="Pay in (%)">{hrsPct(f.payin)}</HrcmCell>
      <HrcmCell label="Network tournover commissions">{hrsMoney(f.ntc)}</HrcmCell>
      <HrcmCell label="Network profit commissions">{hrsMoney(f.npc)}</HrcmCell>
      <HrcmCell label="Own commissions">{hrsMoney(f.own)}<HrcmCostLink row={row} tab={tab} openModal={openModal} /></HrcmCell>
      <HrcmCell label="Network profit" box={pn(f.np)}>{hrsMoney(f.np)}</HrcmCell>
    </>
  );
  return (
    <>
      <HrcmCell label="Total bet">{hrsMoney(f.bet)}</HrcmCell>
      <HrcmCell label="Total win">{hrsMoney(f.win)}</HrcmCell>
      {tab === "casino" && <HrcmCell label="Bonus bet">{hrsMoney(f.bonusBet)}</HrcmCell>}
      {/* Jackpot: plain amount — real blade appends a spurious "%" (divergence 2). */}
      {tab === "casino" && <HrcmCell label="Jackpot">{hrsMoney(f.jackpot)}</HrcmCell>}
      <HrcmCell label="Profit" box={pn(f.profit)}>{hrsMoney(f.profit)}</HrcmCell>
      <HrcmCell label="Network commissions">{hrsMoney(f.own)}</HrcmCell>
      <HrcmCell label="Network profit commissions">{hrsMoney(f.sotto)}<HrcmCostLink row={row} tab={tab} openModal={openModal} /></HrcmCell>
      <HrcmCell label="Network profit" box={pn(f.np)}>{hrsMoney(f.np)}</HrcmCell>
    </>
  );
};

/* ---------------- summary card + period strip ---------------- */
const HrcmPeriodBar = ({ period, tab, row }) => {
  const parts = period.split("|");
  const f = tab === "sport" ? hrcmF(row, "sport") : null;
  return (
    <div className="hrcm-periodbar">
      Commissions from <b>{hrcmDMY(parts[0])}</b> To <b>{hrcmDMY(parts[1])}</b>
      {f && <> · Closed bets: <b>{hrsInt(f.nr)}</b> · Average bet: <b>{hrsMoney(f.avg)}</b></>}
    </div>
  );
};
/* Hidden from affiliates on the real page (@if(!isAffiliate())) — demo session is the super
   admin, so it always renders here. */
const HrcmSummary = ({ row, tab, openModal }) => (
  <div className="hrcm-summary">
    <div className="hrcm-subname hrcm-subname--main">
      <Icon name="user" size={15} />
      <span><small>{HRCM_LEVELS[row.lvl]}</small>{row.u}</span>
    </div>
    <HrcmCells row={row} tab={tab} openModal={openModal} />
    <div className="hrcm-subacts">
      <button className="hrcm-ibtn" title={`Commission profile — ${row.u}`} onClick={() => openModal({ kind: "profile", row, tab })}>
        <Icon name="info" size={14} />
      </button>
    </div>
  </div>
);

/* ---------------- cashier ("S" badge) table ---------------- */
const hrcmShopColumns = (tab, openModal) => {
  const userCol = { key: "u", label: "Username", render: (r) => <span className="hrcm-ucell"><span className="hrcm-sbadge">S</span><b>{r.u}</b></span> };
  const infoCol = {
    key: "_info", label: "Info", align: "center",
    render: (r) => <button className="hrcm-ibtn" title={`Commission profile — ${r.u}`} onClick={() => openModal({ kind: "profile", row: r, tab })}><Icon name="info" size={13} /></button>,
  };
  const money = (key, label, get, box) => ({
    key, label, align: "right",
    render: (r) => hrsMoney(get(hrcmF(r, tab))),
    cellClass: box ? (r) => (get(hrcmF(r, tab)) >= 0 ? "hrs-pos" : "hrs-neg") : undefined,
  });
  if (tab === "all") return [
    userCol,
    money("bet", "Bet", f => f.totBet),
    money("win", "Win", f => f.totWin),
    money("bonusBet", "Bonus bet", f => f.bonusBet),
    money("jackpot", "Jackpot", f => f.jackpot),
    money("profit", "Profit", f => f.profit, true),
    money("commT", "Commissions on Turnover", f => f.commT),
    {
      key: "commG", label: "Commissions on GGR", align: "right",
      /* Cashier Costs link shows the SHOP's own figure — the real cell prints the main user's
         $sum_costs (divergence 4). */
      render: (r) => <span className="hrcm-commwrap">{hrsMoney(hrcmF(r, tab).commG)}<HrcmCostLink row={r} tab={tab} openModal={openModal} /></span>,
    },
    money("dep", "Deposits", f => f.dep),
    money("wd", "Withdrawals", f => f.wd),
    money("np", "Network Profit", f => f.np, true),
    infoCol,
  ];
  if (tab === "sport") return [
    userCol,
    money("bet", "Bet (issued)", f => f.bet),
    money("betClosed", "Bet (closed)", f => f.betClosed),
    money("win", "Total win", f => f.win),
    money("bonusBet", "Bonus bet", f => f.bonusBet),
    /* Red/green Profit box kept — the real shop rows lose it to a duplicate class attr
       (divergence 3). */
    money("profit", "Profit", f => f.profit, true),
    { key: "nr", label: "Number bets", align: "right", render: (r) => hrsInt(hrcmF(r, tab).nr) },
    money("avg", "Average bet", f => f.avg),
    {
      key: "commT", label: "Commissions on Turnover", align: "right",
      /* Value + turnover drill-down icon; both replaced by "-" on show_costs rows (real rule). */
      render: (r) => r.showCosts ? "-" : (
        <span className="hrcm-commwrap">
          {hrsMoney(hrcmF(r, tab).commT)}
          <button className="hrcm-ibtn" title={`Turnover details — ${r.u}`} onClick={() => openModal({ kind: "turnover", row: r })}><Icon name="info" size={12} /></button>
        </span>
      ),
    },
    { key: "commG", label: "Commissions on GGR", align: "right", render: (r) => r.showCosts ? "-" : hrsMoney(hrcmF(r, tab).commG) },
    { key: "profile", label: "Profile", render: (r) => r.profile ? r.profile.name : "Not set" },
    infoCol,
  ];
  const cols = [
    userCol,
    money("bet", "Bet", f => f.bet),
    money("win", "Win", f => f.win),
  ];
  if (tab === "casino") {
    cols.push(money("bonusBet", "Bonus bet", f => f.bonusBet));
    cols.push(money("jackpot", "Jackpot", f => f.jackpot));
  }
  cols.push(money("profit", "Profit", f => f.profit, true));
  /* Real header says "Network profit commissions" but the cell prints comm_proprie (the
     cashier's OWN commissions) — label kept, value kept, mismatch documented here. */
  cols.push(money("sotto", "Network profit commissions", f => f.own));
  cols.push(money("np", "Network Profit", f => f.np, true));
  cols.push(infoCol);
  return cols;
};
const hrcmShopCard = (tab, openModal) => (r) => {
  const f = hrcmF(r, tab);
  const bet = tab === "all" ? f.totBet : f.bet;
  return (
    <>
      <div className="hrs-card__top"><b><span className="hrcm-sbadge">S</span> {r.u}</b><span className={f.profit >= 0 ? "hrs-pos" : "hrs-neg"}>{hrsMoney(f.profit)}</span></div>
      <div className="hrs-card__grid">
        <span>Bet</span><b>{hrsMoney(bet)}</b>
        <span>Win</span><b>{hrsMoney(tab === "all" ? f.totWin : f.win)}</b>
        {tab === "sport" && <><span>Number bets</span><b>{hrsInt(f.nr)}</b></>}
        {tab !== "sport" && f.np != null && <><span>Network Profit</span><b>{hrsMoney(f.np)}</b></>}
      </div>
      <div className="hrcm-cardacts">
        <button className="hrcm-ibtn" title="Commission profile" onClick={() => openModal({ kind: "profile", row: r, tab })}><Icon name="info" size={13} /></button>
        {tab === "sport" && !r.showCosts && <button className="hrcm-ibtn" title="Turnover details" onClick={() => openModal({ kind: "turnover", row: r })}><Icon name="chart" size={13} /></button>}
        <HrcmCostLink row={r} tab={tab} openModal={openModal} />
      </div>
    </>
  );
};
const HrcmShopTable = ({ rows, tab, openModal, title }) => (
  <>
    {title && <div className="hrcm-subhead">{title}</div>}
    <HrsTable
      dense
      columns={hrcmShopColumns(tab, openModal)}
      rows={rows}
      rowKey="u"
      renderCard={hrcmShopCard(tab, openModal)}
      empty="No cashier (Shop-level) accounts directly under this user."
    />
  </>
);

/* ---------------- subnet drill-down (HrsTable rowDetail content) ----------------
   Mirrors elenco_utenti_sottostanti(): same partial one level down with the summary card
   suppressed — child card-rows + the child's cashier table; nested expanders recurse. */
const HrcmSubnet = ({ row, tab, openModal }) => {
  const [open, setOpen] = hrcmUseState({});
  return (
    <div className="hrcm-subnet">
      {row.kids.length > 0 && (
        <div className="hrcm-subrows">
          {row.kids.map(k => (
            <React.Fragment key={k.u}>
              <div className="hrcm-subrow">
                <div className="hrcm-subname">
                  <Icon name="user" size={13} />
                  <span><small>{HRCM_LEVELS[k.lvl]}</small>{k.u}</span>
                </div>
                <HrcmCells row={k} tab={tab} openModal={openModal} />
                <div className="hrcm-subacts">
                  <button className="hrcm-ibtn" title={`Commission profile — ${k.u}`} onClick={() => openModal({ kind: "profile", row: k, tab })}>
                    <Icon name="info" size={13} />
                  </button>
                  {(k.kids.length > 0 || k.shops.length > 0) && (
                    <button className="hrcm-ibtn" title={open[k.u] ? "Collapse subnet" : "Expand subnet"} onClick={() => setOpen(o => ({ ...o, [k.u]: !o[k.u] }))}>
                      <Icon name={open[k.u] ? "chevron_down" : "plus"} size={13} />
                    </button>
                  )}
                </div>
              </div>
              {open[k.u] && <div className="hrcm-subnest"><HrcmSubnet row={k} tab={tab} openModal={openModal} /></div>}
            </React.Fragment>
          ))}
        </div>
      )}
      {row.shops.length > 0 && <HrcmShopTable rows={row.shops} tab={tab} openModal={openModal} title={`Cashiers of ${row.u}`} />}
      {row.kids.length === 0 && row.shops.length === 0 && (
        <div className="hrcm-subempty">
          No sub-accounts under this user for the selected period. (The real page only renders a
          "+" expander when getCountUsers() &gt; 0 — the shared table shows the chevron on every row.)
        </div>
      )}
    </div>
  );
};

/* ---------------- network table (one row per non-cashier child) ---------------- */
const hrcmNetColumns = (tab, openModal) => {
  const userCol = {
    key: "u", label: "Username",
    render: (r) => <span className="hrcm-ucell"><Icon name="user" size={12} /><b>{r.u}</b><small>{HRCM_LEVELS[r.lvl]}</small></span>,
  };
  const infoCol = {
    key: "_info", label: "Info", align: "center",
    render: (r) => <button className="hrcm-ibtn" title={`Commission profile — ${r.u}`} onClick={() => openModal({ kind: "profile", row: r, tab })}><Icon name="info" size={14} /></button>,
  };
  const money = (key, label, get, box) => ({
    key, label, align: "right",
    render: (r) => hrsMoney(get(hrcmF(r, tab))),
    cellClass: box ? (r) => (get(hrcmF(r, tab)) >= 0 ? "hrs-pos" : "hrs-neg") : undefined,
  });
  if (tab === "all") return [
    userCol,
    money("totBet", "Total bet", f => f.totBet),
    money("totWin", "Total win", f => f.totWin),
    money("bonusBet", "Bonus bet", f => f.bonusBet),
    money("jackpot", "Jackpot", f => f.jackpot),
    money("profit", "Profit", f => f.profit, true),
    money("ntc", "Network tournover commissions", f => f.ntc),
    money("npc", "Network profit commissions", f => f.npc),
    {
      key: "comm", label: "Commissions", align: "right",
      render: (r) => <span className="hrcm-commwrap">{hrsMoney(hrcmF(r, tab).comm)}<HrcmCostLink row={r} tab={tab} openModal={openModal} /></span>,
    },
    money("dep", "Deposits", f => f.dep),
    money("wd", "Withdrawals", f => f.wd),
    money("np", "Network profit", f => f.np, true),
    infoCol,
  ];
  if (tab === "sport") return [
    userCol,
    money("bet", "Bet (issued)", f => f.bet),
    money("betClosed", "Bet (closed)", f => f.betClosed),
    money("win", "Total win", f => f.win),
    money("bonusBet", "Bonus bet", f => f.bonusBet),
    money("profit", "Profit", f => f.profit, true),
    { key: "payin", label: "Pay in (%)", align: "right", render: (r) => hrsPct(hrcmF(r, tab).payin) },
    money("ntc", "Network tournover commissions", f => f.ntc),
    money("npc", "Network profit commissions", f => f.npc),
    {
      key: "own", label: "Own commissions", align: "right",
      render: (r) => <span className="hrcm-commwrap">{hrsMoney(hrcmF(r, tab).own)}<HrcmCostLink row={r} tab={tab} openModal={openModal} /></span>,
    },
    money("np", "Network profit", f => f.np, true),
    infoCol,
  ];
  const cols = [
    userCol,
    money("bet", "Total bet", f => f.bet),
    money("win", "Total win", f => f.win),
  ];
  if (tab === "casino") {
    cols.push(money("bonusBet", "Bonus bet", f => f.bonusBet));
    cols.push(money("jackpot", "Jackpot", f => f.jackpot));
  }
  cols.push(money("profit", "Profit", f => f.profit, true));
  cols.push(money("own", "Network commissions", f => f.own));
  cols.push({
    key: "sotto", label: "Network profit commissions", align: "right",
    render: (r) => <span className="hrcm-commwrap">{hrsMoney(hrcmF(r, tab).sotto)}<HrcmCostLink row={r} tab={tab} openModal={openModal} /></span>,
  });
  cols.push(money("np", "Network profit", f => f.np, true));
  cols.push(infoCol);
  return cols;
};
const hrcmNetCard = (tab, openModal) => (r) => {
  const f = hrcmF(r, tab);
  const bet = tab === "all" ? f.totBet : f.bet;
  const win = tab === "all" ? f.totWin : f.win;
  const comm = tab === "all" ? f.comm : tab === "sport" ? f.own : f.sotto;
  const commLabel = tab === "all" ? "Commissions" : tab === "sport" ? "Own commissions" : "Network profit commissions";
  return (
    <>
      <div className="hrs-card__top"><b>{r.u}</b><span className={f.np >= 0 ? "hrs-pos" : "hrs-neg"}>{hrsMoney(f.np)}</span></div>
      <div className="hrs-card__grid">
        <span>{tab === "sport" ? "Bet (closed)" : "Total bet"}</span><b>{hrsMoney(tab === "sport" ? f.betClosed : bet)}</b>
        <span>Total win</span><b>{hrsMoney(win)}</b>
        <span>Profit</span><b>{hrsMoney(f.profit)}</b>
        <span>{commLabel}</span><b>{hrsMoney(comm)}</b>
        {tab === "all" && <><span>Deposits</span><b>{hrsMoney(f.dep)}</b><span>Withdrawals</span><b>{hrsMoney(f.wd)}</b></>}
      </div>
      <div className="hrcm-cardacts">
        <button className="hrcm-ibtn" title="Commission profile" onClick={() => openModal({ kind: "profile", row: r, tab })}><Icon name="info" size={13} /></button>
        <HrcmCostLink row={r} tab={tab} openModal={openModal} />
      </div>
    </>
  );
};

/* ---------------- modals ---------------- */
const HrcmModal = ({ title, onClose, wide, children }) => (
  <div className="bp-modal-scrim hrcm-scrim" onClick={onClose}>
    <div className={`bp-modal hrcm-modal${wide ? " hrcm-modal--wide" : ""}`} onClick={e => e.stopPropagation()}>
      <div className="hrcm-modal__head">
        <div className="hrcm-modal__title">{title}</div>
        <button className="hrs-x" title="Close" onClick={onClose}><Icon name="x" size={14} /></button>
      </div>
      <div className="hrcm-modal__body">{children}</div>
    </div>
  </div>
);

/* infoCommissionProfile -> #commissionProfileModal, real title ":username :tab profile".
   ADMIN level / profile -1/-2 ("a costo") -> provider %-cost list from users_providers; else
   percentage breakdown via showPercType(); no profile -> "Not set" (real: "NON IMPOSTATO"). */
const HrcmProfileModal = ({ row, tab, onClose, data }) => {
  const tabLabel = (HRCM_TABS.find(t => t.value === tab) || HRCM_TABS[0]).label;
  const atCost = row.lvl === 2 || (row.node.atCost && !row.profile);
  return (
    <HrcmModal title={`${row.u} — ${tabLabel} profile`} onClose={onClose}>
      <div className="hrcm-mnote">
        Real endpoint GET /reports/commissions/infoCommissionProfile has <b>no permission or
        hierarchy check</b> — any BO session can request any user_id (surfaced, not hidden).
      </div>
      {atCost ? (
        <>
          <div className="hrcm-mnote">At-cost account — settles by paying the platform a per-provider percentage of GGR (% cost column header is "% Costo" on the real modal).</div>
          <div className="hrcm-mtable-wrap">
            <table className="hrs-table">
              <thead><tr><th>Provider</th><th className="hrs-al-r">% cost</th></tr></thead>
              <tbody>
                {/* WAS seven invented provider names with a percentage seeded on
                    (user | provider). This table tells an operator what share of
                    GGR they are billed; inventing it is inventing a rate card.
                    `user_providers` for this account, and nothing if none is
                    configured — an empty rate card is a true statement. */}
                {((data && data.rates) || [])
                  .filter(r => String(r.user_id) === String(row.node.id))
                  .map(r => (
                    <tr key={r.provider_id}>
                      <td>{(r.provider && r.provider.name) || `Provider ${r.provider_id}`}</td>
                      <td className="hrs-al-r">{r.percentage == null ? "—" : hrsPct(Number(r.percentage))}</td>
                    </tr>
                  ))}
                {!((data && data.rates) || []).some(r => String(r.user_id) === String(row.node.id)) && (
                  <tr><td colSpan={2} style={{ color: "var(--text-tertiary)" }}>
                    No per-provider cost rate is configured for this account.
                  </td></tr>
                )}
              </tbody>
            </table>
          </div>
        </>
      ) : row.profile ? (
        <>
          <div className="hrcm-mnote">Commission profile: <b>{row.profile.name}</b> — tipologia {row.profile.tipologia} (misto a scaletta: per-event-count turnover ladder). Range presentation inferred from showPercType().</div>
          <div className="hrcm-mtable-wrap">
            <table className="hrs-table">
              <thead><tr><th>Category</th><th>Basis</th><th className="hrs-al-r">Percentage</th></tr></thead>
              <tbody>
                <tr><td>Sport</td><td>Turnover ladder — 1st range</td><td className="hrs-al-r">{hrsPct(row.profile.r1)}</td></tr>
                <tr><td>Sport</td><td>Turnover ladder — 2nd range</td><td className="hrs-al-r">{hrsPct(row.profile.r2)}</td></tr>
                <tr><td>Sport</td><td>GGR share</td><td className="hrs-al-r">{hrsPct(row.node.rates ? row.node.rates.p : 0)}</td></tr>
                <tr><td>Casino / Casino live / Virtual</td><td>GGR share</td><td className="hrs-al-r">{hrsPct(row.node.rates ? row.node.rates.c : 0)}</td></tr>
              </tbody>
            </table>
          </div>
        </>
      ) : (
        <div className="hrcm-mnote">Not set <i>(real modal prints hardcoded Italian "NON IMPOSTATO")</i>.</div>
      )}
    </HrcmModal>
  );
};

/* statsCosts -> #costsModal "Costs details" (title hardcoded English on the real platform). */
const HrcmCostsModal = ({ row, tab, onClose }) => {
  const rows = row.costRows.filter(r => tab === "all" || r.cat === tab);
  const tot = (k) => hrcmR2(rows.reduce((s, r) => s + r[k], 0));
  return (
    <HrcmModal title="Costs details" onClose={onClose} wide>
      <div className="hrcm-mnote">
        {row.u} · cost source: business_report x provider percentages (GREATEST(GGR, 0) x %/100).
        Real endpoint GET /reports/commissions/statsCosts accepts <b>any user_id</b> with no gate.
      </div>
      <div className="hrcm-mtable-wrap">
        <table className="hrs-table">
          <thead>
            <tr><th>Provider</th><th className="hrs-al-r">Bet</th><th className="hrs-al-r">Win</th><th className="hrs-al-r">Profit</th><th className="hrs-al-r">% cost</th><th className="hrs-al-r">Costs</th></tr>
          </thead>
          <tbody>
            {rows.length === 0 && <tr><td colSpan={6} className="hrs-empty">No provider activity in this period.</td></tr>}
            {rows.map(r => (
              <tr key={r.name}>
                <td>{r.name}</td>
                <td className="hrs-al-r">{hrsMoney(r.bet)}</td>
                <td className="hrs-al-r">{hrsMoney(r.win)}</td>
                <td className={`hrs-al-r ${r.profit >= 0 ? "hrs-pos" : "hrs-neg"}`}>{hrsMoney(r.profit)}</td>
                <td className="hrs-al-r">{hrsPct(r.pct)}</td>
                <td className="hrs-al-r">{hrsMoney(r.cost)}</td>
              </tr>
            ))}
          </tbody>
          {rows.length > 0 && (
            <tfoot>
              <tr className="hrs-totals hrs-totals--dark">
                <td>Totals</td>
                <td className="hrs-al-r">{hrsMoney(tot("bet"))}</td>
                <td className="hrs-al-r">{hrsMoney(tot("win"))}</td>
                <td className="hrs-al-r">{hrsMoney(tot("profit"))}</td>
                <td />
                <td className="hrs-al-r">{hrsMoney(tot("cost"))}</td>
              </tr>
            </tfoot>
          )}
        </table>
      </div>
      <div className="hrcm-mfoot">Last update: 07/08/2026 05:12 <Tip size={12}>From setting <code>last_br_update</code> — written by the business_report aggregation cron; mocked as a fixed timestamp here.</Tip></div>
    </HrcmModal>
  );
};

/* statsTurnover -> #turnoverinfosportModal. Real title is hardcoded Italian "Dettaglio
   Turnover" — "Turnover details" used per label policy. */
const HrcmTurnoverModal = ({ row, period, onClose, onWeek, data }) => {
  const [mtab, setMtab] = hrcmUseState("weeks");
  const wkDefs = hrcmWeeksOf(period);
  const weeks = wkDefs.map(w => hrcmShopWeek(row, w, (data && data.days) || [], (data && data.accruals) || []));
  const rates = { p: 0 };   // the ladder lives in commission_profile_tiers; the accrual already applies it
  return (
    <HrcmModal title={`Turnover details — ${row.u}`} onClose={onClose} wide>
      <div className="hrcm-mtabs">
        <button className={`hrcm-mtab${mtab === "weeks" ? " hrcm-mtab--on" : ""}`} onClick={() => setMtab("weeks")}>Weeks</button>
        <button className={`hrcm-mtab${mtab === "steps" ? " hrcm-mtab--on" : ""}`} onClick={() => setMtab("steps")}>Percentage steps</button>
      </div>
      {mtab === "weeks" ? (
        <div className="hrcm-mtable-wrap">
          <table className="hrs-table">
            <thead>
              <tr><th>Week</th><th className="hrs-al-r">Bet</th><th className="hrs-al-r">Win</th><th className="hrs-al-r">Profit</th><th className="hrs-al-r">Commissions</th><th className="hrs-al-c">Details</th></tr>
            </thead>
            <tbody>
              {weeks.map(w => (
                <tr key={w.start}>
                  <td>{hrcmDMY(w.start)} – {hrcmDMY(w.end)}</td>
                  <td className="hrs-al-r">{hrsMoney(w.bet)}</td>
                  <td className="hrs-al-r">{hrsMoney(w.win)}</td>
                  <td className={`hrs-al-r ${w.profit >= 0 ? "hrs-pos" : "hrs-neg"}`}>{hrsMoney(w.profit)}</td>
                  <td className="hrs-al-r">{hrsMoney(w.comm)}</td>
                  <td className="hrs-al-c">
                    {/* "+" only on finished weeks, like the real modal. */}
                    {w.finished
                      ? <button className="hrcm-ibtn" title="Bets by event count" onClick={() => onWeek(w)}><Icon name="plus" size={12} /></button>
                      : <span className="hrcm-subempty">running</span>}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      ) : (
        <>
          <div className="hrcm-mnote">Turnover ladder per number of events (scaglioni 1–30, scaglionisport()) — synthesized curve; GGR share on top: <b>{hrsPct(rates.p)}</b>.</div>
          <div className="hrcm-steps">
            {Array.from({ length: 30 }, (_, i) => i + 1).map(n => (
              <div key={n} className="hrcm-step"><span>{n} ev.</span><b>{hrsPct(hrcmStepPct(n))}</b></div>
            ))}
          </div>
        </>
      )}
    </HrcmModal>
  );
};

/* statsWeekSport -> #dettagliosettimanaSportModal. Real title hardcoded Italian "DETTAGLIO
   GIOCATE PER EVENTI DI ..." — English per label policy. Coupons grouped by count_items. */
const HRCM_WEEK_W = [0.34, 0.22, 0.15, 0.11, 0.08, 0.05, 0.03, 0.02];
const HrcmWeekModal = ({ row, week, onClose }) => (
  <HrcmModal title={`Bets by event count — ${hrcmDMY(week.start)} – ${hrcmDMY(week.end)} (${row.u})`} onClose={onClose}>
    <div className="hrcm-mtable-wrap">
      <table className="hrs-table">
        <thead>
          <tr><th className="hrs-al-r">Number of events</th><th className="hrs-al-r">Bet</th><th className="hrs-al-r">Percentage</th><th className="hrs-al-r">Commissions</th></tr>
        </thead>
        <tbody>
          {HRCM_WEEK_W.map((w, i) => {
            const bet = hrcmR2(week.bet * w);
            const pct = hrcmStepPct(i + 1);
            return (
              <tr key={i}>
                <td className="hrs-al-r">{i + 1}</td>
                <td className="hrs-al-r">{hrsMoney(bet)}</td>
                <td className="hrs-al-r">{hrsPct(pct)}</td>
                <td className="hrs-al-r">{hrsMoney(hrcmR2(bet * pct / 100))}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
    <div className="hrcm-mfoot">Coupons read from MySQL or Mongo per env('CASINO_DB') on the real platform.</div>
  </HrcmModal>
);

/* ---------------- export (from data — divergence 1) ---------------- */
const hrcmExportHeaders = (tab) => {
  const m = (key, label) => ({ key, label, get: (r) => Number(r[key] || 0).toFixed(2) });
  const base = [
    { key: "type", label: "User Type" },
    { key: "u", label: "Username" },
    { key: "parent", label: "Parent Username" },
  ];
  if (tab === "all") return base.concat([
    m("totBet", "Total Bet"), m("totWin", "Total Win"), m("bonusBet", "Bonus bet"),
    /* Real col G (Jackpot) is commented out server-side and exports blank — filled here. */
    m("jackpot", "Jackpot"), m("profit", "Total profit"),
    m("ntc", "Network tournover commissions"), m("npc", "Network profit commissions"),
    m("comm", "Commissions"), m("costs", "Costs"), m("dep", "Deposits"), m("wd", "Withdrawals"),
    m("np", "Network profit"),
  ]);
  if (tab === "sport") return base.concat([
    m("bet", "Bet (issued)"), m("betClosed", "Bet (closed)"), m("win", "Total win"),
    m("bonusBet", "Bonus bet"), m("profit", "Profit"), m("payin", "Pay in (%)"),
    { key: "nr", label: "Number bets", get: (r) => r.nr || 0 }, m("avg", "Average bet"),
    m("commT", "Commissions on Turnover"), m("commG", "Commissions on GGR"), m("np", "Network profit"),
  ]);
  const cols = base.concat([m("bet", "Bet"), m("win", "Win")]);
  if (tab === "casino") { cols.push(m("bonusBet", "Bonus bet")); cols.push(m("jackpot", "Jackpot")); }
  return cols.concat([m("profit", "Profit"), m("own", "Network commissions"), m("sotto", "Network profit commissions"), m("np", "Network profit")]);
};
const hrcmExportRows = (sel, tab) => hrcmFlatten(sel).map(r => ({
    // EMBED-OK: `r` is a mapped row — `parent` is the parent USERNAME the mapper produced.
  type: HRCM_LEVELS[r.lvl], u: r.u, parent: r.parent, ...hrcmF(r, tab),
}));

/* ---------------- page ---------------- */
const CommissionsReport = () => {
  const [draft, setDraft] = hrcmUseState({ tab: "sport", user: "admin", ptype: "m", pm: HRCM_PM0, pw: HRCM_PW0 });
  const [applied, setApplied] = hrcmUseState(null); // null = not searched yet (real no-auto-load)

  /* Everything the report needs, fetched once for the period. Five sources
     because five different questions: who is in the network, what they traded,
     what they accrued, what their players paid in and took out, and what the
     providers cost. Every row on screen is a filter over these — so a parent's
     figure and the sum of its children come from the same rows and cannot
     drift apart the way independently generated numbers did. */
  const feed = useHrsFetch(() => {
    if (!applied) return Promise.resolve({ ok: true, meta: {}, source: "live",
      data: { users: [], days: [], accruals: [], players: [], prov: [], rates: [], closed: {} } });
    const [from, to] = String(applied.period).split("|");
    const range = { from, to };
    return Promise.all([
      window.sb.list("networkUsers", { limit: 500 }),
      window.sb.list("reportUserDaily", { limit: 5000, filters: range }),
      window.sb.list("commissionAccruals", { limit: 5000, filters: range }),
      window.sb.list("reportPlayers", { limit: 5000, filters: range }),
      window.sb.list("reportUserProviderDaily", { limit: 5000, filters: range }),
      window.sb.list("userProviders", { limit: 2000 }),
      window.sb.list("reportBetType", { limit: 5000, filters: range }),
    ]).then(([users, days, accruals, players, prov, rates, bt]) => {
      const bad = [users, days, accruals, players, prov, rates, bt].find(r => !r.ok);
      if (bad) return bad;
      /* Settled sport stake per user: total stake minus what is still open.
         Sport commission is owed on SETTLED turnover, so counting open tickets
         would bill an agent for bets that have not resolved. */
      const closed = {};
      (bt.data || []).forEach(r => {
        const k = String(r.user_id);
        closed[k] = (closed[k] || 0) + ((Number(r.stake) || 0) - (Number(r.open_stake) || 0));
      });
      return { ok: true, meta: {}, source: "live",
               data: { users: users.data, days: days.data, accruals: accruals.data,
                       players: players.data, prov: prov.data, rates: rates.data, closed } };
    });
  }, [applied ? applied.period : ""]);

  const data = feed.data || { users: [], days: [], accruals: [], players: [], prov: [], rates: [], closed: {} };

  /* WAS `HRCM_USER_OPTS`, walked from the literal tree. Level below SHOP(20),
     matching UsersController::getUsers(min_level = SHOP_LEVEL) — cashiers are
     excluded from the picker upstream and are excluded here. */
  const HRCM_USER_OPTS = (data.users || [])
    .filter(u => Number(u.user_level) < HRCM_SHOP_LEVEL)
    .map(u => ({ value: String(u.id), label: `${u.username} (${HRCM_LEVELS[Number(u.user_level)] || "Level " + u.user_level})` }));

  const [modal, setModal] = hrcmUseState(null);

  const fields = [
    {
      key: "tab", label: "Category", type: "select", icon: "list", options: HRCM_TABS, defaultValue: "sport",
      tip: <>Options are skin-gated (<code>show_sport</code>/<code>show_casino</code>/<code>show_casinolive</code>/<code>show_virtual</code>, bypassed for admin — the demo session). "ALL" is listed first but <b>Sport</b> carries the selected attr; Poker is hardcoded off and unreachable.</>,
    },
    {
      key: "user", label: "User", type: "select", icon: "user", options: HRCM_USER_OPTS, defaultValue: "admin",
      tip: <>Self + all <code>user_path</code> descendants with level below Shop(20), excluding Administration/Customer care/Affiliate accounts, limited to the caller's skins; disabled for affiliates on the real page.</>,
    },
    {
      key: "ptype", label: "Period type", type: "select", icon: "calendar", defaultValue: "m",
      options: [{ value: "m", label: "Monthly" }, { value: "w", label: "Weekly" }],
      tip: <>Labels inferred — <code>backend.period_type</code> / <code>backend.monthly</code> / <code>backend.weekly</code> resolve in no committed lang file.</>,
    },
    {
      key: "pm", label: "Period", type: "month-period", mode: "commission", fromYear: 2020, icon: "calendar",
      defaultValue: HRCM_PM0, hidden: draft.ptype === "w",
      tip: <>Commission months (getDateCalendario): first Monday of the month → day before the next month's first Monday. Default = the period containing today.</>,
    },
    {
      key: "pw", label: "Period", type: "month-period", mode: "week", fromYear: 2020, icon: "calendar",
      defaultValue: HRCM_PW0, hidden: draft.ptype !== "w",
      tip: <>Mon–Sun weeks since 2020-01-06 (getWeekDatesRanges). The real select defaults to the <b>earliest 2020 week</b>; current week used here as evident intent (header divergence 6).</>,
    },
  ];
  const resetDraft = () => { setDraft({ tab: "sport", user: "admin", ptype: "m", pm: HRCM_PM0, pw: HRCM_PW0 }); setApplied(null); setModal(null); };
  /* Real submit sends only tab, user_id, is_esporta, periodo_mese — is_esporta is never read
     by the controller; the weekly select is name-swapped into periodo_mese by commissions.js. */
  const doSearch = (v) => { setApplied({ tab: v.tab || "sport", user: v.user || "admin", period: v.ptype === "w" ? v.pw : v.pm }); setModal(null); };

  const netTree = hrcmUseMemo(() => hrcmBuildTree(data.users), [data.users]);
  const tree = hrcmUseMemo(
    () => (applied && netTree ? hrcmBuildRow(netTree, applied.period, "", data) : null),
    [applied ? applied.period : "", netTree, data]);
  const sel = applied && tree ? (hrcmFlatten(tree).find(r => String(r.node.id) === String(applied.user) || r.u === applied.user) || tree) : null;
  const tab = applied ? applied.tab : draft.tab;
  const netRows = sel ? sel.kids : [];
  const exportRows = sel ? hrcmExportRows(sel, tab) : [];
  const exportName = `${tab}_commissions_report.csv`;

  return (
    <HrsShell
      title="Commissions"
      subtitle="Report ▾ · commission settlement by network level over first-Monday commission months"
      gate={["support_report", "support_report_commissions"]}
      gateNote={<> Gates bind Customer Care only (checkUserBoPerm returns true for every other allowed level); the controller re-checks via <code>$this-&gt;authorize('asdasdas')</code> → 403. The four modal data endpoints and the <code>Route::any</code> excel endpoints have <b>no server-side gate at all</b>; only the UI Export button is hidden without <code>support_export</code>.</>}
      explainer={{
        bullets: [
          <>One card row per non-cashier user in the selected subtree, plus a cashier ("S") table — volumes come from <code>business_report</code> × the skin's <code>reports_multiplier</code>, commissions from pre-computed <code>commissions_monthly_report</code> rows.</>,
          <>Commission figures only populate when the chosen period exactly matches a stored calc period (<code>period = "start,end"</code>); volumes populate for any range. "Commission months" run first Monday → day before the next first Monday, not calendar months.</>,
          <>At-cost accounts (profile −1/−2 or Skin level) earn no commissions — they settle via per-provider % of GGR shown behind the red <b>Costs</b> links.</>,
          <>Nothing loads until Search (the real initial auto-load call is commented out); no sorting or pagination exists on this report.</>,
        ],
      }}
    >
      <HrsFilters
        fields={fields}
        values={draft}
        onChange={(k, v) => setDraft(d => ({ ...d, [k]: v }))}
        onSearch={doSearch}
        onReset={resetDraft}
        resultLabel={applied && sel ? `${netRows.length} network rows · ${sel.shops.length} cashiers` : "—"}
      />

      {feed.error && <HrsError error={feed.error} onRetry={feed.retry} />}
      {applied && feed.loading && <HrsSkeleton rows={8} cols={6} />}

      {!applied && (
        <HrsTable
          columns={hrcmNetColumns(draft.tab, setModal)}
          rows={[]}
          empty="Choose Category, User and Period, then press Search — the real page loads nothing until Search (initial auto-load commented out, commissions.js:53)."
        />
      )}

      {applied && sel && !feed.loading && !feed.error && (
        <>
          <HrcmPeriodBar period={applied.period} tab={tab} row={sel} />
          <HrcmSummary row={sel} tab={tab} openModal={setModal} />
          <HrsSection title="Network commissions" sub="One row per non-cashier child of the selected user — expand a row for its subnet drill-down (same partial, one level down, summary card suppressed).">
            <HrsTable
              columns={hrcmNetColumns(tab, setModal)}
              rows={netRows}
              rowKey="u"
              rowDetail={(r) => <HrcmSubnet row={r} tab={tab} openModal={setModal} />}
              renderCard={hrcmNetCard(tab, setModal)}
              empty="No network users under this account."
            />
          </HrsSection>
          <HrsSection title="Cashiers" sub={`Shop-level accounts directly under ${sel.u} (rendered with the letter badge "S" on the real page).`}>
            <HrcmShopTable rows={sel.shops} tab={tab} openModal={setModal} />
          </HrsSection>
          <HrsExport
            count={exportRows.length}
            filename={exportName}
            gate="support_export"
            onCsv={() => hrsCsv(exportRows, hrcmExportHeaders(tab), exportName)}
            note={<>Exports the full loaded dataset (every level + cashiers). Real flow: client-side DOM scrape → XLSX via ungated <code>Route::any /reports/commissions/{tab === "all" ? "all" : tab}/excel</code>, collapsed sub-levels silently missing (header divergence 1).</>}
          />
        </>
      )}

      {modal && modal.kind === "profile" && <HrcmProfileModal row={modal.row} tab={modal.tab || tab} onClose={() => setModal(null)} data={data} />}
      {modal && modal.kind === "costs" && <HrcmCostsModal row={modal.row} tab={modal.tab || tab} onClose={() => setModal(null)} />}
      {modal && modal.kind === "turnover" && applied && (
        <HrcmTurnoverModal row={modal.row} period={applied.period} onClose={() => setModal(null)} onWeek={(week) => setModal({ kind: "week", row: modal.row, week })} data={data} />
      )}
      {modal && modal.kind === "week" && (
        <HrcmWeekModal row={modal.row} week={modal.week} onClose={() => setModal({ kind: "turnover", row: modal.row })} />
      )}
    </HrsShell>
  );
};

window.CommissionsReport = CommissionsReport;
