// TaxIntelligencePage — window.TaxIntelligencePage
//
// Tax planning and compliance, computed from real GL data. Four tabs:
// Overview (estimated tax + year-end checklist), Deduction Finder,
// Tax Footprint (payroll/physical nexus), Documents.
//
// ─── DATA REALITY (read before changing anything) ────────────────────────────
//
// THE BOTTOM LINE IS ALREADY AFTER TAX. data-live.js SECTION_BUCKET maps the
// taxonomy sections `tax`, `interest`, `da` and `other_expense` all into `opex`.
// So plHistory.ebitda (= gp − opex) is book NET INCOME, already net of any tax
// expense the company booked, and data.net_income_ltm is the trailing-12 sum of
// that series. Estimating tax as `net_income × rate` therefore taxes an
// after-tax number. We add booked tax expense back to reach PRE-TAX income and
// estimate from that. Tax expense is recovered by summing data.txns whose
// canonical_category resolves to the `tax` taxonomy section.
//
// plHistory has NO `netIncome` key. Its keys are:
//   { labels, years, revenue, cogs, opex, gp, ebitda }   (data-live.js:141)
//
// TRANSACTIONS HAVE NO `description` FIELD. gl_transactions is selected as
// `id, posted_date, account_code, account_name, amount, memo, reference`
// (data-live.js:470). Text matching for the deduction finder must use
// account_name and memo. There is no `description`, no `category`, no `date`.
//
// CATEGORIES ARE EXACT TAXONOMY KEYS. Not 'rent' / 'utilities' / 'fixed_assets'
// but "Opex – Rent & Occupancy", "Opex – Utilities", "Property, Plant &
// Equipment". Resolve via PerduraTaxonomy.sectionForCategory(); a hand-written
// slug matches nothing and the rule silently never fires.
//
// SIGN CONVENTION. data.txns[].amount is signed in the income-statement
// convention: revenue / credits NEGATIVE, expenses / debits POSITIVE. Expense
// totals are therefore summed directly; we Math.abs() only for display safety.
//
// THERE IS NO CUSTOMER LOCATION DATA. No table in this schema carries a state,
// jurisdiction, postal code or ship-to address. `customers_master.region` is
// free text ("Midwest"), is a sales region rather than a tax jurisdiction, and
// is not even selected by data-live.js. Economic sales-tax nexus therefore
// CANNOT be computed and is rendered as an honest locked panel. What we DO have
// is payroll_runs (fed_tax / state_tax / fica) and employees.location, which
// evidence PHYSICAL nexus. Do not invent an economic-nexus screen from `region`.
//
// PROFILE. companyProfile is `select("*")` from `companies`. It has
// business_type / fiscal_year_start_month / holds_inventory / is_services.
// There is no `archetype` field and no `state` field.
//
// NOTHING HERE IS TAX ADVICE. Every estimate carries a disclaimer, every
// deduction names the IRS form, and no figure is shown that the ledger cannot
// support.
//
// Props: { data, companyProfile, scopedCompanyId, setPage }

(function () {
  const { useState, useMemo, useEffect, useCallback } = 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", greenSoft: "#DCFCE7", greenBorder: "#A7F3D0",
    amber: "#B45309", amberSoft: "#FEF3C7", amberBorder: "#FCD34D",
    red: "#DC2626", redSoft: "#FEE2E2", redBorder: "#FCA5A5",
    navy: "#0d2040",
  };
  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 pctStr = (v) => (v * 100).toFixed(1).replace(/\.0$/, "") + "%";

  const DISCLAIMER = "For planning purposes only — not tax advice. Consult a qualified tax professional.";

  // ── localStorage, scoped per company ───────────────────────────────────────
  const lsGet = (k, fallback) => { try { const v = localStorage.getItem(k); return v == null ? fallback : JSON.parse(v); } catch (e) { return fallback; } };
  const lsSet = (k, v) => { try { localStorage.setItem(k, JSON.stringify(v)); } catch (e) { /* quota / private mode */ } };

  function usePersisted(key, initial) {
    const [val, setVal] = useState(() => (key ? lsGet(key, initial) : initial));
    // Re-read when the key changes (company switch) rather than carrying the
    // previous company's checkboxes across.
    useEffect(() => { if (key) setVal(lsGet(key, initial)); /* eslint-disable-next-line */ }, [key]);
    const set = useCallback((v) => { setVal(v); if (key) lsSet(key, v); }, [key]);
    return [val, set];
  }

  // ── Taxonomy ───────────────────────────────────────────────────────────────
  function sectionOf(category, profile) {
    const tax = window.PerduraTaxonomy;
    if (!tax || !tax.sectionForCategory || !category) return null;
    try { return tax.sectionForCategory(category, profile) || null; } catch (e) { return null; }
  }

  // ── Date windows ───────────────────────────────────────────────────────────
  // US estimated tax runs on the calendar year regardless of the company's
  // fiscal_year_start_month, so YTD here is deliberately calendar YTD.
  function dateWindows(now) {
    const y = now.getFullYear();
    const ltmFrom = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate());
    return { year: y, ltmFrom, ytdFrom: new Date(y, 0, 1) };
  }

  const parseDate = (s) => { const d = new Date(s); return isNaN(d.getTime()) ? null : d; };

  // ── Core tax computation ───────────────────────────────────────────────────
  function computeTaxBasis(data, profile, now) {
    const txns = (data && data.txns) || [];
    const { ltmFrom, ytdFrom, year } = dateWindows(now);

    let taxExpenseLTM = 0, taxExpenseYTD = 0, taxTxnCount = 0;
    for (const t of txns) {
      if (sectionOf(t.canonical_category, profile) !== "tax") continue;
      const d = parseDate(t.posted_date);
      if (!d) continue;
      const amt = Number(t.amount) || 0;   // expenses positive
      if (d >= ltmFrom) { taxExpenseLTM += amt; taxTxnCount += 1; }
      if (d >= ytdFrom) { taxExpenseYTD += amt; }
    }

    // Book net income, trailing twelve months. data.net_income_ltm is the
    // trailing-12 sum of plHistory.ebitda; fall back to summing it ourselves.
    let netIncomeLTM = (data && typeof data.net_income_ltm === "number") ? data.net_income_ltm : null;
    if (netIncomeLTM == null) {
      const series = data && data.plHistory && Array.isArray(data.plHistory.ebitda) ? data.plHistory.ebitda : null;
      netIncomeLTM = series ? series.slice(-12).reduce((a, b) => a + (Number(b) || 0), 0) : null;
    }

    const hasIncome = typeof netIncomeLTM === "number" && isFinite(netIncomeLTM);
    // Add booked tax expense back out of the bottom line to reach pre-tax income.
    const pretaxLTM = hasIncome ? netIncomeLTM + taxExpenseLTM : null;

    // Effective rate the company actually booked. Only trustworthy when there is
    // both a positive pre-tax base and real tax expense on the books.
    let effectiveRate = null;
    if (pretaxLTM != null && pretaxLTM > 0 && taxExpenseLTM > 0) {
      const r = taxExpenseLTM / pretaxLTM;
      if (r > 0.01 && r < 0.6) effectiveRate = r;   // outside this band it's noise, not a rate
    }

    return {
      netIncomeLTM: hasIncome ? netIncomeLTM : null,
      pretaxLTM, taxExpenseLTM, taxExpenseYTD, taxTxnCount,
      effectiveRate, year, hasIncome,
    };
  }

  // Quarterly schedule. US estimated-tax periods are genuinely uneven
  // (Q2 is two months, Q3 is three) — this is not a typo.
  function quarterSchedule(year, now) {
    const Q = [
      { q: "Q1", period: "Jan 1 – Mar 31", dueLabel: "Apr 15", due: new Date(year, 3, 15) },
      { q: "Q2", period: "Apr 1 – May 31", dueLabel: "Jun 15", due: new Date(year, 5, 15) },
      { q: "Q3", period: "Jun 1 – Aug 31", dueLabel: "Sep 15", due: new Date(year, 8, 15) },
      { q: "Q4", period: "Sep 1 – Dec 31", dueLabel: "Jan 15, " + (year + 1), due: new Date(year + 1, 0, 15) },
    ];
    const DAY = 86400000;
    return Q.map((q) => {
      const days = Math.round((q.due - now) / DAY);
      let status = "upcoming";
      if (days < 0) status = "overdue";
      else if (days <= 30) status = "due_soon";
      return Object.assign({}, q, { daysUntil: days, status });
    });
  }

  // ── Deduction finder ───────────────────────────────────────────────────────
  // Every rule reads real transactions. `match` runs over account_name + memo
  // (there is no `description` field) or over the taxonomy section. A rule that
  // finds nothing is reported as "not detected", never as a fabricated amount.
  const textOf = (t) => ((t.account_name || "") + " " + (t.memo || "")).toLowerCase();

  const DEDUCTION_RULES = [
    {
      id: "meals",
      label: "Business Meals (50% deductible)",
      description: "Meals with clients or employees are 50% deductible.",
      form: "Schedule C / Form 1120",
      deductibleShare: 0.5,
      match: (t) => /\b(meal|meals|restaurant|dining|catering)\b/.test(textOf(t)),
      sections: ["opex", "other_op_exp"],
    },
    {
      id: "vehicle",
      label: "Vehicle & Mileage",
      description: "Business use of a vehicle is deductible. The deductible share depends on your business-use percentage, which the ledger cannot know — a mileage log is required.",
      form: "Schedule C / Form 4562",
      deductibleShare: null,   // cannot be computed from the GL
      match: (t) => /\b(fuel|gasoline|mileage|vehicle|auto expense|car expense)\b/.test(textOf(t)),
      sections: ["opex", "other_op_exp"],
    },
    {
      id: "health_insurance",
      label: "Self-Employed Health Insurance",
      description: "Health insurance premiums may be fully deductible for self-employed owners.",
      form: "Schedule 1",
      deductibleShare: 1,
      match: (t) => /\b(health insurance|medical insurance|health premium|medical premium)\b/.test(textOf(t)),
      sections: ["opex", "other_op_exp"],
    },
    {
      id: "home_office",
      label: "Home Office",
      description: "If you work from home, a portion of rent and utilities may be deductible. The deductible portion depends on the square footage used exclusively for business — not derivable from the ledger.",
      form: "Form 8829",
      deductibleShare: null,
      match: () => false,
      // Category-driven rather than text-driven.
      categories: ["Opex – Rent & Occupancy", "Opex – Utilities"],
    },
    {
      id: "section_179",
      label: "Section 179 Equipment",
      description: "Equipment placed in service this year may be deducted in full rather than depreciated, subject to the annual IRS limit and taxable-income cap.",
      form: "Form 4562",
      deductibleShare: null,
      match: () => false,
      categories: ["Property, Plant & Equipment"],
      ytdOnly: true,   // Section 179 applies to assets placed in service this tax year
    },
  ];

  function runDeductionFinder(data, profile, basis, now) {
    const txns = (data && data.txns) || [];
    const { ltmFrom, ytdFrom } = dateWindows(now);
    const out = [];

    for (const rule of DEDUCTION_RULES) {
      const from = rule.ytdOnly ? ytdFrom : ltmFrom;
      let total = 0, count = 0;
      const accounts = new Set();

      for (const t of txns) {
        const d = parseDate(t.posted_date);
        if (!d || d < from) continue;

        let hit = false;
        if (rule.categories) {
          hit = rule.categories.includes(t.canonical_category);
        } else {
          const sec = sectionOf(t.canonical_category, profile);
          if (rule.sections && !rule.sections.includes(sec)) continue;
          hit = rule.match(t);
        }
        if (!hit) continue;

        const amt = Number(t.amount) || 0;
        if (amt <= 0) continue;             // credits / reversals are not spend
        total += amt; count += 1;
        if (t.account_name) accounts.add(t.account_name);
      }

      if (count === 0) continue;
      out.push({
        id: rule.id, label: rule.label, description: rule.description, form: rule.form,
        found: total, count,
        accounts: Array.from(accounts).slice(0, 4),
        estimated: rule.deductibleShare != null ? total * rule.deductibleShare : null,
        deductibleShare: rule.deductibleShare,
        window: rule.ytdOnly ? "this calendar year" : "the last 12 months",
      });
    }

    // Retirement is an OPPORTUNITY rule: it fires on the ABSENCE of evidence.
    const hasRetirement = txns.some((t) => /\b(401k|401\(k\)|sep-ira|sep ira|simple ira|pension|retirement)\b/.test(textOf(t)));
    const opportunities = [];
    if (!hasRetirement) {
      opportunities.push({
        id: "retirement",
        label: "Retirement Plan (SEP-IRA / Solo 401(k))",
        description: "No retirement-plan contributions found in the ledger. Employer contributions of up to 25% of net self-employment income are deductible, subject to the annual IRS cap.",
        form: "Form 5498",
        // Only quote a number when we have a positive pre-tax base to quote it against.
        headroom: (basis.pretaxLTM != null && basis.pretaxLTM > 0) ? basis.pretaxLTM * 0.25 : null,
      });
    }

    // R&D credit keys off the company's own profile, not an `archetype` field
    // (which does not exist on companyProfile).
    const bt = String((profile && (profile.business_type || profile.industry)) || "").toLowerCase();
    if (/saas|software|technology|tech/.test(bt)) {
      opportunities.push({
        id: "research",
        label: "R&D Tax Credit",
        description: "Software development and product R&D may qualify for the research credit. Qualification depends on the four-part test applied to specific activities, not on industry alone.",
        form: "Form 6765",
        headroom: null,
      });
    }

    return { found: out, opportunities, hasRetirement };
  }

  // ── Tax footprint (physical nexus) ─────────────────────────────────────────
  function computeFootprint(data, now) {
    const dims = (data && data.dimensions) || {};
    const payroll = dims.payroll || [];
    const employees = dims.employees || [];
    const { ltmFrom } = dateWindows(now);

    let fed = 0, state = 0, fica = 0, gross = 0, rows = 0;
    for (const p of payroll) {
      const d = parseDate(p.pay_date);
      if (!d || d < ltmFrom) continue;
      fed += Number(p.fed_tax) || 0;
      state += Number(p.state_tax) || 0;
      fica += Number(p.fica) || 0;
      gross += Number(p.gross_pay) || 0;
      rows += 1;
    }

    // employees.location is free text as entered by the customer. It is NOT a
    // validated tax jurisdiction. We surface it as evidence, clearly labelled.
    const byLocation = new Map();
    let activeCount = 0, unlocated = 0;
    for (const e of employees) {
      if (e.status && String(e.status).toLowerCase() !== "active") continue;
      activeCount += 1;
      const loc = (e.location && String(e.location).trim()) || "";
      // Inactive staff are excluded entirely; only an ACTIVE employee with no
      // location is genuinely unlocated. Counting terminated staff here would
      // overstate how much of the footprint is unknown.
      if (!loc) { unlocated += 1; continue; }
      byLocation.set(loc, (byLocation.get(loc) || 0) + 1);
    }
    const locations = Array.from(byLocation.entries())
      .map(([location, headcount]) => ({ location, headcount }))
      .sort((a, b) => b.headcount - a.headcount || a.location.localeCompare(b.location));

    return {
      hasPayroll: rows > 0, fed, state, fica, gross, rows,
      locations, employeeCount: activeCount,
      unlocatedEmployees: unlocated,
    };
  }

  // ── Small presentational pieces ────────────────────────────────────────────
  function Disclaimer({ text, tone }) {
    const t = tone || "amber";
    const bg = t === "amber" ? C.amberSoft : C.accentSoft;
    const fg = t === "amber" ? C.amber : C.accent;
    const bd = t === "amber" ? C.amberBorder : "#C7D2FE";
    return h("div", { style: { background: bg, border: "1px solid " + bd, borderRadius: 8, padding: "10px 14px", fontSize: 11.5, color: fg, lineHeight: 1.5 } }, text || DISCLAIMER);
  }

  function LockedPanel({ title, why, need }) {
    return h("div", { style: Object.assign({}, CARD, { padding: 22, borderStyle: "dashed" }) },
      h("div", { style: { fontSize: 14, fontWeight: 700, color: C.text1, marginBottom: 6 } }, title),
      h("div", { style: { fontSize: 12.5, color: C.text2, lineHeight: 1.6, marginBottom: 10 } }, why),
      h("div", { style: { fontSize: 11.5, color: C.muted, background: C.bg, borderRadius: 7, padding: "9px 12px", lineHeight: 1.5 } },
        h("strong", { style: { color: C.text2 } }, "To unlock: "), need));
  }

  function Checklist({ items, storageKey, columns }) {
    const [checked, setChecked] = usePersisted(storageKey, {});
    const toggle = (id) => setChecked(Object.assign({}, checked, { [id]: !checked[id] }));
    const done = items.filter((i) => checked[i.id]).length;

    const groups = {};
    for (const i of items) { const g = i.group || ""; (groups[g] = groups[g] || []).push(i); }

    return h("div", null,
      h("div", { style: { fontSize: 11.5, color: C.muted, marginBottom: 10 } },
        done + " of " + items.length + " complete",
        h("span", { style: { display: "inline-block", width: 120, height: 5, background: C.border, borderRadius: 4, marginLeft: 10, verticalAlign: "middle", overflow: "hidden" } },
          h("span", { style: { display: "block", width: (items.length ? (done / items.length) * 100 : 0) + "%", height: "100%", background: C.green, transition: "width .3s ease" } }))),
      h("div", { style: { display: "grid", gridTemplateColumns: columns ? "repeat(auto-fit,minmax(280px,1fr))" : "1fr", gap: 14 } },
        Object.keys(groups).map((g) =>
          h("div", { key: g || "_" },
            g ? h("div", { style: { fontSize: 10.5, fontWeight: 700, color: C.muted, textTransform: "uppercase", letterSpacing: 0.5, marginBottom: 6 } }, g) : null,
            groups[g].map((item) =>
              h("label", { key: item.id, style: { display: "flex", alignItems: "center", gap: 10, padding: "8px 10px", borderRadius: 7, cursor: "pointer", background: checked[item.id] ? C.greenSoft : "transparent", border: "1px solid " + (checked[item.id] ? C.greenBorder : "transparent") } },
                h("input", { type: "checkbox", checked: !!checked[item.id], onChange: () => toggle(item.id), style: { width: 15, height: 15, cursor: "pointer", accentColor: C.green, flexShrink: 0 } }),
                h("span", { style: { flex: 1, fontSize: 12.5, color: checked[item.id] ? C.text2 : C.text1, textDecoration: checked[item.id] ? "line-through" : "none" } }, item.label),
                item.due ? h("span", { style: { fontSize: 10.5, color: C.muted, whiteSpace: "nowrap" } }, "Due " + item.due) : null,
                item.auto ? h("span", { style: { fontSize: 10, fontWeight: 700, color: C.green, whiteSpace: "nowrap" } }, "✓ we generate this") : null))))));
  }

  // ── Tab 1 — Overview ───────────────────────────────────────────────────────
  function TaxOverviewTab({ basis, rate, setRate, rateIsDerived, resetRate, companyId, now, setPage }) {
    const [paid, setPaid] = usePersisted(companyId ? "perdura.tax.qpaid." + companyId : null, {});

    if (!basis.hasIncome) {
      return h("div", { style: { display: "grid", gap: 16 } },
        h(LockedPanel, {
          title: "Estimated tax needs a bottom line",
          why: "We could not derive net income from the general ledger for this company, so there is nothing to estimate tax against. We will not show a placeholder number.",
          need: "General-ledger transactions mapped to revenue and expense categories. Check the Category Mapping page for unmapped accounts.",
        }),
        h(Disclaimer, null));
    }

    const annual = Math.max(0, (basis.pretaxLTM || 0) * rate);
    const quarterly = annual / 4;
    const schedule = quarterSchedule(basis.year, now);

    const STATUS = {
      paid:     { label: "Paid",      bg: C.greenSoft, fg: "#065f46", bd: C.greenBorder },
      overdue:  { label: "Overdue",   bg: C.redSoft,   fg: "#991b1b", bd: C.redBorder },
      due_soon: { label: "Due soon",  bg: C.amberSoft, fg: C.amber,   bd: C.amberBorder },
      upcoming: { label: "Upcoming",  bg: C.bg,        fg: C.muted,   bd: C.border },
    };

    const stat = (label, value, sub, color) =>
      h("div", { style: { background: "rgba(255,255,255,0.08)", borderRadius: 8, padding: "12px 15px" } },
        h("div", { style: { fontSize: 10.5, color: "rgba(255,255,255,0.5)", marginBottom: 3 } }, label),
        h("div", { style: { fontSize: 19, fontWeight: 800, color: color || "#fff", fontVariantNumeric: "tabular-nums" } }, value),
        sub ? h("div", { style: { fontSize: 10.5, color: "rgba(255,255,255,0.45)", marginTop: 2 } }, sub) : null);

    return h("div", { style: { display: "grid", gap: 16 } },

      // Hero summary
      h("div", { style: { background: "linear-gradient(135deg,#0d2040 0%,#1a3a5c 100%)", borderRadius: 14, padding: 24 } },
        h("div", { style: { fontSize: 17, fontWeight: 800, color: "#fff", marginBottom: 3 } }, "Estimated Tax Summary"),
        h("div", { style: { fontSize: 11.5, color: "rgba(255,255,255,0.55)", marginBottom: 16, lineHeight: 1.6 } },
          "Based on pre-tax income of " + money(basis.pretaxLTM) + " over the last 12 months, at a " + pctStr(rate) + " effective rate. ",
          h("span", { style: { color: "rgba(255,255,255,0.75)" } },
            "Pre-tax income is book net income (" + money(basis.netIncomeLTM) + ") with " + money(basis.taxExpenseLTM) + " of booked tax expense added back.")),

        h("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(170px,1fr))", gap: 11, marginBottom: 18 } },
          stat("Annual estimated tax", money(annual)),
          stat("Quarterly payment", money(quarterly), "annual ÷ 4"),
          stat("Tax booked YTD", money(basis.taxExpenseYTD), basis.taxTxnCount ? basis.taxTxnCount + " GL entries (LTM)" : "no tax accounts found", basis.taxExpenseYTD > 0 ? "#34d399" : "#fbbf24"),
          stat("Book net income (LTM)", money(basis.netIncomeLTM), "after tax expense")),

        // Rate slider
        h("div", { style: { background: "rgba(0,0,0,0.2)", borderRadius: 9, padding: "12px 15px" } },
          h("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8, flexWrap: "wrap", gap: 8 } },
            h("div", { style: { fontSize: 11.5, color: "rgba(255,255,255,0.65)" } },
              "Effective tax rate",
              rateIsDerived
                ? h("span", { style: { color: "#34d399", marginLeft: 8, fontSize: 10.5, fontWeight: 700 } }, "derived from your books")
                : h("span", { style: { color: "rgba(255,255,255,0.4)", marginLeft: 8, fontSize: 10.5 } }, "default — no tax accounts in the GL")),
            h("div", { style: { display: "flex", alignItems: "center", gap: 10 } },
              h("span", { style: { fontSize: 17, fontWeight: 800, color: "#fff", fontVariantNumeric: "tabular-nums", minWidth: 54, textAlign: "right" } }, pctStr(rate)),
              basis.effectiveRate != null
                ? h("button", { onClick: resetRate, style: { padding: "4px 9px", fontSize: 10.5, borderRadius: 6, border: "1px solid rgba(255,255,255,0.2)", background: "transparent", color: "rgba(255,255,255,0.7)", cursor: "pointer" } }, "Reset to " + pctStr(basis.effectiveRate))
                : null)),
          h("input", {
            type: "range", min: 0, max: 50, step: 0.5, value: Math.round(rate * 1000) / 10,
            onChange: (e) => setRate(Number(e.target.value) / 100),
            "aria-label": "Effective tax rate",
            style: { width: "100%", accentColor: "#00b894", cursor: "pointer" } }),
          h("div", { style: { display: "flex", justifyContent: "space-between", fontSize: 10, color: "rgba(255,255,255,0.35)", marginTop: 2 } },
            h("span", null, "0%"), h("span", null, "25%"), h("span", null, "50%")))),

      // Quarterly schedule
      h("div", { style: Object.assign({}, CARD, { padding: 18 }) },
        h("div", { style: { fontSize: 14, fontWeight: 800, color: C.text1, marginBottom: 3 } }, "Quarterly Payment Schedule"),
        h("div", { style: { fontSize: 11.5, color: C.muted, marginBottom: 14 } },
          "US estimated-tax periods are uneven by statute — Q2 covers two months, Q3 covers three. Tick a quarter once you have paid it."),
        h("div", { style: { display: "grid", gap: 7 } },
          schedule.map((q) => {
            const isPaid = !!paid[basis.year + ":" + q.q];
            const st = STATUS[isPaid ? "paid" : q.status];
            return h("div", { key: q.q, style: { display: "grid", gridTemplateColumns: "26px 60px 1fr 130px 110px 96px", alignItems: "center", gap: 10, padding: "11px 13px", borderRadius: 8, border: "1px solid " + st.bd, background: isPaid ? C.greenSoft : C.card } },
              h("input", { type: "checkbox", checked: isPaid, "aria-label": "Mark " + q.q + " paid",
                onChange: () => setPaid(Object.assign({}, paid, { [basis.year + ":" + q.q]: !isPaid })),
                style: { width: 15, height: 15, cursor: "pointer", accentColor: C.green } }),
              h("div", { style: { fontWeight: 800, color: C.text1, fontSize: 13 } }, q.q),
              h("div", { style: { fontSize: 11.5, color: C.muted } }, q.period),
              h("div", { style: { fontSize: 11.5, color: C.text2 } }, "Due " + q.dueLabel),
              h("div", { style: { fontSize: 13.5, fontWeight: 700, color: C.text1, fontVariantNumeric: "tabular-nums", textAlign: "right" } }, money(quarterly)),
              h("div", { style: { textAlign: "center" } },
                h("span", { style: { padding: "3px 9px", borderRadius: 20, fontSize: 10.5, fontWeight: 700, background: st.bg, color: st.fg, whiteSpace: "nowrap" } }, st.label)));
          }))),

      // Year-end checklist
      h("div", { style: Object.assign({}, CARD, { padding: 18 }) },
        h("div", { style: { fontSize: 14, fontWeight: 800, color: C.text1, marginBottom: 3 } }, "Year-End Tax Checklist"),
        h("div", { style: { fontSize: 11.5, color: C.muted, marginBottom: 14 } },
          "Deadlines shown for " + (basis.year + 1) + ", covering the " + basis.year + " tax year. Dates that fall on a weekend or holiday shift to the next business day."),
        h(Checklist, {
          storageKey: companyId ? "perdura.tax.checklist." + companyId : null,
          items: yearEndChecklist(basis.year + 1),
        })),

      h(Disclaimer, null));
  }

  function yearEndChecklist(filingYear) {
    return [
      { id: "w2_prep",       label: "Prepare W-2s for employees",                  due: "Jan 31, " + filingYear },
      { id: "1099_prep",     label: "Issue 1099-NECs to contractors paid $600+",   due: "Jan 31, " + filingYear },
      { id: "books_close",   label: "Close the books for the year",                due: "Jan 31, " + filingYear },
      { id: "depreciation",  label: "Calculate depreciation schedules",            due: "Mar 15, " + filingYear },
      { id: "entity_return", label: "File entity return (S-Corp / partnership)",   due: "Mar 15, " + filingYear },
      { id: "personal",      label: "File personal return",                        due: "Apr 15, " + filingYear },
      { id: "extension",     label: "File an extension if needed",                 due: "Apr 15, " + filingYear },
      { id: "q1_estimate",   label: "Make Q1 estimated tax payment",               due: "Apr 15, " + filingYear },
    ];
  }

  // ── Tab 2 — Deduction Finder ───────────────────────────────────────────────
  function DeductionFinderTab({ result, basis }) {
    const { found, opportunities } = result;

    return h("div", { style: { display: "grid", gap: 16 } },
      h("div", { style: Object.assign({}, CARD, { padding: 18 }) },
        h("div", { style: { fontSize: 14, fontWeight: 800, color: C.text1, marginBottom: 3 } },
          "Potential Deductions Identified" + (found.length ? " (" + found.length + ")" : "")),
        h("div", { style: { fontSize: 11.5, color: C.muted, marginBottom: 14, lineHeight: 1.6 } },
          "Every figure below is summed from real general-ledger transactions. Where the deductible share depends on facts the ledger does not hold — business-use percentage, square footage — we show what we found and say so, rather than estimating."),

        !found.length
          ? h("div", { style: { padding: 24, textAlign: "center", background: C.bg, borderRadius: 9, fontSize: 12.5, color: C.muted } },
              "No transactions matched the deduction patterns in the last 12 months. This does not mean you have no deductions — it means none are identifiable from account names and memos.")
          : h("div", { style: { display: "grid", gap: 10 } },
              found.map((d) =>
                h("div", { key: d.id, style: { border: "1px solid " + C.greenBorder, borderRadius: 9, padding: "14px 16px", background: "#FBFEFC" } },
                  h("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 14, flexWrap: "wrap" } },
                    h("div", { style: { flex: 1, minWidth: 240 } },
                      h("div", { style: { fontWeight: 700, color: C.text1, fontSize: 13.5, marginBottom: 3 } }, d.label),
                      h("div", { style: { fontSize: 11.5, color: C.text2, lineHeight: 1.6, marginBottom: 7 } }, d.description),
                      h("div", { style: { fontSize: 11.5, color: C.muted } },
                        "Found ", h("strong", { style: { color: C.text1 } }, money(d.found)),
                        " across " + d.count + " transaction" + (d.count === 1 ? "" : "s") + " in " + d.window + ".",
                        d.accounts.length ? h("span", null, " Accounts: " + d.accounts.join(", ") + ".") : null)),
                    h("div", { style: { textAlign: "right", minWidth: 150 } },
                      d.estimated != null
                        ? h("div", null,
                            h("div", { style: { fontSize: 10.5, color: C.muted } }, "Estimated deduction"),
                            h("div", { style: { fontSize: 18, fontWeight: 800, color: C.green, fontVariantNumeric: "tabular-nums" } }, money(d.estimated)),
                            h("div", { style: { fontSize: 10, color: C.muted } }, pctStr(d.deductibleShare) + " of total"))
                        : h("div", null,
                            h("div", { style: { fontSize: 10.5, color: C.muted } }, "Deductible amount"),
                            h("div", { style: { fontSize: 12.5, fontWeight: 700, color: C.amber, lineHeight: 1.4 } }, "Not derivable from the ledger"),
                            h("div", { style: { fontSize: 10, color: C.muted } }, "depends on business-use facts")),
                      h("div", { style: { fontSize: 10.5, color: C.accent, fontWeight: 600, marginTop: 6 } }, d.form))))))),

      opportunities.length
        ? h("div", { style: Object.assign({}, CARD, { padding: 18 }) },
            h("div", { style: { fontSize: 14, fontWeight: 800, color: C.text1, marginBottom: 3 } }, "Opportunities"),
            h("div", { style: { fontSize: 11.5, color: C.muted, marginBottom: 14 } },
              "Surfaced from the absence of evidence in the ledger, or from your business profile."),
            h("div", { style: { display: "grid", gap: 10 } },
              opportunities.map((o) =>
                h("div", { key: o.id, style: { border: "1px solid " + C.amberBorder, borderRadius: 9, padding: "14px 16px", background: "#FFFDF7" } },
                  h("div", { style: { fontWeight: 700, color: C.text1, fontSize: 13.5, marginBottom: 3 } }, o.label),
                  h("div", { style: { fontSize: 11.5, color: C.text2, lineHeight: 1.6 } }, o.description),
                  o.headroom != null
                    ? h("div", { style: { fontSize: 11.5, color: C.text2, marginTop: 7 } },
                        "25% of pre-tax income is ", h("strong", { style: { color: C.text1 } }, money(o.headroom)),
                        h("span", { style: { color: C.muted } }, " — an upper bound before the annual IRS cap and the net-self-employment-income adjustment."))
                    : null,
                  h("div", { style: { fontSize: 10.5, color: C.accent, fontWeight: 600, marginTop: 7 } }, o.form)))))
        : null,

      h(Disclaimer, { text: DISCLAIMER + " Deduction eligibility depends on facts and circumstances the general ledger does not record." }));
  }

  // ── Tab 3 — Tax Footprint ──────────────────────────────────────────────────
  function TaxFootprintTab({ footprint }) {
    const fp = footprint;

    const payrollPanel = fp.hasPayroll
      ? h("div", { style: Object.assign({}, CARD, { padding: 18 }) },
          h("div", { style: { fontSize: 14, fontWeight: 800, color: C.text1, marginBottom: 3 } }, "Payroll Tax Withheld (last 12 months)"),
          h("div", { style: { fontSize: 11.5, color: C.muted, marginBottom: 14 } },
            "Summed from " + fp.rows.toLocaleString() + " payroll records against " + money(fp.gross) + " of gross pay."),
          h("div", { style: { display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(150px,1fr))", gap: 11 } },
            [["Federal withheld", fp.fed], ["State withheld", fp.state], ["FICA", fp.fica]].map(([label, v]) =>
              h("div", { key: label, style: { background: C.bg, borderRadius: 8, padding: "12px 15px" } },
                h("div", { style: { fontSize: 10.5, color: C.muted, marginBottom: 3 } }, label),
                h("div", { style: { fontSize: 18, fontWeight: 800, color: C.text1, fontVariantNumeric: "tabular-nums" } }, money(v))))),
          fp.state > 0
            ? h("div", { style: { fontSize: 11.5, color: C.text2, marginTop: 12, lineHeight: 1.6 } },
                "State income tax is being withheld, which means you have payroll in at least one state. Payroll in a state generally creates ",
                h("strong", null, "physical nexus"), " there — an obligation that does not depend on sales volume.")
            : null)
      : h(LockedPanel, {
          title: "Payroll tax footprint",
          why: "No payroll records are loaded for this company, so we cannot show what was withheld or where you employ people.",
          need: "A payroll feed populating payroll_runs (gross pay, federal / state withholding, FICA).",
        });

    const locationPanel = fp.locations.length
      ? h("div", { style: Object.assign({}, CARD, { padding: 18 }) },
          h("div", { style: { fontSize: 14, fontWeight: 800, color: C.text1, marginBottom: 3 } }, "Employee Locations"),
          h("div", { style: { fontSize: 11.5, color: C.muted, marginBottom: 14, lineHeight: 1.6 } },
            "Active employees grouped by the location recorded on their employee record. ",
            h("strong", { style: { color: C.amber } }, "This field is free text as entered — it is not a validated tax jurisdiction."),
            " Treat it as a prompt to check, not as a determination."),
          h("div", { style: { display: "grid", gap: 6 } },
            fp.locations.map((l) =>
              h("div", { key: l.location, style: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "10px 13px", border: "1px solid " + C.border, borderRadius: 8 } },
                h("div", { style: { fontWeight: 600, color: C.text1, fontSize: 12.5 } }, l.location),
                h("div", { style: { fontSize: 11.5, color: C.muted } }, l.headcount + " employee" + (l.headcount === 1 ? "" : "s"))))),
          fp.unlocatedEmployees > 0
            ? h("div", { style: { fontSize: 11, color: C.muted, marginTop: 10 } },
                fp.unlocatedEmployees + " employee record" + (fp.unlocatedEmployees === 1 ? " has" : "s have") + " no location set and are excluded above.")
            : null)
      : null;

    return h("div", { style: { display: "grid", gap: 16 } },
      payrollPanel,
      locationPanel,

      // Economic nexus — honestly locked. Do not build this from customers_master.region.
      h(LockedPanel, {
        title: "Economic sales-tax nexus",
        why: "Economic nexus depends on revenue and transaction counts per destination state (commonly $100,000 or 200 transactions). Nothing in this platform's schema records where a customer is: there is no state, postal code, or ship-to address on invoices, orders, or the customer master. We will not infer a tax jurisdiction from the free-text sales region field, because a wrong nexus warning is worse than none.",
        need: "An invoice or order feed carrying a destination state or postal code — typically a sales-tax platform (Avalara, TaxJar) or an e-commerce connector with ship-to addresses.",
      }),

      h(Disclaimer, { text: "Nexus rules vary by state and change frequently. This is a screening prompt, not a determination. " + DISCLAIMER }));
  }

  // ── Tab 4 — Documents ──────────────────────────────────────────────────────
  const DOCUMENTS = [
    { id: "pl",            group: "Income",  label: "P&L statement",                        auto: true },
    { id: "1099_recv",     group: "Income",  label: "1099s received from clients" },
    { id: "bank_stmts",    group: "Income",  label: "Bank statements (January–December)" },
    { id: "processor",     group: "Income",  label: "PayPal / Stripe / Square annual summary" },

    { id: "receipts",      group: "Expenses", label: "Receipts for business expenses" },
    { id: "mileage",       group: "Expenses", label: "Mileage log (if claiming vehicle)" },
    { id: "home_office",   group: "Expenses", label: "Home office measurements (if claiming)" },
    { id: "equipment",     group: "Expenses", label: "Equipment purchase receipts" },

    { id: "payroll_sum",   group: "Payroll",  label: "Payroll summary from processor" },
    { id: "w2_issued",     group: "Payroll",  label: "W-2s issued to employees" },
    { id: "1099_issued",   group: "Payroll",  label: "1099s issued to contractors" },

    { id: "prior_return",  group: "Other",    label: "Prior year tax return" },
    { id: "estimates",     group: "Other",    label: "Estimated tax payment records" },
    { id: "loan_stmts",    group: "Other",    label: "Business loan statements (interest deduction)" },
  ];

  function DocumentsTab({ companyId, setPage }) {
    return h("div", { style: { display: "grid", gap: 16 } },
      h("div", { style: Object.assign({}, CARD, { padding: 18 }) },
        h("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 14, flexWrap: "wrap", marginBottom: 14 } },
          h("div", null,
            h("div", { style: { fontSize: 14, fontWeight: 800, color: C.text1, marginBottom: 3 } }, "Documents to Collect for Tax Time"),
            h("div", { style: { fontSize: 11.5, color: C.muted } }, "Progress is saved in this browser, per company.")),
          h("div", { style: { display: "flex", gap: 8 } },
            setPage ? h("button", { onClick: () => setPage("income_statement"),
              style: { padding: "8px 14px", fontSize: 12, fontWeight: 600, color: C.accent, background: C.accentSoft, border: "1px solid " + C.accent, borderRadius: 8, cursor: "pointer" } }, "→ Income Statement") : null,
            h("button", { onClick: () => window.print(),
              style: { padding: "8px 14px", fontSize: 12, fontWeight: 700, color: "#fff", background: C.green, border: "none", borderRadius: 8, cursor: "pointer" } }, "🖨 Print tax prep package"))),
        h(Checklist, { storageKey: companyId ? "perdura.tax.documents." + companyId : null, items: DOCUMENTS, columns: true })),

      h("div", { style: { fontSize: 11, color: C.muted, lineHeight: 1.6 } },
        "“Print tax prep package” opens your browser's print dialogue — choose “Save as PDF” to produce a file. There is no PDF generator bundled with this app; printing is how every other report here is exported."),

      h(Disclaimer, null));
  }

  // ── Page ───────────────────────────────────────────────────────────────────
  const TABS = [
    { key: "overview",   label: "Tax Overview" },
    { key: "deductions", label: "Deduction Finder" },
    { key: "footprint",  label: "Tax Footprint" },
    { key: "documents",  label: "Documents" },
  ];

  function TaxIntelligencePage(props) {
    const { data, companyProfile, scopedCompanyId, setPage } = props || {};
    const [tab, setTab] = useState("overview");

    // One clock for the whole render, so the quarterly schedule and the LTM
    // window cannot disagree with each other mid-render.
    const now = useMemo(() => new Date(), []);

    const basis = useMemo(() => computeTaxBasis(data, companyProfile, now), [data, companyProfile, now]);

    // Rate: derived from the books when the GL supports it, else 25%. The user's
    // slider overrides and persists; `null` in storage means "follow the books".
    const rateKey = scopedCompanyId ? "perdura.tax.rate." + scopedCompanyId : null;
    const [override, setOverride] = usePersisted(rateKey, null);
    const derived = basis.effectiveRate;
    const rate = (typeof override === "number" && isFinite(override)) ? override : (derived != null ? derived : 0.25);
    const rateIsDerived = !(typeof override === "number") && derived != null;

    const deductions = useMemo(() => runDeductionFinder(data, companyProfile, basis, now), [data, companyProfile, basis, now]);
    const footprint = useMemo(() => computeFootprint(data, now), [data, now]);

    const txns = (data && data.txns) || [];

    const hero = h("div", { className: "pa-hero", style: { borderRadius: 14, marginBottom: 18 } },
      h("div", { className: "pa-hero-eyebrow" }, "TAX & COMPLIANCE"),
      h("div", { className: "pa-hero-title" }, "Tax Intelligence"),
      h("div", { className: "pa-hero-subtitle" }, "Estimated tax, deductions and compliance — computed from your general ledger, never fabricated"));

    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 see tax estimates and deductions."));
    }

    const tabBar = h("div", { style: { display: "flex", gap: 4, marginBottom: 18, borderBottom: "1px solid " + C.border, flexWrap: "wrap" } },
      TABS.map((t) =>
        h("button", { key: t.key, onClick: () => setTab(t.key),
          style: { padding: "10px 16px", fontSize: 12.5, fontWeight: tab === t.key ? 700 : 600,
            color: tab === t.key ? C.accent : C.muted, background: "transparent", border: "none",
            borderBottom: "2px solid " + (tab === t.key ? C.accent : "transparent"),
            marginBottom: -1, cursor: "pointer" } }, t.label)));

    return h("div", { style: { background: C.bg, minHeight: "100%", padding: "4px 2px" } }, hero, tabBar,
      tab === "overview" ? h(TaxOverviewTab, {
        basis, rate, rateIsDerived, now, setPage,
        companyId: scopedCompanyId,
        setRate: setOverride,
        resetRate: () => setOverride(null),
      }) : null,
      tab === "deductions" ? h(DeductionFinderTab, { result: deductions, basis }) : null,
      tab === "footprint"  ? h(TaxFootprintTab, { footprint }) : null,
      tab === "documents"  ? h(DocumentsTab, { companyId: scopedCompanyId, setPage }) : null);
  }

  window.TaxIntelligencePage = TaxIntelligencePage;
})();
