// CategoryMappingPage — window.CategoryMappingPage
//
// Admin management view: every distinct GL account with its current canonical
// category, editable inline. Changes persist to account_mappings
// (category_override) in Supabase — the same write the Trial Balance inline
// editor performs, but in a dedicated, searchable management surface. Honest
// fallback when no GL data is present; never fabricates accounts.
//
// Props: { data, companyProfile, scopedCompanyId, setPage }

(function () {
  const { useState, useMemo } = React;
  const h = React.createElement;

  const C = {
    bg: "#F7F8FB", card: "#FFFFFF", border: "#E4E8F0", accent: "#1C4ED8", accentSoft: "#EEF2FF",
    text1: "#1A2233", text2: "#475569", muted: "#6B7A99", green: "#059669",
  };
  const CARD = { background: C.card, border: "1px solid " + C.border, boxShadow: "0 1px 4px rgba(0,0,0,0.06)", borderRadius: 10 };
  const money = (v, o) => (window.PerduraFormat && window.PerduraFormat.money) ? window.PerduraFormat.money(v, o) : "$" + Math.round(v || 0).toLocaleString();
  const ctrlStyle = { padding: "7px 10px", fontSize: 12.5, border: "1px solid " + C.border, borderRadius: 7, outline: "none", background: C.card, color: C.text1 };

  // Reuse the taxonomy section resolver (same logic as Trial Balance).
  function sectionFor(t, profile) {
    const tax = window.PerduraTaxonomy;
    if (tax && tax.sectionForCategory && t.canonical_category) {
      try { const s = tax.sectionForCategory(t.canonical_category, profile); if (s) return s; } catch (e) { /* fall through */ }
    }
    const c = String(t.account_code || "");
    if (/^1\d/.test(c)) return "current_assets";
    if (/^2\d/.test(c)) return "current_liab";
    if (/^3\d/.test(c)) return "equity";
    if (/^4\d/.test(c)) return "revenue";
    if (/^[5-9]\d/.test(c)) return "opex";
    return "opex";
  }

  function canonicalCategories(profile, txns) {
    const tax = window.PerduraTaxonomy;
    if (tax && tax.categoriesForProfile) {
      try { const seen = new Set(); const out = []; tax.categoriesForProfile(profile).forEach((c) => { if (c.key && !seen.has(c.key)) { seen.add(c.key); out.push(c.key); } }); if (out.length) return out; } catch (e) { /* fall through */ }
    }
    const seen = new Set(); (txns || []).forEach((t) => { if (t.canonical_category) seen.add(t.canonical_category); });
    return Array.from(seen).sort();
  }

  function CategoryMappingPage(props) {
    const { data, companyProfile, scopedCompanyId, setPage } = props || {};
    const txns = (data && data.txns) || [];
    const [overrides, setOverrides] = useState({}); // account_code -> category
    const [savingCode, setSavingCode] = useState(null);
    const [savedCode, setSavedCode] = useState(null);
    const [search, setSearch] = useState("");
    const [onlyUnmapped, setOnlyUnmapped] = useState(false);

    const categoryOptions = useMemo(() => canonicalCategories(companyProfile, txns), [companyProfile, txns]);

    // One row per distinct GL account, with txn count + absolute volume so the
    // most material accounts can be prioritized for mapping.
    const accounts = useMemo(() => {
      const map = new Map();
      for (const t of txns) {
        const code = (t.account_code != null && t.account_code !== "") ? String(t.account_code) : "(no code)";
        const name = t.account_name || t.canonical_category || "(unnamed account)";
        const id = code + "||" + name;
        const cur = map.get(id) || { id, code, name, category: t.canonical_category || "", count: 0, volume: 0, section: sectionFor(t, companyProfile) };
        cur.count += 1; cur.volume += Math.abs(Number(t.amount) || 0);
        if (!cur.category && t.canonical_category) cur.category = t.canonical_category;
        map.set(id, cur);
      }
      let out = Array.from(map.values());
      out = out.map((r) => overrides[r.code] ? Object.assign({}, r, { category: overrides[r.code] }) : r);
      out.sort((a, b) => b.volume - a.volume);
      return out;
    }, [txns, companyProfile, overrides]);

    const filtered = accounts.filter((r) => {
      if (onlyUnmapped && r.category) return false;
      if (search) { const q = search.toLowerCase(); if (!(String(r.name).toLowerCase().includes(q) || String(r.code).toLowerCase().includes(q) || String(r.category).toLowerCase().includes(q))) return false; }
      return true;
    });
    const unmappedCount = accounts.filter((r) => !r.category).length;

    const save = (code, newCat) => {
      if (!newCat) return;
      setOverrides((p) => Object.assign({}, p, { [code]: newCat }));
      const db = window.supabaseClient;
      if (!db || !scopedCompanyId || code === "(no code)") { setSavedCode(code); setTimeout(() => setSavedCode(null), 1800); return; }
      setSavingCode(code); setSavedCode(null);
      db.from("account_mappings").update({ category_override: newCat }).eq("company_id", scopedCompanyId).eq("account_code", code)
        .then(() => { setSavingCode(null); setSavedCode(code); setTimeout(() => setSavedCode(null), 1800); window.logAuditEvent && window.logAuditEvent("account_mapping_change", { category: "admin", page: "category-mapping", detail: "Account " + code + " re-categorized to " + newCat }); }, () => { setSavingCode(null); });
    };

    const hero = h("div", { className: "pa-hero", style: { borderRadius: 14, marginBottom: 18 } },
      h("div", { className: "pa-hero-eyebrow" }, "ADMIN"),
      h("div", { className: "pa-hero-title" }, "Category Mapping"),
      h("div", { className: "pa-hero-subtitle" }, "Re-classify GL accounts to canonical categories · saves to account_mappings"));

    if (!txns.length) {
      return h("div", { style: { background: C.bg, minHeight: "100%", padding: "4px 2px" } }, hero,
        h("div", { style: Object.assign({}, CARD, { padding: 22, color: C.muted, fontSize: 13 }) },
          "Awaiting general-ledger data — connect your accounting system or import a transaction feed to manage account categories."));
    }

    const controls = h("div", { style: { display: "flex", flexWrap: "wrap", gap: 10, alignItems: "center", marginBottom: 16 } },
      h("input", { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Search account name, code or category…", style: Object.assign({}, ctrlStyle, { minWidth: 260 }) }),
      h("label", { style: { display: "flex", alignItems: "center", gap: 6, fontSize: 12.5, color: C.text2, cursor: "pointer" } },
        h("input", { type: "checkbox", checked: onlyUnmapped, onChange: (e) => setOnlyUnmapped(e.target.checked) }), "Only unmapped (" + unmappedCount + ")"),
      h("div", { style: { flex: 1 } }),
      setPage ? h("button", { onClick: () => setPage("trial_balance"), style: Object.assign({}, ctrlStyle, { cursor: "pointer", fontWeight: 600, color: C.accent, background: C.accentSoft, border: "1px solid " + C.accent }) }, "→ Trial Balance") : null);

    const th = (txt, align) => h("th", { style: { textAlign: align || "right", padding: "10px 12px", borderBottom: "1px solid " + C.border, color: C.muted, fontWeight: 600, fontSize: 11, whiteSpace: "nowrap" } }, txt);
    const td = (children, style) => h("td", { style: Object.assign({ padding: "7px 12px", borderBottom: "1px solid #F1F5F9", color: C.text2, whiteSpace: "nowrap" }, style || {}) }, children);

    return h("div", { style: { background: C.bg, minHeight: "100%", padding: "4px 2px" } }, hero, controls,
      h("div", { style: Object.assign({}, CARD, { marginBottom: 16 }) },
        h("div", { style: { padding: "14px 16px 0", fontSize: 14, fontWeight: 700, color: C.text1 } }, filtered.length + " accounts" + (onlyUnmapped ? " · unmapped only" : "")),
        h("div", { style: { padding: "8px 0 0" } },
          h("div", { style: { overflowX: "auto" } },
            h("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: 12.5 } },
              h("thead", null, h("tr", null, th("Code", "left"), th("Account Name", "left"), th("Section", "left"), th("Category (click to edit)", "left"), th("Txns"), th("Volume"))),
              h("tbody", null, filtered.map((r) => h("tr", { key: r.id },
                td(r.code, { textAlign: "left", color: C.muted, fontVariantNumeric: "tabular-nums" }),
                td(r.name, { textAlign: "left", fontWeight: 600, color: C.text1 }),
                td(String(r.section).replace(/_/g, " "), { textAlign: "left", color: C.muted, fontSize: 11.5, textTransform: "capitalize" }),
                h("td", { style: { padding: "5px 12px", borderBottom: "1px solid #F1F5F9", textAlign: "left" } },
                  h("select", { value: r.category || "", onChange: (e) => save(r.code, e.target.value), style: Object.assign({}, ctrlStyle, { padding: "4px 6px", maxWidth: 240, borderColor: r.category ? C.border : "#d97706" }) },
                    h("option", { value: "" }, r.category ? "— select —" : "⚠ Unmapped"),
                    categoryOptions.map((o) => h("option", { key: o, value: o }, o))),
                  savingCode === r.code ? h("span", { style: { marginLeft: 6, fontSize: 10.5, color: C.muted } }, "saving…") : null,
                  savedCode === r.code ? h("span", { style: { marginLeft: 6, fontSize: 10.5, color: C.green, fontWeight: 700 } }, "✓ saved") : null),
                td(r.count.toLocaleString(), { color: C.muted, fontVariantNumeric: "tabular-nums" }),
                td(money(r.volume, { compact: true }), { fontVariantNumeric: "tabular-nums" })))))))));
  }

  window.CategoryMappingPage = CategoryMappingPage;
})();
