import { useEffect, 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 { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import { Checkbox } from "@/components/ui/checkbox";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from "@/components/ui/sheet";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Badge } from "@/components/ui/badge";
import { FileDown, Download, ChevronDown, ChevronsDownUp, ChevronsUpDown, AlertTriangle } from "lucide-react";
import { fmtDate } from "../lib/format";
import { cn } from "@/lib/utils";
import jsPDF from "jspdf";
import autoTable from "jspdf-autotable";
import { ReportSheet } from "./ReportSheet";
import { loadBrandedLogo, drawBrandedHeader, drawBrandedFooter } from "../lib/reportHeader";

const TEAL: [number, number, number] = [0, 137, 123];
const DEBIT_NATURAL = ["asset", "expense"];
const PAGE_SIZE = 50;

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 startOfYear() { return new Date(new Date().getFullYear(), 0, 1).toISOString().slice(0, 10); }
function today() { return new Date().toISOString().slice(0, 10); }

type Txn = {
  id: string; date: string; description: string | null; reference: string | null;
  type: "debit" | "credit"; amount: number; account_id: string; category: string | null;
  debit: number; credit: number; balance: number; abnormal?: boolean;
};

export function GeneralLedgerReport({ companyId, companyName, logoUrl, initialAccountId }: { companyId: string; companyName: string; logoUrl?: string | null; initialAccountId?: string | null }) {
  const [from, setFrom] = useState(startOfYear());
  const [to, setTo] = useState(today());
  const [selectedAccounts, setSelectedAccounts] = useState<string[]>(initialAccountId ? [initialAccountId] : []);

  // When opened via a report drill-down, focus the chosen account.
  useEffect(() => {
    if (initialAccountId) setSelectedAccounts([initialAccountId]);
  }, [initialAccountId]);
  const [basis, setBasis] = useState<"accrual" | "cash">("accrual");
  const [showOpening, setShowOpening] = useState(true);
  const [search, setSearch] = useState("");
  const [collapsed, setCollapsed] = useState<Record<string, boolean>>({});
  const [allCollapsed, setAllCollapsed] = useState(false);
  const [pageByAcct, setPageByAcct] = useState<Record<string, number>>({});
  const [drillTxn, setDrillTxn] = useState<Txn | null>(null);

  const { data: accounts = [] } = useQuery({
    queryKey: ["gl-accounts", companyId],
    enabled: !!companyId,
    queryFn: async () => (await accounting.from("accounts").select("id,name,code,type").eq("company_id", companyId).order("code")).data ?? [],
  });

  // General-ledger lines up to "to" (the single source — opening balances are
  // posted to the GL too). Lines before "from" roll into each account's opening.
  const { data: glLines = [] } = useQuery({
    queryKey: ["gl-lines", companyId, to],
    enabled: !!companyId,
    queryFn: async () => {
      const { data } = await accounting
        .from("journal_entry_lines")
        .select("id,debit,credit,description,account_id,journal_entries!inner(company_id,date,reference)")
        .eq("journal_entries.company_id", companyId)
        .lte("journal_entries.date", to);
      return data ?? [];
    },
  });

  const accountMap = useMemo(() => {
    const m = new Map<string, any>();
    for (const a of accounts as any[]) m.set(a.id, a);
    return m;
  }, [accounts]);

  const groups = useMemo(() => {
    const filterAccts = selectedAccounts.length > 0;
    const map: Record<string, { account: any; opening: number; entries: Txn[]; closing: number; totalD: number; totalC: number }> = {};
    // initialize from selected accounts list (so empty accounts still show when selected)
    const accountIds = filterAccts ? selectedAccounts : (accounts as any[]).map((a) => a.id);
    for (const id of accountIds) {
      const a = accountMap.get(id); if (!a) continue;
      map[id] = { account: a, opening: 0, entries: [], closing: 0, totalD: 0, totalC: 0 };
    }
    const searchLower = search.trim().toLowerCase();
    const rows = (glLines as any[])
      .map((l) => ({
        id: l.id,
        date: l.journal_entries?.date as string,
        description: l.description ?? null,
        reference: l.journal_entries?.reference ?? null,
        account_id: l.account_id,
        type: (Number(l.debit || 0) >= Number(l.credit || 0) ? "debit" : "credit") as "debit" | "credit",
        amount: Number(l.debit || 0) || Number(l.credit || 0),
        category: null as string | null,
        debit: Number(l.debit || 0),
        credit: Number(l.credit || 0),
        balance: 0,
      }))
      .sort((a, b) => (a.date ?? "").localeCompare(b.date ?? ""));
    for (const t of rows) {
      const g = map[t.account_id]; if (!g) continue;
      const a = g.account;
      const naturalDebit = DEBIT_NATURAL.includes(a.type);
      const delta = naturalDebit ? t.debit - t.credit : t.credit - t.debit;
      if (t.date < from) {
        g.opening += delta;
      } else {
        if (searchLower) {
          const hay = `${t.description ?? ""} ${t.reference ?? ""}`.toLowerCase();
          if (!hay.includes(searchLower)) continue;
        }
        g.entries.push({ ...t });
        g.totalD += t.debit; g.totalC += t.credit;
      }
    }
    for (const k of Object.keys(map)) {
      const g = map[k];
      let bal = g.opening;
      const naturalDebit = DEBIT_NATURAL.includes(g.account.type);
      g.entries = g.entries.map((t) => {
        bal += naturalDebit ? t.debit - t.credit : t.credit - t.debit;
        const abnormal = naturalDebit ? bal < 0 : bal < 0;
        return { ...t, balance: bal, abnormal };
      });
      g.closing = bal;
      // drop empty accounts only when not explicitly selected
      if (!filterAccts && g.entries.length === 0 && g.opening === 0) delete map[k];
    }
    return map;
  }, [glLines, accountMap, accounts, selectedAccounts, search, from]);

  const accountList = useMemo(() => Object.values(groups).sort((a, b) =>
    (a.account.code ?? "").localeCompare(b.account.code ?? "")
  ), [groups]);

  const isCollapsed = (id: string) => collapsed[id] ?? allCollapsed;
  const toggleAll = (val: boolean) => {
    setAllCollapsed(val);
    setCollapsed({});
  };
  const toggleOne = (id: string) => setCollapsed((c) => ({ ...c, [id]: !isCollapsed(id) }));

  const toggleAccount = (id: string) => setSelectedAccounts((s) => s.includes(id) ? s.filter((x) => x !== id) : [...s, id]);

  const exportPDF = async () => {
    const doc = new jsPDF({ unit: "pt", format: "letter" });
    const W = doc.internal.pageSize.getWidth();
    const H = doc.internal.pageSize.getHeight();
    const ML = 54;

    const logo = await loadBrandedLogo(logoUrl);
    let cursorY = drawBrandedHeader(doc, {
      logo, title: "General Ledger",
      subtitle: `${fmtDate(from)} – ${fmtDate(to)}  ·  ${basis === "accrual" ? "Accrual" : "Cash"} Basis`,
      metaLines: [{ label: "Properties:", value: companyName }],
    });

    // Table of contents (placeholder — page numbers filled after layout)
    const tocEntries: { name: string; page: number }[] = [];
    const showToc = selectedAccounts.length === 0 && accountList.length > 1;
    if (showToc) {
      doc.setFont("times", "bold"); doc.setFontSize(13); doc.setTextColor(20);
      doc.text("Accounts in this Report", ML, cursorY);
      cursorY += 12;
      autoTable(doc, {
        startY: cursorY,
        head: [["#", "Account", "Page"]],
        body: accountList.map((g, i) => [String(i + 1), `${g.account.code ?? ""} ${g.account.name}`, "—"]),
        styles: { fontSize: 9, cellPadding: 4 },
        headStyles: { fillColor: [232, 240, 240], textColor: 20 },
        margin: { left: ML, right: ML },
      });
      cursorY = (doc as any).lastAutoTable.finalY + 20;
      doc.addPage(); cursorY = 60;
    }

    for (const g of accountList) {
      tocEntries.push({ name: `${g.account.code ?? ""} ${g.account.name}`, page: doc.getCurrentPageInfo().pageNumber });
      const body: any[] = [];
      if (showOpening) {
        body.push([
          { content: fmtDate(from), styles: { fontStyle: "bold", fillColor: [245, 245, 245] } },
          { content: "", styles: { fillColor: [245, 245, 245] } },
          { content: "Opening Balance", styles: { fontStyle: "bold", fillColor: [245, 245, 245] } },
          { content: "", styles: { fillColor: [245, 245, 245] } },
          { content: "", styles: { fillColor: [245, 245, 245] } },
          { content: fmt(g.opening), styles: { fontStyle: "bold", halign: "right", fillColor: [245, 245, 245] } },
        ]);
      }
      for (const t of g.entries) {
        body.push([
          fmtDate(t.date),
          t.reference ?? "",
          t.description ?? "",
          { content: fmt(t.debit), styles: { halign: "right" } },
          { content: fmt(t.credit), styles: { halign: "right" } },
          { content: fmt(t.balance), styles: { halign: "right", textColor: t.abnormal ? [200, 100, 0] : [20, 20, 20] } },
        ]);
      }
      body.push([
        { content: fmtDate(to), styles: { fontStyle: "bold", fillColor: [232, 240, 240] } },
        { content: "", styles: { fillColor: [232, 240, 240] } },
        { content: "Closing Balance", styles: { fontStyle: "bold", fillColor: [232, 240, 240] } },
        { content: fmt(g.totalD), styles: { fontStyle: "bold", halign: "right", fillColor: [232, 240, 240] } },
        { content: fmt(g.totalC), styles: { fontStyle: "bold", halign: "right", fillColor: [232, 240, 240] } },
        { content: fmt(g.closing), styles: { fontStyle: "bold", halign: "right", fillColor: [232, 240, 240] } },
      ]);
      autoTable(doc, {
        startY: cursorY,
        head: [
          [{ content: `${g.account.code ?? ""} ${g.account.name}  ·  ${g.account.type.toUpperCase()}`, colSpan: 6, styles: { fillColor: TEAL, textColor: 255, halign: "left", fontStyle: "bold" } }],
          ["Date", "Reference", "Description", "Debit", "Credit", "Balance"],
        ],
        body,
        styles: { fontSize: 9, cellPadding: 4 },
        headStyles: { fillColor: [232, 240, 240], textColor: 20 },
        alternateRowStyles: { fillColor: [250, 250, 250] },
        margin: { left: ML, right: ML },
        showHead: "everyPage",
      });
      cursorY = (doc as any).lastAutoTable.finalY + 20;
      if (cursorY > H - 80) { doc.addPage(); cursorY = 60; }
    }

    // Re-render ToC page numbers if shown
    if (showToc && tocEntries.length) {
      doc.setPage(1);
      // Recompute roughly: we already added a page break after ToC; ToC starts after header at y≈165.
      // Re-draw the table fresh with correct pages.
      doc.setFillColor(255, 255, 255); doc.rect(ML - 4, 160, W - ML * 2 + 8, 250, "F");
      doc.setFont("times", "bold"); doc.setFontSize(13); doc.setTextColor(20);
      doc.text("Accounts in this Report", ML, 175);
      autoTable(doc, {
        startY: 185,
        head: [["#", "Account", "Page"]],
        body: tocEntries.map((e, i) => [String(i + 1), e.name, String(e.page)]),
        styles: { fontSize: 9, cellPadding: 4 },
        headStyles: { fillColor: [232, 240, 240], textColor: 20 },
        margin: { left: ML, right: ML },
      });
    }

    drawBrandedFooter(doc);
    doc.save(`general-ledger-${from}-to-${to}.pdf`);
  };

  const exportCSV = () => {
    const lines = ["Account Code,Account Name,Date,Type,Reference,Description,Debit,Credit,Balance"];
    for (const g of accountList) {
      const code = `"${g.account.code ?? ""}"`;
      const name = `"${(g.account.name ?? "").replace(/"/g, '""')}"`;
      if (showOpening) lines.push([code, name, from, "Opening", "", "Opening Balance", "", "", g.opening.toFixed(2)].join(","));
      for (const t of g.entries) {
        lines.push([
          code, name, t.date, `"${t.category ?? t.type}"`, `"${t.reference ?? ""}"`,
          `"${(t.description ?? "").replace(/"/g, '""')}"`, t.debit || "", t.credit || "", t.balance.toFixed(2),
        ].join(","));
      }
      lines.push([code, name, to, "Closing", "", "Closing Balance", g.totalD.toFixed(2), g.totalC.toFixed(2), g.closing.toFixed(2)].join(","));
    }
    const blob = new Blob([lines.join("\n")], { type: "text/csv" });
    const a = document.createElement("a");
    a.href = URL.createObjectURL(blob);
    a.download = `general-ledger-${from}-to-${to}.csv`;
    a.click();
    URL.revokeObjectURL(a.href);
  };

  const selectedLabel = selectedAccounts.length === 0
    ? "All accounts"
    : selectedAccounts.length === 1
      ? (accountMap.get(selectedAccounts[0])?.name ?? "1 account")
      : `${selectedAccounts.length} accounts`;

  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">From</Label>
            <Input type="date" value={from} onChange={(e) => setFrom(e.target.value)} className="w-40 mt-1" />
          </div>
          <div>
            <Label className="text-xs text-muted-foreground">To</Label>
            <Input type="date" value={to} onChange={(e) => setTo(e.target.value)} className="w-40 mt-1" />
          </div>
          <div>
            <Label className="text-xs text-muted-foreground">Accounts</Label>
            <Popover>
              <PopoverTrigger asChild>
                <Button variant="outline" className="w-56 mt-1 justify-between font-normal">
                  <span className="truncate">{selectedLabel}</span>
                  <ChevronDown className="h-4 w-4 opacity-50" />
                </Button>
              </PopoverTrigger>
              <PopoverContent className="w-72 p-0" align="start">
                <div className="flex items-center justify-between border-b px-3 py-2">
                  <span className="text-xs text-muted-foreground">{selectedAccounts.length} selected</span>
                  <Button variant="ghost" size="sm" onClick={() => setSelectedAccounts([])}>Clear</Button>
                </div>
                <ScrollArea className="h-72">
                  <div className="p-2">
                    {(accounts as any[]).map((a) => (
                      <label key={a.id} className="flex items-center gap-2 rounded px-2 py-1.5 hover:bg-muted cursor-pointer">
                        <Checkbox checked={selectedAccounts.includes(a.id)} onCheckedChange={() => toggleAccount(a.id)} />
                        <span className="text-sm flex-1 truncate">
                          {a.code ? <span className="text-muted-foreground mr-1">{a.code}</span> : null}{a.name}
                        </span>
                        <span className="text-xs text-muted-foreground capitalize">{a.type}</span>
                      </label>
                    ))}
                  </div>
                </ScrollArea>
              </PopoverContent>
            </Popover>
          </div>
          <div className="flex flex-col gap-1">
            <Label className="text-xs text-muted-foreground">Basis</Label>
            <div className="flex items-center gap-2 h-9 mt-1">
              <span className={cn("text-sm", basis === "accrual" && "font-semibold")}>Accrual</span>
              <Switch checked={basis === "cash"} onCheckedChange={(v) => setBasis(v ? "cash" : "accrual")} />
              <span className={cn("text-sm", basis === "cash" && "font-semibold")}>Cash</span>
            </div>
          </div>
          <div className="flex flex-col gap-1">
            <Label className="text-xs text-muted-foreground">Opening Row</Label>
            <div className="flex items-center h-9 mt-1">
              <Switch checked={showOpening} onCheckedChange={setShowOpening} />
            </div>
          </div>
          <div className="flex-1 min-w-[200px]">
            <Label className="text-xs text-muted-foreground">Search</Label>
            <Input value={search} onChange={(e) => setSearch(e.target.value)} placeholder="Description or reference…" className="w-full mt-1" />
          </div>
          <div className="flex gap-2">
            <Button variant="outline" size="sm" onClick={() => toggleAll(!allCollapsed)}>
              {allCollapsed ? <><ChevronsUpDown className="mr-1 h-4 w-4" />Expand All</> : <><ChevronsDownUp className="mr-1 h-4 w-4" />Collapse All</>}
            </Button>
            <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>

      <ReportSheet
        title="General Ledger"
        subtitle={`${fmtDate(from)} – ${fmtDate(to)} · ${basis === "accrual" ? "Accrual" : "Cash"} Basis`}
        companyName={companyName}
        logoUrl={logoUrl}
      >
      {accountList.length === 0 ? (
        <div className="py-12 text-center text-sm text-muted-foreground">No transactions in this period.</div>
      ) : (
        <div className="space-y-4">
        {accountList.map((g) => {
          const collapsedHere = isCollapsed(g.account.id);
          const page = pageByAcct[g.account.id] ?? 1;
          const totalRows = g.entries.length;
          const paginated = totalRows > PAGE_SIZE;
          const start = (page - 1) * PAGE_SIZE;
          const visible = paginated ? g.entries.slice(start, start + PAGE_SIZE) : g.entries;
          const totalPages = Math.max(1, Math.ceil(totalRows / PAGE_SIZE));
          return (
            <Card key={g.account.id} className="overflow-hidden">
              <CardContent className="p-0">
                <button
                  onClick={() => toggleOne(g.account.id)}
                  className="w-full bg-primary/10 border-l-4 border-primary px-4 py-2.5 flex items-center justify-between text-left"
                >
                  <div className="flex items-center gap-2">
                    <ChevronDown className={cn("h-4 w-4 transition-transform", collapsedHere && "-rotate-90")} />
                    <span className="font-semibold text-sm">
                      {g.account.code ? <span className="text-muted-foreground mr-2">{g.account.code}</span> : null}
                      {g.account.name}
                    </span>
                    <Badge variant="outline" className="capitalize text-xs">{g.account.type}</Badge>
                  </div>
                  <div className="text-sm tabular-nums font-semibold">
                    {fmt(g.closing) || "0.00"}
                  </div>
                </button>
                {!collapsedHere && (
                  <>
                    <table className="w-full text-sm">
                      <thead>
                        <tr className="border-b text-xs uppercase tracking-wide text-muted-foreground bg-muted/40">
                          <th className="px-3 py-2 text-left w-28">Date</th>
                          <th className="px-3 py-2 text-left w-28">Type</th>
                          <th className="px-3 py-2 text-left w-32">Reference</th>
                          <th className="px-3 py-2 text-left">Description</th>
                          <th className="px-3 py-2 text-right w-28">Debit</th>
                          <th className="px-3 py-2 text-right w-28">Credit</th>
                          <th className="px-3 py-2 text-right w-32">Balance</th>
                        </tr>
                      </thead>
                      <tbody>
                        {showOpening && (
                          <tr className="border-b bg-muted/30 font-medium">
                            <td className="px-3 py-1.5">{fmtDate(from)}</td>
                            <td className="px-3 py-1.5"></td>
                            <td className="px-3 py-1.5"></td>
                            <td className="px-3 py-1.5">Opening Balance</td>
                            <td colSpan={2}></td>
                            <td className="px-3 py-1.5 text-right tabular-nums">{fmt(g.opening) || "0.00"}</td>
                          </tr>
                        )}
                        {visible.map((t) => (
                          <tr key={t.id} className={cn("border-b hover:bg-muted/30", t.abnormal && "bg-amber-50 dark:bg-amber-950/30")}>
                            <td className="px-3 py-1.5">{fmtDate(t.date)}</td>
                            <td className="px-3 py-1.5 capitalize text-muted-foreground">{t.category ?? t.type}</td>
                            <td className="px-3 py-1.5">
                              {t.reference ? (
                                <button onClick={() => setDrillTxn(t)} className="text-primary hover:underline font-medium">
                                  {t.reference}
                                </button>
                              ) : (
                                <button onClick={() => setDrillTxn(t)} className="text-primary hover:underline">view</button>
                              )}
                            </td>
                            <td className="px-3 py-1.5">{t.description ?? ""}</td>
                            <td className="px-3 py-1.5 text-right tabular-nums">{fmt(t.debit)}</td>
                            <td className="px-3 py-1.5 text-right tabular-nums">{fmt(t.credit)}</td>
                            <td className={cn("px-3 py-1.5 text-right tabular-nums", t.abnormal && "text-amber-700 dark:text-amber-400 font-semibold")}>
                              {t.abnormal && <AlertTriangle className="inline h-3 w-3 mr-1" />}
                              {fmt(t.balance)}
                            </td>
                          </tr>
                        ))}
                        {totalRows === 0 && !showOpening && (
                          <tr><td colSpan={7} className="px-3 py-6 text-center text-muted-foreground text-sm">No transactions in period.</td></tr>
                        )}
                      </tbody>
                      <tfoot>
                        <tr className="border-t-2 border-primary font-semibold bg-primary/5">
                          <td className="px-3 py-2">{fmtDate(to)}</td>
                          <td className="px-3 py-2" colSpan={3}>Closing Balance</td>
                          <td className="px-3 py-2 text-right tabular-nums">{fmt(g.totalD)}</td>
                          <td className="px-3 py-2 text-right tabular-nums">{fmt(g.totalC)}</td>
                          <td className="px-3 py-2 text-right tabular-nums">{fmt(g.closing) || "0.00"}</td>
                        </tr>
                      </tfoot>
                    </table>
                    {paginated && (
                      <div className="flex items-center justify-between border-t px-4 py-2 text-xs text-muted-foreground">
                        <span>Showing {start + 1}–{Math.min(start + PAGE_SIZE, totalRows)} of {totalRows} entries</span>
                        <div className="flex gap-2">
                          <Button variant="outline" size="sm" disabled={page <= 1} onClick={() => setPageByAcct((p) => ({ ...p, [g.account.id]: page - 1 }))}>Previous</Button>
                          <span className="self-center">Page {page} of {totalPages}</span>
                          <Button variant="outline" size="sm" disabled={page >= totalPages} onClick={() => setPageByAcct((p) => ({ ...p, [g.account.id]: page + 1 }))}>Next</Button>
                        </div>
                      </div>
                    )}
                  </>
                )}
              </CardContent>
            </Card>
          );
        })}
        </div>
      )}
      </ReportSheet>

      <Sheet open={!!drillTxn} onOpenChange={(o) => !o && setDrillTxn(null)}>
        <SheetContent className="sm:max-w-md">
          {drillTxn && (
            <>
              <SheetHeader>
                <SheetTitle>{drillTxn.reference || "Transaction"}</SheetTitle>
                <SheetDescription>{accountMap.get(drillTxn.account_id)?.name ?? ""}</SheetDescription>
              </SheetHeader>
              <div className="mt-6 space-y-3 text-sm">
                <Row label="Date" value={fmtDate(drillTxn.date)} />
                <Row label="Type" value={drillTxn.category ?? drillTxn.type} />
                <Row label="Description" value={drillTxn.description ?? "—"} />
                <Row label="Debit" value={fmt(drillTxn.debit) || "—"} />
                <Row label="Credit" value={fmt(drillTxn.credit) || "—"} />
                <Row label="Running Balance" value={fmt(drillTxn.balance) || "0.00"} />
              </div>
              <div className="mt-8 flex gap-2">
                <Button className="flex-1" onClick={() => setDrillTxn(null)}>Edit Transaction</Button>
                <Button variant="outline" className="flex-1" onClick={() => setDrillTxn(null)}>Close</Button>
              </div>
            </>
          )}
        </SheetContent>
      </Sheet>
    </div>
  );
}

function Row({ label, value }: { label: string; value: string }) {
  return (
    <div className="flex justify-between border-b pb-2">
      <span className="text-muted-foreground">{label}</span>
      <span className="font-medium tabular-nums">{value}</span>
    </div>
  );
}
