import { useEffect, useMemo, useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import jsPDF from "jspdf";
import autoTable from "jspdf-autotable";
import { Building2, RefreshCw, Save, Upload, Download, FileText, Loader2, RotateCcw, ChevronLeft, ChevronRight } from "lucide-react";
import { toast } from "sonner";
import { useAssociation } from "@/contexts/AssociationContext";
import { supabase } from "@/integrations/supabase/client";
import { accounting } from "@/lib/accountingClient";
import { useCompanyId } from "@/pages/accounting/lib/useCompanyId";
import { money } from "@/pages/accounting/lib/format";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";

const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

// PostgREST caps responses at 1000 rows; page through all GL lines.
async function fetchAllGLLines(cid: string, from: string, to: string): Promise<any[]> {
  const PAGE = 1000;
  const out: any[] = [];
  for (let offset = 0; ; offset += PAGE) {
    const { data, error } = await accounting
      .from("journal_entry_lines")
      .select("id,debit,credit,account_id,journal_entries!inner(company_id,date)")
      .eq("journal_entries.company_id", cid)
      .gte("journal_entries.date", from)
      .lte("journal_entries.date", to)
      .order("id", { ascending: true })
      .range(offset, offset + PAGE - 1);
    if (error) throw error;
    const rows = (data ?? []) as any[];
    out.push(...rows);
    if (rows.length < PAGE) break;
  }
  return out;
}

const monthEnd = (y: number, m: number) => `${y}-${String(m).padStart(2, "0")}-${String(new Date(y, m, 0).getDate()).padStart(2, "0")}`;
const n2 = (s: string) => (s.trim() === "" ? null : Number(s));

export default function BudgetWorkbookPage() {
  const qc = useQueryClient();
  const { associations, selectedAssociation, setSelectedAssociation, loadingAssociations } =
    useAssociation() as any;
  const { companyId, associationId, loading: companyLoading, error: companyError } = useCompanyId();
  const cid = companyId ?? "";

  const now = new Date();
  const [fy, setFy] = useState(now.getFullYear());
  const [through, setThrough] = useState(now.getMonth() + 1); // 1-12

  // editable override maps (string; "" = no override / use computed)
  const [ytdOv, setYtdOv] = useState<Record<string, string>>({});
  const [infl, setInfl] = useState<Record<string, string>>({});
  const [projOv, setProjOv] = useState<Record<string, string>>({});
  const [unitOv, setUnitOv] = useState(""); // "" = use the live unit count
  const [saving, setSaving] = useState(false);
  const [pushing, setPushing] = useState(false);

  const sortedAssoc = useMemo(
    () => [...(associations ?? [])].sort((a: any, b: any) => a.name.localeCompare(b.name)),
    [associations],
  );

  // Step through every client in order so a budget can be built for each.
  const assocIdx = sortedAssoc.findIndex((a: any) => a.id === selectedAssociation?.id);
  const stepAssoc = (delta: number) => {
    if (sortedAssoc.length === 0) return;
    const next = assocIdx < 0 ? 0 : (assocIdx + delta + sortedAssoc.length) % sortedAssoc.length;
    setSelectedAssociation(sortedAssoc[next]);
  };

  const from = `${fy}-01-01`;
  const to = monthEnd(fy, through);

  const { data: accounts = [] } = useQuery({
    queryKey: ["wb-accounts", cid],
    enabled: !!cid,
    queryFn: async () =>
      (await accounting.from("accounts").select("id,name,code,type").eq("company_id", cid)
        .in("type", ["income", "expense"]).order("code", { nullsFirst: false })).data ?? [],
  });

  const { data: glLines = [], isFetching: glFetching } = useQuery({
    queryKey: ["wb-gl", cid, from, to],
    enabled: !!cid,
    queryFn: () => fetchAllGLLines(cid, from, to),
  });

  const { data: unitCount = 0 } = useQuery({
    queryKey: ["wb-units", associationId],
    enabled: !!associationId,
    queryFn: async () => {
      const { count } = await supabase.from("units").select("*", { count: "exact", head: true })
        .eq("association_id", associationId!);
      return count ?? 0;
    },
  });

  // Persisted workbook state
  const { data: workbook } = useQuery({
    queryKey: ["wb-state", cid, fy],
    enabled: !!cid,
    queryFn: async () => {
      const { data: head } = await accounting.from("budget_workbooks")
        .select("*").eq("company_id", cid).eq("fiscal_year", fy).maybeSingle();
      if (!head) return { head: null, lines: [] as any[] };
      const { data: lines } = await accounting.from("budget_workbook_lines")
        .select("*").eq("workbook_id", (head as any).id);
      return { head, lines: lines ?? [] };
    },
  });

  // Hydrate editable maps + through-month from persisted state
  useEffect(() => {
    if (!workbook) return;
    const y: Record<string, string> = {}, i: Record<string, string> = {}, p: Record<string, string> = {};
    for (const l of (workbook.lines as any[]) ?? []) {
      if (l.ytd_override != null) y[l.account_id] = String(l.ytd_override);
      if (l.inflation_pct != null) i[l.account_id] = String(l.inflation_pct);
      if (l.projected_override != null) p[l.account_id] = String(l.projected_override);
    }
    setYtdOv(y); setInfl(i); setProjOv(p);
    if ((workbook.head as any)?.through_month) setThrough((workbook.head as any).through_month);
    setUnitOv((workbook.head as any)?.unit_override != null ? String((workbook.head as any).unit_override) : "");
  }, [workbook]);

  // Computed YTD actual per account from the GL
  const ytdComputed = useMemo(() => {
    const typeById = new Map<string, string>((accounts as any[]).map((a) => [a.id, a.type]));
    const out: Record<string, number> = {};
    for (const l of glLines as any[]) {
      const t = typeById.get(l.account_id);
      if (!t) continue;
      const d = Number(l.debit) || 0, c = Number(l.credit) || 0;
      out[l.account_id] = (out[l.account_id] ?? 0) + (t === "expense" ? d - c : c - d);
    }
    return out;
  }, [glLines, accounts]);

  const N = Math.max(1, through);
  const ytdVal = (id: string) => (ytdOv[id]?.trim() ? Number(ytdOv[id]) : (ytdComputed[id] ?? 0));
  const avgVal = (id: string) => ytdVal(id) / N;
  const inflFrac = (id: string) => (Number(infl[id]) || 0) / 100;
  const compProj = (id: string) => avgVal(id) * 12 * (1 + inflFrac(id));
  const projVal = (id: string) => (projOv[id]?.trim() ? Number(projOv[id]) : compProj(id));

  const sections = useMemo(() => {
    const exp = (accounts as any[]).filter((a) => a.type === "expense");
    const inc = (accounts as any[]).filter((a) => a.type === "income");
    return { exp, inc };
  }, [accounts]);

  const sum = (rows: any[]) => rows.reduce((s, a) => s + projVal(a.id), 0);
  const expenseAnnual = sum(sections.exp);
  const incomeAnnual = sum(sections.inc);
  const monthly = expenseAnnual / 12;
  // Per-unit assessment rate = annual expenses / 12 / units. Units default to the
  // live association count but can be overridden (weighted/excluded units).
  const effUnits = unitOv.trim() ? Number(unitOv) : unitCount;
  const perUnit = effUnits > 0 ? monthly / effUnits : 0;

  const resetRow = (id: string) => {
    setYtdOv((m) => ({ ...m, [id]: "" }));
    setProjOv((m) => ({ ...m, [id]: "" }));
    setInfl((m) => ({ ...m, [id]: "" }));
  };

  const saveWorkbook = async () => {
    if (!cid) return;
    setSaving(true);
    try {
      const { data: head, error: hErr } = await accounting.from("budget_workbooks")
        .upsert({ company_id: cid, fiscal_year: fy, through_month: through, unit_override: unitOv.trim() ? Math.round(Number(unitOv)) : null, updated_at: new Date().toISOString() },
          { onConflict: "company_id,fiscal_year" })
        .select("id").single();
      if (hErr || !head) throw new Error(hErr?.message || "save failed");
      await accounting.from("budget_workbook_lines").delete().eq("workbook_id", (head as any).id);
      const rows = (accounts as any[])
        .map((a) => ({
          workbook_id: (head as any).id, account_id: a.id,
          ytd_override: n2(ytdOv[a.id] ?? ""), inflation_pct: n2(infl[a.id] ?? ""),
          projected_override: n2(projOv[a.id] ?? ""),
        }))
        .filter((r) => r.ytd_override != null || r.inflation_pct != null || r.projected_override != null);
      if (rows.length) {
        const { error } = await accounting.from("budget_workbook_lines").insert(rows);
        if (error) throw new Error(error.message);
      }
      toast.success("Workbook saved");
      qc.invalidateQueries({ queryKey: ["wb-state", cid, fy] });
    } catch (e: any) {
      toast.error(e.message || "Failed to save");
    } finally {
      setSaving(false);
    }
  };

  const pushToBudget = async () => {
    if (!cid) return;
    if (!confirm(`Push projected annual amounts into the FY${fy} budget? This replaces that budget's monthly figures.`)) return;
    setPushing(true);
    try {
      let { data: budget } = await accounting.from("budgets")
        .select("id").eq("company_id", cid).eq("fiscal_year", fy).eq("period_type", "monthly").maybeSingle();
      if (!budget) {
        const { data: created, error } = await accounting.from("budgets")
          .insert({ company_id: cid, name: `Budget Workbook FY${fy}`, fiscal_year: fy, period_type: "monthly", status: "draft" })
          .select("id").single();
        if (error || !created) throw new Error(error?.message || "Could not create budget");
        budget = created;
      }
      await accounting.from("budget_entries").delete().eq("budget_id", (budget as any).id);
      const rows: any[] = [];
      for (const a of accounts as any[]) {
        const per = projVal(a.id) / 12;
        if (Math.abs(per) < 0.005) continue;
        for (let i = 0; i < 12; i++) rows.push({ budget_id: (budget as any).id, account_id: a.id, period_index: i, amount: Number(per.toFixed(2)) });
      }
      if (rows.length) {
        const { error } = await accounting.from("budget_entries").insert(rows);
        if (error) throw new Error(error.message);
      }
      toast.success(`Pushed to FY${fy} budget`);
    } catch (e: any) {
      toast.error(e.message || "Failed to push");
    } finally {
      setPushing(false);
    }
  };

  const exportCsv = () => {
    const lines = [["Type", "Code", "Account", `YTD through ${MONTHS[through - 1]}`, "Monthly Avg", "Inflation %", "Projected Annual"].join(",")];
    for (const a of [...sections.inc, ...sections.exp]) {
      lines.push([a.type, a.code ?? "", `"${(a.name ?? "").replace(/"/g, '""')}"`,
        ytdVal(a.id).toFixed(2), avgVal(a.id).toFixed(2), infl[a.id] ?? "", projVal(a.id).toFixed(2)].join(","));
    }
    lines.push(["", "", "EXPENSE ANNUAL", "", "", "", expenseAnnual.toFixed(2)].join(","));
    lines.push(["", "", "PER UNIT / MONTH", "", "", "", perUnit.toFixed(2)].join(","));
    const blob = new Blob([lines.join("\n")], { type: "text/csv" });
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a"); link.href = url; link.download = `budget-workbook-FY${fy}.csv`; link.click();
    URL.revokeObjectURL(url);
  };

  const exportPdf = () => {
    const doc = new jsPDF({ orientation: "portrait", unit: "pt", format: "letter" });
    const pageW = doc.internal.pageSize.getWidth();
    const pageH = doc.internal.pageSize.getHeight();
    const margin = 50;
    const contentW = pageW - margin * 2;
    const assocName = selectedAssociation?.name ?? "Association";

    // Header bar
    doc.setFillColor(30, 41, 59);
    doc.rect(0, 0, pageW, 78, "F");
    doc.setTextColor(255, 255, 255);
    doc.setFontSize(20);
    doc.setFont("helvetica", "bold");
    doc.text("PROPOSED BUDGET", margin, 40);
    doc.setFontSize(11);
    doc.setFont("helvetica", "normal");
    doc.text(`${assocName} · Fiscal Year ${fy}`, margin, 60);

    let y = 100;

    const sectionTable = (title: string, rows: any[], total: number) => {
      autoTable(doc, {
        startY: y,
        head: [[title, "Monthly", "Annual"]],
        body: rows.map((a) => [
          `${a.code ? a.code + "  " : ""}${a.name}`, money(projVal(a.id) / 12), money(projVal(a.id)),
        ]),
        foot: [[`Total ${title}`, money(total / 12), money(total)]],
        theme: "grid",
        styles: { font: "helvetica", fontSize: 9, cellPadding: 5, textColor: 30, lineColor: [220, 220, 220], lineWidth: 0.5 },
        headStyles: { fillColor: [30, 41, 59], textColor: 255, fontStyle: "bold", halign: "left" },
        footStyles: { fillColor: [241, 245, 249], textColor: 30, fontStyle: "bold" },
        alternateRowStyles: { fillColor: [248, 250, 252] },
        columnStyles: { 0: { cellWidth: "auto" }, 1: { cellWidth: 95, halign: "right" }, 2: { cellWidth: 95, halign: "right" } },
        margin: { left: margin, right: margin },
      });
      y = (doc as any).lastAutoTable.finalY + 22;
    };

    if (sections.inc.length) sectionTable("Income", sections.inc, incomeAnnual);
    if (sections.exp.length) sectionTable("Expenses", sections.exp, expenseAnnual);

    // Keep the summary + assessment banner together on one page.
    if (y > pageH - 220) { doc.addPage(); y = margin; }

    const surplus = incomeAnnual - expenseAnnual;
    doc.setTextColor(100, 116, 139);
    doc.setFontSize(10);
    doc.setFont("helvetica", "bold");
    doc.text("SUMMARY", margin, y);
    y += 10;
    autoTable(doc, {
      startY: y,
      body: [
        ["Total Projected Income (annual)", money(incomeAnnual)],
        ["Total Projected Expenses (annual)", money(expenseAnnual)],
        ["Projected Surplus / (Deficit)", money(surplus)],
        ["Monthly Budget (expenses ÷ 12)", money(monthly)],
        [`Number of Units${unitOv.trim() && Number(unitOv) !== unitCount ? " (override)" : ""}`, String(effUnits)],
      ],
      theme: "plain",
      styles: { font: "helvetica", fontSize: 10, cellPadding: 3 },
      columnStyles: { 0: { cellWidth: contentW - 150 }, 1: { cellWidth: 150, halign: "right", fontStyle: "bold" } },
      margin: { left: margin, right: margin },
    });
    y = (doc as any).lastAutoTable.finalY + 16;

    // Headline: the proposed per-unit monthly assessment.
    doc.setFillColor(22, 101, 52);
    doc.roundedRect(margin, y, contentW, 44, 5, 5, "F");
    doc.setTextColor(255, 255, 255);
    doc.setFontSize(11);
    doc.setFont("helvetica", "normal");
    doc.text("Proposed Assessment — per unit / month", margin + 16, y + 19);
    doc.setFontSize(18);
    doc.setFont("helvetica", "bold");
    doc.text(effUnits > 0 ? money(perUnit) : "—", margin + contentW - 16, y + 28, { align: "right" });

    // Footer on every page
    const totalPages = (doc as any).internal.getNumberOfPages();
    for (let i = 1; i <= totalPages; i++) {
      doc.setPage(i);
      const footerY = pageH - 30;
      doc.setDrawColor(226, 232, 240);
      doc.setLineWidth(0.5);
      doc.line(margin, footerY, margin + contentW, footerY);
      doc.setTextColor(148, 163, 184);
      doc.setFontSize(8);
      doc.text(`Proposed budget — generated ${new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" })}`, margin, footerY + 14);
      doc.text(`Page ${i} of ${totalPages}`, margin + contentW - 60, footerY + 14);
    }

    doc.save(`Proposed-Budget-${assocName.replace(/[^a-z0-9]/gi, "_")}-FY${fy}.pdf`);
  };

  const renderSection = (title: string, rows: any[]) => (
    <Card>
      <CardHeader className="pb-2"><CardTitle className="text-base">{title}</CardTitle></CardHeader>
      <CardContent className="p-0 overflow-x-auto">
        <table className="w-full text-sm">
          <thead>
            <tr className="border-y text-xs uppercase tracking-wide text-muted-foreground">
              <th className="px-3 py-2 text-left font-semibold">Account</th>
              <th className="px-3 py-2 text-right font-semibold">YTD (thru {MONTHS[through - 1]})</th>
              <th className="px-3 py-2 text-right font-semibold">Monthly Avg (÷{N})</th>
              <th className="px-3 py-2 text-right font-semibold w-[110px]">Inflation %</th>
              <th className="px-3 py-2 text-right font-semibold">Projected Annual</th>
              <th className="px-3 py-2 w-[40px]"></th>
            </tr>
          </thead>
          <tbody>
            {rows.map((a) => {
              const overridden = (ytdOv[a.id]?.trim() || projOv[a.id]?.trim() || infl[a.id]?.trim());
              return (
                <tr key={a.id} className="border-b hover:bg-muted/30">
                  <td className="px-3 py-1.5">
                    {a.code && <span className="font-mono text-xs text-muted-foreground mr-2">{a.code}</span>}{a.name}
                  </td>
                  <td className="px-2 py-1 text-right">
                    <Input className="h-8 text-right tabular-nums" type="number" step="0.01"
                      value={ytdOv[a.id] ?? ""} placeholder={(ytdComputed[a.id] ?? 0).toFixed(2)}
                      onChange={(e) => setYtdOv((m) => ({ ...m, [a.id]: e.target.value }))} />
                  </td>
                  <td className="px-3 py-1.5 text-right tabular-nums text-muted-foreground">{money(avgVal(a.id))}</td>
                  <td className="px-2 py-1 text-right">
                    <Input className="h-8 text-right tabular-nums" type="number" step="0.1" placeholder="0"
                      value={infl[a.id] ?? ""} onChange={(e) => setInfl((m) => ({ ...m, [a.id]: e.target.value }))} />
                  </td>
                  <td className="px-2 py-1 text-right">
                    <Input className="h-8 text-right tabular-nums font-medium" type="number" step="0.01"
                      value={projOv[a.id] ?? ""} placeholder={compProj(a.id).toFixed(2)}
                      onChange={(e) => setProjOv((m) => ({ ...m, [a.id]: e.target.value }))} />
                  </td>
                  <td className="px-1 py-1 text-center">
                    {overridden ? (
                      <Button size="icon" variant="ghost" className="h-7 w-7 text-muted-foreground" title="Reset row to imported values"
                        onClick={() => resetRow(a.id)}><RotateCcw className="h-3.5 w-3.5" /></Button>
                    ) : null}
                  </td>
                </tr>
              );
            })}
            <tr className="border-t-2 border-primary font-semibold bg-muted/40">
              <td className="px-3 py-2">Total {title}</td>
              <td className="px-3 py-2 text-right tabular-nums">{money(rows.reduce((s, a) => s + ytdVal(a.id), 0))}</td>
              <td className="px-3 py-2 text-right tabular-nums">{money(rows.reduce((s, a) => s + avgVal(a.id), 0))}</td>
              <td></td>
              <td className="px-3 py-2 text-right tabular-nums">{money(sum(rows))}</td>
              <td></td>
            </tr>
          </tbody>
        </table>
      </CardContent>
    </Card>
  );

  return (
    <div className="space-y-4">
      <div className="flex flex-wrap items-end gap-3 justify-between">
        <div>
          <h1 className="text-2xl font-semibold">Budget Workbook</h1>
          <p className="text-sm text-muted-foreground mt-0.5">Build next year's budget from YTD actuals through a chosen month.</p>
        </div>
        <div className="flex items-end gap-2">
          <Button variant="outline" size="icon" className="h-10 w-10 shrink-0" title="Previous client"
            onClick={() => stepAssoc(-1)} disabled={sortedAssoc.length <= 1}>
            <ChevronLeft className="h-4 w-4" />
          </Button>
          <div className="flex flex-col">
            <Select value={selectedAssociation?.id ?? ""} onValueChange={(id) => setSelectedAssociation(sortedAssoc.find((a: any) => a.id === id) ?? null)}>
              <SelectTrigger className="w-[240px]">
                <Building2 className="h-4 w-4 mr-2 text-muted-foreground" />
                <SelectValue placeholder={loadingAssociations ? "Loading…" : "Select association"} />
              </SelectTrigger>
              <SelectContent>
                {sortedAssoc.map((a: any) => <SelectItem key={a.id} value={a.id}>{a.name}</SelectItem>)}
              </SelectContent>
            </Select>
            {sortedAssoc.length > 0 && assocIdx >= 0 && (
              <span className="text-[11px] text-muted-foreground text-center mt-1">Client {assocIdx + 1} of {sortedAssoc.length}</span>
            )}
          </div>
          <Button variant="outline" size="icon" className="h-10 w-10 shrink-0" title="Next client"
            onClick={() => stepAssoc(1)} disabled={sortedAssoc.length <= 1}>
            <ChevronRight className="h-4 w-4" />
          </Button>
        </div>
      </div>

      {!associationId ? (
        <p className="text-sm text-muted-foreground">Select an association to begin.</p>
      ) : companyLoading ? (
        <div className="flex justify-center py-12"><Loader2 className="h-6 w-6 animate-spin text-muted-foreground" /></div>
      ) : companyError || !companyId ? (
        <p className="text-sm text-muted-foreground py-12 text-center">{companyError || "Accounting is not set up for this association."}</p>
      ) : (
        <>
          <Card>
            <CardContent className="flex flex-wrap items-end gap-4 py-4">
              <div>
                <Label className="text-xs">Fiscal Year</Label>
                <Input type="number" className="h-9 w-[110px]" value={fy} onChange={(e) => setFy(Number(e.target.value) || fy)} />
              </div>
              <div>
                <Label className="text-xs">Through month</Label>
                <Select value={String(through)} onValueChange={(v) => setThrough(Number(v))}>
                  <SelectTrigger className="h-9 w-[130px]"><SelectValue /></SelectTrigger>
                  <SelectContent>
                    {MONTHS.map((m, i) => <SelectItem key={m} value={String(i + 1)}>{m} ({i + 1})</SelectItem>)}
                  </SelectContent>
                </Select>
              </div>
              <div>
                <Label className="text-xs"># Units (assessment)</Label>
                <Input type="number" step="1" min="0" className="h-9 w-[120px]"
                  value={unitOv} placeholder={String(unitCount)}
                  onChange={(e) => setUnitOv(e.target.value)} />
                {unitOv.trim() && Number(unitOv) !== unitCount && (
                  <button type="button" className="text-[11px] text-muted-foreground hover:underline mt-0.5"
                    onClick={() => setUnitOv("")}>Reset to {unitCount}</button>
                )}
              </div>
              <div className="ml-auto flex gap-2">
                <Button variant="outline" size="sm" onClick={() => qc.invalidateQueries({ queryKey: ["wb-gl", cid, from, to] })} disabled={glFetching}>
                  <RefreshCw className={`h-4 w-4 mr-1 ${glFetching ? "animate-spin" : ""}`} /> Refresh
                </Button>
                <Button variant="outline" size="sm" onClick={exportCsv}><Download className="h-4 w-4 mr-1" /> CSV</Button>
                <Button variant="outline" size="sm" onClick={exportPdf}><FileText className="h-4 w-4 mr-1" /> PDF</Button>
                <Button variant="outline" size="sm" onClick={saveWorkbook} disabled={saving}><Save className="h-4 w-4 mr-1" /> {saving ? "Saving…" : "Save"}</Button>
                <Button size="sm" onClick={pushToBudget} disabled={pushing}><Upload className="h-4 w-4 mr-1" /> {pushing ? "Pushing…" : "Push to Budget"}</Button>
              </div>
            </CardContent>
          </Card>

          {/* Summary */}
          <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
            <Card><CardContent className="p-4"><div className="text-xs text-muted-foreground uppercase tracking-wide">Annual Budget (Expenses)</div><div className="text-xl font-semibold mt-1 tabular-nums">{money(expenseAnnual)}</div></CardContent></Card>
            <Card><CardContent className="p-4"><div className="text-xs text-muted-foreground uppercase tracking-wide">Monthly (÷12)</div><div className="text-xl font-semibold mt-1 tabular-nums">{money(monthly)}</div></CardContent></Card>
            <Card><CardContent className="p-4"><div className="text-xs text-muted-foreground uppercase tracking-wide">Per Unit / Month ({effUnits} units{unitOv.trim() && Number(unitOv) !== unitCount ? " · override" : ""})</div><div className="text-xl font-semibold mt-1 tabular-nums">{effUnits > 0 ? money(perUnit) : "—"}</div></CardContent></Card>
            <Card><CardContent className="p-4"><div className="text-xs text-muted-foreground uppercase tracking-wide">Projected Surplus / (Deficit)</div><div className={`text-xl font-semibold mt-1 tabular-nums ${incomeAnnual - expenseAnnual < 0 ? "text-destructive" : "text-emerald-700"}`}>{money(incomeAnnual - expenseAnnual)}</div></CardContent></Card>
          </div>

          {sections.exp.length > 0 && renderSection("Expenses", sections.exp)}
          {sections.inc.length > 0 && renderSection("Income", sections.inc)}
        </>
      )}
    </div>
  );
}
