// APAgingPage (Accounts Payable Aging deep-dive) — window.APAgingPage
// Dedicated AP aging cockpit: summary tiles, aging-bucket bar, sortable vendor
// table with per-bill drill-down, a payment-optimization panel (early-pay
// discounts parsed from real vendor terms + overdue penalty risk), a DPO trend,
// and a CFO commentary panel. All values are real: pulled from the ap_bills
// subledger (open balances) queried directly, with a fallback to data.aging.ap
// bucket totals when the subledger is empty. Built on window.PerduraPageKit.
// No hardcoded vendor names or amounts — everything comes from the data.

(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" }); };

  // Parse a payment-terms string (e.g. "2/10 net 30") into a discount offer.
  // Returns { discPct, discDays, netDays } or null. Real terms only — never guessed.
  function parseTerms(terms) {
    if (!terms) return null;
    const m = String(terms).match(/(\d+(?:\.\d+)?)\s*\/\s*(\d+)\s*(?:,?\s*n(?:et)?\s*(\d+))?/i);
    if (!m) return null;
    const discPct = parseFloat(m[1]), discDays = parseInt(m[2], 10);
    if (!(discPct > 0) || !(discDays >= 0)) return null;
    return { discPct, discDays, netDays: m[3] ? parseInt(m[3], 10) : null };
  }

  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);
    const [wc, setWc] = useState(null);
    const [sortKey, setSortKey] = useState("total");
    const [sortDir, setSortDir] = useState("desc");
    const [expanded, setExpanded] = useState(null);
    const ps = K.usePeriodState("ap_aging", "ltm");

    useEffect(() => {
      let alive = true;
      const db = window.supabaseClient;
      if (!db || !scopedCompanyId) { setRows([]); setWc([]); return; }
      db.from("ap_bills").select("bill_no,issue_date,due_date,vendor_name,category,amount,open_balance,terms")
        .eq("company_id", scopedCompanyId).gt("open_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,dpo_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]);

    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]);

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

    const agg = useMemo(() => {
      const buckets = [0, 0, 0, 0, 0];
      const byName = new Map();
      const discounts = [];
      for (const it of (rows || [])) {
        const bal = Number(it.open_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;
        const name = (it.vendor_name || "(unnamed)").trim();
        const cur = byName.get(name) || { name, total: 0, b: [0, 0, 0, 0, 0], bills: [], nextDue: null };
        cur.total += bal; cur.b[bi] += bal;
        cur.bills.push({ bill_no: it.bill_no || "—", issue_date: it.issue_date, due_date: it.due_date, amount: bal, dpd, terms: it.terms, category: it.category });
        const due = parseDay(it.due_date);
        if (due != null && (cur.nextDue == null || due < cur.nextDue)) cur.nextDue = due;
        byName.set(name, cur);
        // Early-pay discount still available? (within discount window from issue date)
        const t = parseTerms(it.terms);
        if (t && it.issue_date) {
          const deadline = parseDay(it.issue_date) + t.discDays * 86400000;
          if (deadline >= asOf.getTime()) discounts.push({ vendor: name, bill_no: it.bill_no || "—", terms: it.terms, discPct: t.discPct, deadline, save: bal * t.discPct / 100 });
        }
      }
      const total = buckets.reduce((a, b) => a + b, 0);
      return { buckets, total, byName, vendors: Array.from(byName.values()), discounts };
    }, [rows, asOf]);

    const fb = (data && data.aging && data.aging.ap) || null;
    const usingFallback = agg.total === 0 && fb && fb.buckets && fb.outstanding > 0;
    const fbBuckets = usingFallback ? [
      (fb.buckets.find((b) => /not due|current/i.test(b.label)) || {}).value || 0,
      (fb.buckets.find((b) => /1.?(15|30)/.test(b.label)) || {}).value || 0,
      (fb.buckets.find((b) => /(16|31).?(30|60)/.test(b.label)) || {}).value || 0,
      (fb.buckets.find((b) => /61.?90/.test(b.label)) || {}).value || 0,
      (fb.buckets.find((b) => />\s?(30|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 dpo = (total && ltmCogs) ? Math.round(total / (ltmCogs / 365)) : null;
    const discountTotal = agg.discounts.reduce((a, d) => a + d.save, 0);
    const overdue = buckets[1] + buckets[2] + buckets[3] + buckets[4];
    const loading = rows === null;

    const sortedVendors = useMemo(() => {
      const arr = agg.vendors.slice();
      const val = (v) => sortKey === "total" ? v.total
        : sortKey === "overdue" ? (v.b[1] + v.b[2] + v.b[3] + v.b[4])
        : sortKey === "next" ? (v.nextDue == null ? Infinity : v.nextDue)
        : v.total;
      arr.sort((a, b) => {
        const av = val(a), bv = val(b);
        return (sortKey === "next") ? (av - bv) * (sortDir === "desc" ? -1 : 1) : (bv - av) * (sortDir === "desc" ? 1 : -1);
      });
      return arr;
    }, [agg.vendors, 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" ? " ▼" : " ▲") : "";

    const dpoTrend = useMemo(() => {
      const s = (wc || []).map((r) => (r.dpo_days != null ? Number(r.dpo_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) => h(K.Kpi, { label, value, valueColor: color || "navy", sub, 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 AP", M(total), "100% · " + (agg.vendors.length || (fb ? fb.count : 0)) + " vendors", "navy"),
      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), "red", buckets[4] > 0),
      tile("DPO", dpo == null ? "—" : dpo + " days", "AP ÷ COGS × 365", "blue"),
      tile("Early Pay Discount", discountTotal > 0 ? money(discountTotal) : "—", discountTotal > 0 ? "potential savings" : "no active offers", "green"));

    const bucketBar = h(K.Card, { title: "AGING DISTRIBUTION", sub: "Share of total AP 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: "#0d2040" } }, money(buckets[i]) + " · " + p.toFixed(0) + "%"));
        })));

    // ── Vendor table + bill 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 vendorTable = h(K.Card, { title: "VENDOR AGING", sub: sortedVendors.length + " vendors · click a row to see bills · amber = has overdue balance", padding: 0 },
      sortedVendors.length ? h("div", { style: { overflowX: "auto" } }, h("table", { className: "pa-table" },
        h("thead", null, h("tr", null,
          th("Vendor", null), th("Total AP", "total", true), th("Current", null, true), th("1-30", null, true),
          th("31-60", null, true), th("61-90", null, true), th("90+", "overdue", true), th("Next Due", "next", true))),
        h("tbody", null, sortedVendors.map((v) => {
          const isOverdue = (v.b[1] + v.b[2] + v.b[3] + v.b[4]) > 0;
          const isOpen = expanded === v.name;
          const out = [h("tr", { key: v.name, onClick: () => setExpanded(isOpen ? null : v.name), title: "Click to expand bills",
            style: { cursor: "pointer", background: isOverdue ? "rgba(180,83,9,.07)" : undefined } },
            h("td", { style: { fontWeight: 600 } }, (isOpen ? "▼ " : "▸ ") + v.name),
            h("td", { style: { ...tdR, fontWeight: 700 } }, money(v.total)),
            h("td", { style: tdR }, v.b[0] ? money(v.b[0]) : "—"),
            h("td", { style: tdR }, v.b[1] ? money(v.b[1]) : "—"),
            h("td", { style: tdR }, v.b[2] ? money(v.b[2]) : "—"),
            h("td", { style: tdR }, v.b[3] ? money(v.b[3]) : "—"),
            h("td", { style: { ...tdR, color: v.b[4] ? "#DC2626" : undefined } }, v.b[4] ? money(v.b[4]) : "—"),
            h("td", { style: tdR }, v.nextDue ? fmtDate(new Date(v.nextDue).toISOString()) : "—"))];
          if (isOpen) {
            out.push(h("tr", { key: v.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, "Bill #"), h("th", null, "Date"), h("th", null, "Due Date"), h("th", { className: "num" }, "Amount"), h("th", null, "Status"))),
                h("tbody", null, v.bills.slice().sort((a, b) => b.dpd - a.dpd).map((bill, i) => {
                  const st = bill.dpd > 0 ? ("Overdue " + bill.dpd + " days") : bill.dpd > -7 ? ("Due in " + Math.abs(bill.dpd) + " days") : "Current";
                  const stColor = bill.dpd > 0 ? "#B45309" : bill.dpd > -7 ? "#1C4ED8" : "#059669";
                  return h("tr", { key: i },
                    h("td", { style: { fontWeight: 600 } }, bill.bill_no),
                    h("td", null, fmtDate(bill.issue_date)),
                    h("td", null, fmtDate(bill.due_date)),
                    h("td", { style: tdR }, money(bill.amount)),
                    h("td", null, h("span", { style: { fontSize: 10.5, fontWeight: 700, color: stColor } }, st)));
                }))))));
          }
          return out;
        }))))
        : h("div", { className: "pa-card-body", style: { color: "#6475a0", fontSize: 12.5 } },
          usingFallback ? "Bucket totals are available but per-bill detail requires the AP subledger (ap_bills)." : "No open payables. Connect your accounting data or sync to populate the AP subledger."));

    // ── Payment optimization ────────────────────────────────────────────────────
    const overdueBills = [];
    agg.vendors.forEach((v) => v.bills.forEach((b) => { if (b.dpd > 0) overdueBills.push({ vendor: v.name, ...b }); }));
    overdueBills.sort((a, b) => b.dpd - a.dpd);
    const paymentPanel = (agg.discounts.length || overdueBills.length) ? h(K.Card, { title: "PAYMENT OPTIMIZATION", sub: "Discount capture and penalty risk from your open bills" },
      h("div", { style: { padding: "4px 2px" } },
        agg.discounts.length ? h("div", { style: { marginBottom: overdueBills.length ? 14 : 0 } },
          h("div", { style: { fontSize: 11, fontWeight: 800, color: "#059669", textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 6 } }, "💡 Early-pay discounts available"),
          agg.discounts.slice().sort((a, b) => b.save - a.save).slice(0, 8).map((d, i) => h("div", { key: i, style: { display: "flex", justifyContent: "space-between", fontSize: 12, padding: "5px 8px", borderBottom: "1px solid rgba(13,32,64,.05)" } },
            h("span", { style: { color: "#1a2540" } }, d.vendor + " · " + d.terms + " · pay by " + fmtDate(new Date(d.deadline).toISOString())),
            h("b", { style: { color: "#059669", fontFamily: K.MONO } }, "save " + money(d.save)))),
          h("div", { style: { fontSize: 12.5, fontWeight: 800, color: "#059669", marginTop: 8, textAlign: "right" } }, "Total potential savings: " + money(discountTotal))) : null,
        overdueBills.length ? h("div", null,
          h("div", { style: { fontSize: 11, fontWeight: 800, color: "#B45309", textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 6 } }, "⚠️ Overdue bills (penalty risk)"),
          overdueBills.slice(0, 8).map((b, i) => h("div", { key: i, style: { fontSize: 12, padding: "4px 8px", color: "#1a2540", borderBottom: "1px solid rgba(13,32,64,.05)" } },
            b.vendor + " · " + b.bill_no + ": " + money(b.amount) + " — " + b.dpd + " days overdue — check for late fees"))) : null)) : null;

    const trendCard = h(K.Card, { title: "DPO TREND", sub: "Days Payable Outstanding · are we paying faster or slower?" },
      dpoTrend ? h(K.Line, { values: dpoTrend.values, labels: dpoTrend.labels, color: "#0891B2", suffix: "d", height: 170 })
        : h("div", { style: { color: "#6475a0", fontSize: 12.5, padding: 12 } }, "Awaiting stored monthly working-capital snapshots to plot the DPO trend. Current DPO is " + (dpo == null ? "—" : dpo + " days") + "."));

    const topVendor = agg.vendors.slice().sort((a, b) => b.total - a.total)[0];
    const concPct = (topVendor && total) ? topVendor.total / total * 100 : null;
    const commentary = h(K.CFOCommentaryPanel, { title: "Accounts Payable", insights: [
      concPct != null ? { type: "neutral", text: "Largest payable: <b>" + topVendor.name + "</b> at " + money(topVendor.total) + " (<b>" + concPct.toFixed(0) + "%</b> of AP).", detail: "Concentrated payables give you negotiating leverage on terms and discounts." } : null,
      { type: dpo != null && dpo < 25 ? "warning" : "positive", text: "DPO is <b>" + (dpo == null ? "—" : dpo + " days") + "</b>" + (dpo != null ? (dpo < 25 ? " — you may be paying faster than necessary." : " — reasonable payment timing.") : "."), detail: "Extending DPO toward terms frees working capital without straining suppliers." },
      overdue > 0 ? { type: "warning", text: "<b>" + money(overdue) + "</b> of AP is past due.", detail: "Clear overdue balances to avoid late fees and protect supplier relationships." } : { type: "positive", text: "No payables are past due.", detail: "Payment discipline is healthy." },
    ].filter(Boolean), savings: discountTotal > 0 ? ("Capturing all available early-pay discounts would save <b>" + money(discountTotal) + "</b> — a guaranteed return on paying a few bills early.") : 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 Payable Aging",
      subtitle: (loading ? "Loading subledger…" : (total ? money(total) + " outstanding across " + (agg.vendors.length || (fb ? fb.count : 0)) + " vendors" : "No open payables")) + " · as of " + fmtDate(asOf.toISOString()), controls } },
      loading ? h(K.Card, { title: "Loading AP subledger…" }, h("div", { style: { padding: 16, color: "#6475a0" } }, "Fetching open bills…"))
        : [tilesRow1, tilesRow2, bucketBar, vendorTable, paymentPanel, trendCard, commentary]);
  }

  window.APAgingPage = Page;
})();
