// ARAgingPage (Accounts Receivable Aging deep-dive) — window.ARAgingPage
// Dedicated AR aging cockpit: summary tiles, aging-bucket bar, sortable customer
// table with per-invoice drill-down, an AI-style collections priority list, and a
// CFO commentary panel (concentration, DSO, bad-debt exposure). All values are
// real: pulled from the ar_invoices subledger (open balances) queried directly,
// with a fallback to data.aging.ar bucket totals when the subledger is empty.
// Built on window.PerduraPageKit. No fabricated numbers.

(function () {
  const { useState, useEffect, useMemo } = React;
  const h = React.createElement;
  const BUCKET_LABELS = ["Current", "1-30", "31-60", "61-90", "90+"];
  const BUCKET_COLORS = ["#1C4ED8", "#0891B2", "#F59E0B", "#F97316", "#DC2626"];
  const bucketOf = (dpd) => (dpd <= 0 ? 0 : dpd <= 30 ? 1 : dpd <= 60 ? 2 : dpd <= 90 ? 3 : 4);
  const parseDay = (s) => { const t = Date.parse(s); return Number.isFinite(t) ? t : null; };
  const fmtDate = (s) => { const t = parseDay(s); return t == null ? "—" : new Date(t).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "2-digit" }); };

  function Page(props) {
    const K = window.PerduraPageKit;
    if (!K) return h("div", { className: "pc-page" }, "Loading…");
    const { data, companyProfile, scopedCompanyId, setPage } = props;
    const M = (v) => K.moneyStr(v, { compact: true });
    const money = (v) => K.moneyStr(v);
    const nav = (p) => (setPage ? setPage(p) : (window.__perduraSetPage && window.__perduraSetPage(p)));

    const [rows, setRows] = useState(null); // null=loading, []=none
    const [wc, setWc] = useState(null);     // working-capital snapshots (DSO trend)
    const [sortKey, setSortKey] = useState("total");
    const [sortDir, setSortDir] = useState("desc");
    const [expanded, setExpanded] = useState(null);
    const ps = K.usePeriodState("ar_aging", "ltm");

    useEffect(() => {
      let alive = true;
      const db = window.supabaseClient;
      if (!db || !scopedCompanyId) { setRows([]); setWc([]); return; }
      db.from("ar_invoices").select("invoice_no,issue_date,due_date,customer_name,total,paid_amount,balance,status,terms")
        .eq("company_id", scopedCompanyId).gt("balance", 0).limit(5000)
        .then(({ data: d }) => { if (alive) setRows(Array.isArray(d) ? d : []); });
      const cutoff = new Date(); cutoff.setMonth(cutoff.getMonth() - 16);
      db.from("working_capital_snapshots").select("period_end,dso_days").eq("company_id", scopedCompanyId)
        .gte("period_end", cutoff.toISOString().slice(0, 10)).order("period_end", { ascending: true }).limit(2000)
        .then(({ data: d }) => { if (alive) setWc(Array.isArray(d) ? d : []); });
      return () => { alive = false; };
    }, [scopedCompanyId]);

    // Anchor aging to the dataset's own latest reference date (not wall-clock), so
    // fixtures dated in the past don't all collapse into 90+. Falls back to today.
    const asOf = useMemo(() => {
      let mx = null;
      for (const r of (rows || [])) { const t = parseDay(r.due_date) || parseDay(r.issue_date); if (t != null && (mx == null || t > mx)) mx = t; }
      return mx != null ? new Date(mx) : new Date();
    }, [rows]);

    // LTM revenue for DSO.
    const ltmRevenue = useMemo(() => {
      const r = (data && data.plHistory && data.plHistory.revenue) || (data && data.pl && data.pl.revenue) || [];
      const s = r.slice(-12).reduce((a, b) => a + (Number(b) || 0), 0);
      return s > 0 ? s : null;
    }, [data]);

    // Aggregate open invoices → buckets + per-customer rollups + per-invoice detail.
    const agg = useMemo(() => {
      const buckets = [0, 0, 0, 0, 0];
      const byName = new Map();
      let paid = 0, invoiced = 0;
      for (const it of (rows || [])) {
        const bal = Number(it.balance) || 0; if (bal <= 0) continue;
        const dpd = it.due_date ? Math.floor((asOf - new Date(it.due_date)) / 86400000) : 0;
        const bi = bucketOf(dpd);
        buckets[bi] += bal;
        paid += Number(it.paid_amount) || 0; invoiced += Number(it.total) || bal;
        const name = (it.customer_name || "(unnamed)").trim();
        const cur = byName.get(name) || { name, total: 0, b: [0, 0, 0, 0, 0], invoices: [], lastActivity: 0 };
        cur.total += bal; cur.b[bi] += bal;
        cur.invoices.push({ invoice_no: it.invoice_no || "—", issue_date: it.issue_date, due_date: it.due_date, amount: bal, dpd, status: it.status });
        const act = parseDay(it.issue_date) || 0; if (act > cur.lastActivity) cur.lastActivity = act;
        byName.set(name, cur);
      }
      const total = buckets.reduce((a, b) => a + b, 0);
      return { buckets, total, byName, customers: Array.from(byName.values()), collectionRate: invoiced ? paid / invoiced * 100 : null };
    }, [rows, asOf]);

    // Fallback to pre-computed bucket totals when the subledger is empty.
    const fb = (data && data.aging && data.aging.ar) || null;
    const usingFallback = agg.total === 0 && fb && fb.buckets && fb.outstanding > 0;
    const fbBuckets = usingFallback ? [
      (fb.buckets.find((b) => /current/i.test(b.label)) || {}).value || 0,
      (fb.buckets.find((b) => /1.?30/.test(b.label)) || {}).value || 0,
      (fb.buckets.find((b) => /31.?60/.test(b.label)) || {}).value || 0,
      (fb.buckets.find((b) => /61.?90/.test(b.label)) || {}).value || 0,
      (fb.buckets.find((b) => />\s?90|91/.test(b.label)) || {}).value || 0,
    ] : null;
    const buckets = usingFallback ? fbBuckets : agg.buckets;
    const total = usingFallback ? fbBuckets.reduce((a, b) => a + b, 0) : agg.total;

    const dso = (total && ltmRevenue) ? Math.round(total / (ltmRevenue / 365)) : null;
    const pastDue90 = buckets[4];
    const pct90 = total ? pastDue90 / total * 100 : 0;
    const badDebt = pastDue90 * 0.40; // 40% reserve on 90+
    const loading = rows === null;

    // Sorting.
    const sortedCustomers = useMemo(() => {
      const arr = agg.customers.slice();
      const val = (c) => sortKey === "total" ? c.total
        : sortKey === "b4" ? c.b[4]
        : sortKey === "last" ? c.lastActivity
        : c.total;
      arr.sort((a, b) => (val(b) - val(a)) * (sortDir === "desc" ? 1 : -1));
      return arr;
    }, [agg.customers, sortKey, sortDir]);

    const setSort = (k) => { if (k === sortKey) setSortDir((d) => d === "desc" ? "asc" : "desc"); else { setSortKey(k); setSortDir("desc"); } };
    const sortArrow = (k) => sortKey === k ? (sortDir === "desc" ? " ▼" : " ▲") : "";

    // Collections priority — rank customers by overdue exposure (weight oldest most).
    const priority = useMemo(() => {
      return agg.customers.map((c) => {
        const overdue = c.b[1] + c.b[2] + c.b[3] + c.b[4];
        const weighted = c.b[1] * 1 + c.b[2] * 2 + c.b[3] * 3 + c.b[4] * 4;
        const oldestBucket = c.b[4] > 0 ? 4 : c.b[3] > 0 ? 3 : c.b[2] > 0 ? 2 : c.b[1] > 0 ? 1 : 0;
        return { name: c.name, overdue, weighted, oldestBucket };
      }).filter((c) => c.overdue > 0).sort((a, b) => b.weighted - a.weighted).slice(0, 6);
    }, [agg.customers]);

    // DSO trend from snapshots (honest placeholder when absent).
    const dsoTrend = useMemo(() => {
      const s = (wc || []).map((r) => (r.dso_days != null ? Number(r.dso_days) : null)).filter((v) => v != null);
      return s.length >= 2 ? { values: s, labels: (wc || []).map((r) => new Date(String(r.period_end)).toLocaleDateString("en-US", { month: "short", year: "2-digit" })) } : null;
    }, [wc]);

    // ── Tiles ─────────────────────────────────────────────────────────────────
    const tile = (label, value, sub, color, danger, onClick) => h(K.Kpi, {
      label, value, valueColor: color || "navy", sub, animDelay: 0,
      onClick, subColor: danger ? "#DC2626" : undefined,
    });
    const bpct = (i) => total ? (buckets[i] / total * 100).toFixed(0) + "%" : "—";
    const tilesRow1 = h("div", { className: "pa-kpi-strip pa-kpi-strip-4" },
      tile("Total AR", M(total), "100% · " + (agg.customers.length || (fb ? fb.count : 0)) + " customers", "navy", false, () => nav("arap")),
      tile("Current", M(buckets[0]), bpct(0), "green"),
      tile("1-30 Days", M(buckets[1]), bpct(1), "teal"),
      tile("31-60 Days", M(buckets[2]), bpct(2), "amber"));
    const tilesRow2 = h("div", { className: "pa-kpi-strip pa-kpi-strip-4", style: { marginTop: 4 } },
      tile("61-90 Days", M(buckets[3]), bpct(3), "amber"),
      tile("90+ Days", M(buckets[4]), bpct(4) + (pct90 > 10 ? " ⚠" : ""), pct90 > 10 ? "red" : "amber", pct90 > 10),
      tile("DSO", dso == null ? "—" : dso + " days", "AR ÷ revenue × 365", "blue"),
      tile("Collection Rate", agg.collectionRate == null ? "—" : agg.collectionRate.toFixed(0) + "%", "paid ÷ invoiced (open)", "green"));

    // ── Aging bucket bar ────────────────────────────────────────────────────────
    const bucketBar = h(K.Card, { title: "AGING DISTRIBUTION", sub: "Share of total AR by bucket" },
      h("div", { style: { padding: "6px 4px", display: "flex", flexDirection: "column", gap: 9 } },
        BUCKET_LABELS.map((lb, i) => {
          const p = total ? buckets[i] / total * 100 : 0;
          return h("div", { key: i, style: { display: "flex", alignItems: "center", gap: 10 } },
            h("span", { style: { width: 62, fontSize: 11, fontWeight: 600, color: "#4a5680" } }, lb),
            h("div", { style: { flex: 1, height: 16, background: "rgba(13,32,64,.06)", borderRadius: 4, overflow: "hidden" } },
              h("div", { style: { height: "100%", width: Math.max(1, p) + "%", background: BUCKET_COLORS[i], borderRadius: 4 } })),
            h("span", { style: { width: 92, textAlign: "right", fontSize: 11, fontWeight: 700, fontFamily: K.MONO, color: i === 4 && p > 0 ? "#DC2626" : "#0d2040" } }, money(buckets[i]) + " · " + p.toFixed(0) + "%"));
        })));

    // ── Customer table + invoice drill-down ─────────────────────────────────────
    const th = (label, k, right) => h("th", { onClick: k ? () => setSort(k) : undefined, className: right ? "num" : undefined, style: { cursor: k ? "pointer" : "default", whiteSpace: "nowrap" } }, label + (k ? sortArrow(k) : ""));
    const tdR = { textAlign: "right", fontFamily: K.MONO, fontVariantNumeric: "tabular-nums" };
    const customerTable = h(K.Card, { title: "CUSTOMER AGING", sub: sortedCustomers.length + " customers · click a row to see invoices · red = has 90+ balance", padding: 0 },
      sortedCustomers.length ? h("div", { style: { overflowX: "auto" } }, h("table", { className: "pa-table" },
        h("thead", null, h("tr", null,
          th("Customer", null), th("Total AR", "total", true), th("Current", null, true), th("1-30", null, true),
          th("31-60", null, true), th("61-90", null, true), th("90+", "b4", true), th("Last Invoice", "last", true))),
        h("tbody", null, sortedCustomers.map((c) => {
          const has90 = c.b[4] > 0;
          const isOpen = expanded === c.name;
          const rowsOut = [h("tr", { key: c.name, onClick: () => setExpanded(isOpen ? null : c.name), title: "Click to expand invoices",
            style: { cursor: "pointer", background: has90 ? "rgba(220,38,38,.06)" : undefined } },
            h("td", { style: { fontWeight: 600 } }, (isOpen ? "▼ " : "▸ ") + c.name),
            h("td", { style: { ...tdR, fontWeight: 700 } }, money(c.total)),
            h("td", { style: tdR }, c.b[0] ? money(c.b[0]) : "—"),
            h("td", { style: tdR }, c.b[1] ? money(c.b[1]) : "—"),
            h("td", { style: tdR }, c.b[2] ? money(c.b[2]) : "—"),
            h("td", { style: tdR }, c.b[3] ? money(c.b[3]) : "—"),
            h("td", { style: { ...tdR, color: has90 ? "#DC2626" : undefined, fontWeight: has90 ? 700 : 400 } }, c.b[4] ? money(c.b[4]) : "—"),
            h("td", { style: tdR }, c.lastActivity ? fmtDate(new Date(c.lastActivity).toISOString()) : "—"))];
          if (isOpen) {
            rowsOut.push(h("tr", { key: c.name + "-det" }, h("td", { colSpan: 8, style: { background: "#F7F8FB", padding: "6px 12px 12px" } },
              h("table", { className: "pa-table", style: { margin: 0 } },
                h("thead", null, h("tr", null, h("th", null, "Invoice #"), h("th", null, "Date"), h("th", null, "Due Date"), h("th", { className: "num" }, "Amount"), h("th", { className: "num" }, "Days Overdue"), h("th", null, "Status"))),
                h("tbody", null, c.invoices.slice().sort((a, b) => b.dpd - a.dpd).map((inv, i) => {
                  const st = inv.dpd > 0 ? "Overdue" : inv.dpd > -7 ? "Due Soon" : "Current";
                  const stColor = st === "Overdue" ? "#DC2626" : st === "Due Soon" ? "#B45309" : "#059669";
                  return h("tr", { key: i },
                    h("td", { style: { fontWeight: 600 } }, inv.invoice_no),
                    h("td", null, fmtDate(inv.issue_date)),
                    h("td", null, fmtDate(inv.due_date)),
                    h("td", { style: tdR }, money(inv.amount)),
                    h("td", { style: { ...tdR, color: inv.dpd > 0 ? "#DC2626" : "#6b7a99" } }, inv.dpd > 0 ? inv.dpd + " days" : "—"),
                    h("td", null, h("span", { style: { fontSize: 10.5, fontWeight: 700, color: stColor } }, inv.status || st)));
                }))))));
          }
          return rowsOut;
        }))))
        : h("div", { className: "pa-card-body", style: { color: "#6475a0", fontSize: 12.5 } },
          usingFallback ? "Bucket totals are available but per-invoice detail requires the AR subledger (ar_invoices)." : "No open receivables. Connect your accounting data or sync to populate the AR subledger."));

    // ── Collections priority ────────────────────────────────────────────────────
    const priorityPanel = priority.length ? h(K.Card, { title: "COLLECTIONS PRIORITY", sub: "Ranked by overdue exposure, weighted to the oldest balances" },
      h("div", { style: { padding: "4px 0" } }, priority.map((p, i) => {
        const sev = p.oldestBucket >= 3 ? "high" : p.oldestBucket === 2 ? "medium" : "low";
        const dot = sev === "high" ? "🔴" : sev === "medium" ? "🟡" : "🟢";
        const rec = sev === "high" ? "Immediate call + payment-plan offer" : sev === "medium" ? "Send reminder + statement" : "Automated reminder";
        return h("div", { key: i, onClick: () => setExpanded(p.name), style: { display: "flex", gap: 10, alignItems: "flex-start", padding: "9px 14px", borderBottom: "1px solid rgba(13,32,64,.05)", cursor: "pointer" } },
          h("span", { style: { fontSize: 13, flexShrink: 0 } }, dot),
          h("div", null,
            h("div", { style: { fontSize: 12.5, fontWeight: 700, color: "#0d2040" } }, "Priority " + (i + 1) + ": " + p.name + " — " + money(p.overdue) + " overdue"),
            h("div", { style: { fontSize: 11, color: "#6475a0", marginTop: 2 } }, "Recommended: " + rec)));
      }))) : null;

    // ── DSO trend ────────────────────────────────────────────────────────────────
    const trendCard = h(K.Card, { title: "DSO TREND", sub: "Days Sales Outstanding · from stored working-capital snapshots" },
      dsoTrend ? h(K.Line, { values: dsoTrend.values, labels: dsoTrend.labels, color: "#1C4ED8", suffix: "d", height: 170 })
        : h("div", { style: { color: "#6475a0", fontSize: 12.5, padding: 12 } }, "Awaiting stored monthly working-capital snapshots to plot the DSO trend. Current DSO is " + (dso == null ? "—" : dso + " days") + "."));

    // ── CFO commentary ───────────────────────────────────────────────────────────
    const topCust = agg.customers.slice().sort((a, b) => b.total - a.total)[0];
    const concPct = (topCust && total) ? topCust.total / total * 100 : null;
    const commentary = h(K.CFOCommentaryPanel, { title: "Accounts Receivable", insights: [
      concPct != null ? { type: concPct > 25 ? "warning" : "neutral", text: "Concentration: <b>" + topCust.name + "</b> is <b>" + concPct.toFixed(0) + "%</b> of total AR (" + money(topCust.total) + ").", detail: concPct > 25 ? "Single-customer concentration above 25% is a collection risk — diversify or tighten terms." : "Customer concentration is within a comfortable range." } : null,
      { type: dso != null && dso > 45 ? "warning" : "positive", text: "DSO is <b>" + (dso == null ? "—" : dso + " days") + "</b>" + (dso != null ? (dso > 45 ? " — slower than a healthy ~45-day target." : " — within a healthy range.") : "."), detail: "Days Sales Outstanding = AR ÷ trailing revenue × 365." },
      { type: pct90 > 10 ? "warning" : "neutral", text: "<b>" + pct90.toFixed(0) + "%</b> of AR is 90+ days (" + money(pastDue90) + ").", detail: pct90 > 10 ? "Above 10% in the oldest bucket signals write-off risk — escalate collections." : "The oldest bucket is contained." },
    ].filter(Boolean), savings: pastDue90 > 0 ? ("Estimated bad-debt exposure at a 40% reserve on 90+ balances: <b>" + money(badDebt) + "</b>. Recovering even half of this is direct cash to the business.") : null });

    const controls = h("div", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" } },
      h(K.PeriodControls, Object.assign({}, ps, { showCompare: false })),
      h("button", { onClick: () => window.print(), style: { background: "rgba(255,255,255,.14)", border: "1px solid rgba(255,255,255,.24)", borderRadius: 7, padding: "6px 12px", color: "#eef2ff", fontSize: 11.5, fontWeight: 700, cursor: "pointer", fontFamily: "Inter, system-ui" } }, "⭳ Export"));

    return h(K.Shell, { hero: { eyebrow: "GENERAL LEDGER", title: "Accounts Receivable Aging",
      subtitle: (loading ? "Loading subledger…" : (total ? money(total) + " outstanding across " + (agg.customers.length || (fb ? fb.count : 0)) + " customers" : "No open receivables")) + " · as of " + fmtDate(asOf.toISOString()), controls } },
      loading ? h(K.Card, { title: "Loading AR subledger…" }, h("div", { style: { padding: 16, color: "#6475a0" } }, "Fetching open invoices…"))
        : [tilesRow1, tilesRow2, bucketBar, customerTable, priorityPanel, trendCard, commentary]);
  }

  window.ARAgingPage = Page;
})();
