import { useMemo, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { accounting } from "@/lib/accountingClient";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { Button } from "@/components/ui/button";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import { CheckCircle2, AlertTriangle, FileDown, Download } from "lucide-react";
import { fmtDate } from "../lib/format";
import jsPDF from "jspdf";
import autoTable from "jspdf-autotable";
import { ReportSheet } from "./ReportSheet";
import { loadBrandedLogo, drawBrandedHeader, drawBrandedFooter } from "../lib/reportHeader";

type Account = {
  id: string;
  name: string;
  code: string | null;
  type: "asset" | "liability" | "equity" | "income" | "expense";
  subtype: string | null;
  balance: number;
};

const TYPE_ORDER: Account["type"][] = ["asset", "liability", "equity", "income", "expense"];
const TYPE_LABEL: Record<Account["type"], string> = {
  asset: "Assets",
  liability: "Liabilities",
  equity: "Equity",
  income: "Income",
  expense: "Expenses",
};
const DEBIT_NATURAL: Account["type"][] = ["asset", "expense"];

const TEAL: [number, number, number] = [0, 137, 123];

function fmt(n: number): string {
  if (!n) return "";
  const abs = Math.abs(n).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
  return n < 0 ? `(${abs})` : abs;
}

function splitDebitCredit(a: Account): { debit: number; credit: number } {
  const bal = Number(a.balance || 0);
  const naturalDebit = DEBIT_NATURAL.includes(a.type);
  // Positive balance shown on the natural side; negative flips
  if (naturalDebit) {
    return bal >= 0 ? { debit: bal, credit: 0 } : { debit: 0, credit: -bal };
  }
  return bal >= 0 ? { debit: 0, credit: bal } : { debit: -bal, credit: 0 };
}

export function TrialBalanceReport({ companyId, companyName, logoUrl }: { companyId: string; companyName: string; logoUrl?: string | null }) {
  const [asOf, setAsOf] = useState(() => new Date().toISOString().slice(0, 10));
  const [basis, setBasis] = useState<"accrual" | "cash">("accrual");
  const [showZero, setShowZero] = useState(false);
  const [typeFilter, setTypeFilter] = useState<"all" | Account["type"]>("all");

  const { data: acctMeta = [], isLoading } = useQuery({
    queryKey: ["tb-accounts", companyId],
    enabled: !!companyId,
    queryFn: async () => {
      const { data } = await accounting
        .from("accounts")
        .select("id,name,code,type,subtype")
        .eq("company_id", companyId)
        .order("code", { ascending: true });
      return (data ?? []) as Omit<Account, "balance">[];
    },
  });

  // Account balances come from the general ledger as of the report date — the
  // single source shared with the P&L, Balance Sheet, and General Ledger report.
  const { data: glLines = [] } = useQuery({
    queryKey: ["tb-gl", companyId, asOf],
    enabled: !!companyId,
    queryFn: async () => {
      const { data } = await accounting
        .from("journal_entry_lines")
        .select("debit,credit,account_id,journal_entries!inner(company_id,date)")
        .eq("journal_entries.company_id", companyId)
        .lte("journal_entries.date", asOf);
      return data ?? [];
    },
  });

  const accounts = useMemo<Account[]>(() => {
    const net = new Map<string, number>(); // debit − credit
    for (const l of glLines as any[]) {
      net.set(l.account_id, (net.get(l.account_id) ?? 0) + Number(l.debit || 0) - Number(l.credit || 0));
    }
    return (acctMeta as any[]).map((a) => {
      const n = net.get(a.id) ?? 0;
      // store as the account's natural balance (positive on its normal side)
      const balance = DEBIT_NATURAL.includes(a.type) ? n : -n;
      return { ...a, balance } as Account;
    });
  }, [acctMeta, glLines]);

  const grouped = useMemo(() => {
    const filtered = accounts.filter((a) => typeFilter === "all" || a.type === typeFilter);
    const map: Record<Account["type"], Account[]> = {
      asset: [], liability: [], equity: [], income: [], expense: [],
    };
    for (const a of filtered) map[a.type].push(a);
    return map;
  }, [accounts, typeFilter]);

  const totals = useMemo(() => {
    let debit = 0, credit = 0;
    for (const a of accounts) {
      if (typeFilter !== "all" && a.type !== typeFilter) continue;
      const { debit: d, credit: c } = splitDebitCredit(a);
      debit += d; credit += c;
    }
    return { debit, credit };
  }, [accounts, typeFilter]);

  const diff = totals.debit - totals.credit;
  const inBalance = Math.abs(diff) < 0.005;

  const exportPDF = async () => {
    const doc = new jsPDF({ unit: "pt", format: "letter" });
    const W = doc.internal.pageSize.getWidth();
    const ML = 40;
    const logo = await loadBrandedLogo(logoUrl);
    const startY = drawBrandedHeader(doc, {
      logo, title: "Trial Balance",
      subtitle: `As of ${fmtDate(asOf)} · ${basis === "cash" ? "Cash" : "Accrual"} basis`,
      metaLines: [{ label: "Properties:", value: companyName }],
    });

    // Body rows
    const body: any[] = [];
    for (const t of TYPE_ORDER) {
      const rows = grouped[t];
      if (!rows.length) continue;
      const tDebit = rows.reduce((s, a) => s + splitDebitCredit(a).debit, 0);
      const tCredit = rows.reduce((s, a) => s + splitDebitCredit(a).credit, 0);
      body.push([{ content: TYPE_LABEL[t], colSpan: 2, styles: { fontStyle: "bold", fillColor: [232, 240, 240] } }, { content: fmt(tDebit), styles: { fontStyle: "bold", fillColor: [232, 240, 240], halign: "right" } }, { content: fmt(tCredit), styles: { fontStyle: "bold", fillColor: [232, 240, 240], halign: "right" } }]);
      for (const a of rows) {
        const { debit, credit } = splitDebitCredit(a);
        const zero = debit === 0 && credit === 0;
        if (zero && !showZero) continue;
        body.push([
          a.code ?? "",
          { content: "  " + a.name, styles: zero ? { textColor: [150, 150, 150], fontStyle: "italic" } : {} },
          { content: fmt(debit), styles: { halign: "right" } },
          { content: fmt(credit), styles: { halign: "right" } },
        ]);
      }
    }
    body.push([
      { content: "Total", colSpan: 2, styles: { fontStyle: "bold", fillColor: [240, 240, 240], lineWidth: { top: 1.5, bottom: 1.5 } } },
      { content: fmt(totals.debit), styles: { fontStyle: "bold", halign: "right", fillColor: [240, 240, 240], lineWidth: { top: 1.5, bottom: 1.5 } } },
      { content: fmt(totals.credit), styles: { fontStyle: "bold", halign: "right", fillColor: [240, 240, 240], lineWidth: { top: 1.5, bottom: 1.5 } } },
    ]);

    autoTable(doc, {
      startY,
      head: [["Code", "Account", "Debit", "Credit"]],
      body,
      styles: { fontSize: 9, cellPadding: 4 },
      headStyles: { fillColor: TEAL, textColor: 255 },
      columnStyles: { 0: { cellWidth: 60 }, 2: { halign: "right" }, 3: { halign: "right" } },
      margin: { left: ML, right: ML },
    });

    const finalY = (doc as any).lastAutoTable.finalY + 20;
    doc.setFontSize(10); doc.setFont("times", "italic");
    if (inBalance) {
      doc.setTextColor(0, 128, 0);
      doc.text(`Total Debits equal Total Credits — Trial Balance is in balance as of ${fmtDate(asOf)}`, W / 2, finalY, { align: "center" });
    } else {
      doc.setTextColor(185, 28, 28);
      doc.text(`Out of balance by ${fmt(Math.abs(diff))} — check for unposted journal entries or missing opening balances`, W / 2, finalY, { align: "center" });
    }

    drawBrandedFooter(doc);
    doc.save(`trial-balance-${asOf}.pdf`);
  };

  const exportCSV = () => {
    const lines = ["Code,Account,Debit,Credit"];
    for (const t of TYPE_ORDER) {
      const rows = grouped[t];
      if (!rows.length) continue;
      lines.push(`,"${TYPE_LABEL[t]}",,`);
      for (const a of rows) {
        const { debit, credit } = splitDebitCredit(a);
        if (debit === 0 && credit === 0 && !showZero) continue;
        lines.push([a.code ?? "", `"${a.name}"`, debit || "", credit || ""].join(","));
      }
    }
    lines.push(`,"Total",${totals.debit.toFixed(2)},${totals.credit.toFixed(2)}`);
    const blob = new Blob([lines.join("\n")], { type: "text/csv" });
    const a = document.createElement("a");
    a.href = URL.createObjectURL(blob);
    a.download = `trial-balance-${asOf}.csv`;
    a.click();
    URL.revokeObjectURL(a.href);
  };

  return (
    <div className="space-y-4">
      <Card>
        <CardContent className="flex flex-wrap items-end gap-4 py-4">
          <div>
            <Label className="text-xs text-muted-foreground">As of Date</Label>
            <Input type="date" value={asOf} onChange={(e) => setAsOf(e.target.value)} className="w-44 mt-1" />
          </div>
          <div>
            <Label className="text-xs text-muted-foreground">Basis</Label>
            <Select value={basis} onValueChange={(v) => setBasis(v as any)}>
              <SelectTrigger className="w-32 mt-1"><SelectValue /></SelectTrigger>
              <SelectContent>
                <SelectItem value="accrual">Accrual</SelectItem>
                <SelectItem value="cash">Cash</SelectItem>
              </SelectContent>
            </Select>
          </div>
          <div>
            <Label className="text-xs text-muted-foreground">Account Type</Label>
            <Select value={typeFilter} onValueChange={(v) => setTypeFilter(v as any)}>
              <SelectTrigger className="w-40 mt-1"><SelectValue /></SelectTrigger>
              <SelectContent>
                <SelectItem value="all">All</SelectItem>
                <SelectItem value="asset">Assets</SelectItem>
                <SelectItem value="liability">Liabilities</SelectItem>
                <SelectItem value="equity">Equity</SelectItem>
                <SelectItem value="income">Income</SelectItem>
                <SelectItem value="expense">Expenses</SelectItem>
              </SelectContent>
            </Select>
          </div>
          <div className="flex items-center gap-2 pb-1">
            <Switch id="zero" checked={showZero} onCheckedChange={setShowZero} />
            <Label htmlFor="zero" className="text-sm">Show zero balances</Label>
          </div>
          <div className="ml-auto flex gap-2">
            <Button variant="outline" onClick={exportCSV}><Download className="mr-1 h-4 w-4" /> CSV</Button>
            <Button onClick={exportPDF}><FileDown className="mr-1 h-4 w-4" /> PDF</Button>
          </div>
        </CardContent>
      </Card>

      {isLoading ? (
        <Card><CardContent className="p-8 text-center text-sm text-muted-foreground">Loading…</CardContent></Card>
      ) : (
        <ReportSheet
          title="Trial Balance"
          subtitle={`As of ${fmtDate(asOf)} · ${basis === "cash" ? "Cash" : "Accrual"} basis`}
          companyName={companyName}
          logoUrl={logoUrl}
        >
            <table className="w-full text-sm">
              <thead>
                <tr className="border-b text-xs uppercase tracking-wide text-muted-foreground">
                  <th className="px-3 py-2 text-left font-semibold w-24">Code</th>
                  <th className="px-3 py-2 text-left font-semibold">Account</th>
                  <th className="px-3 py-2 text-right font-semibold w-40">Debit</th>
                  <th className="px-3 py-2 text-right font-semibold w-40">Credit</th>
                </tr>
              </thead>
              <tbody>
                {TYPE_ORDER.map((t) => {
                  const rows = grouped[t];
                  if (!rows.length) return null;
                  const tDebit = rows.reduce((s, a) => s + splitDebitCredit(a).debit, 0);
                  const tCredit = rows.reduce((s, a) => s + splitDebitCredit(a).credit, 0);
                  return (
                    <>
                      <tr key={`h-${t}`} className="bg-muted/60 font-semibold">
                        <td className="px-3 py-2" colSpan={2}>{TYPE_LABEL[t]}</td>
                        <td className="px-3 py-2 text-right tabular-nums">{fmt(tDebit)}</td>
                        <td className="px-3 py-2 text-right tabular-nums">{fmt(tCredit)}</td>
                      </tr>
                      {rows.map((a) => {
                        const { debit, credit } = splitDebitCredit(a);
                        const zero = debit === 0 && credit === 0;
                        if (zero && !showZero) return null;
                        return (
                          <tr key={a.id} className={`border-b ${zero ? "text-muted-foreground italic" : ""}`}>
                            <td className="px-3 py-1.5 font-mono text-xs text-muted-foreground">{a.code ?? ""}</td>
                            <td className="px-3 py-1.5 pl-8">{a.name}</td>
                            <td className="px-3 py-1.5 text-right tabular-nums">{fmt(debit)}</td>
                            <td className="px-3 py-1.5 text-right tabular-nums">{fmt(credit)}</td>
                          </tr>
                        );
                      })}
                    </>
                  );
                })}
              </tbody>
              <tfoot>
                <tr className="border-t-2 border-b-2 border-primary font-bold text-base">
                  <td className="px-3 py-3" colSpan={2}>Total</td>
                  <td className="px-3 py-3 text-right tabular-nums">{fmt(totals.debit)}</td>
                  <td className="px-3 py-3 text-right tabular-nums">{fmt(totals.credit)}</td>
                </tr>
              </tfoot>
            </table>
        </ReportSheet>
      )}

      <Card>
        <CardContent className="py-4 flex items-center justify-center">
          {inBalance ? (
            <Badge className="bg-green-600 hover:bg-green-600 text-white text-sm px-4 py-1.5 gap-2">
              <CheckCircle2 className="h-4 w-4" /> In Balance
            </Badge>
          ) : (
            <Badge variant="destructive" className="text-sm px-4 py-1.5 gap-2">
              <AlertTriangle className="h-4 w-4" />
              Out of Balance by ${Math.abs(diff).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} — check for unposted journal entries or missing opening balances
            </Badge>
          )}
        </CardContent>
      </Card>
    </div>
  );
}
