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 { FileDown, Download } from "lucide-react";
import jsPDF from "jspdf";
import autoTable from "jspdf-autotable";
import { ReportSheet } from "./ReportSheet";
import { loadBrandedLogo, drawBrandedHeader, drawBrandedFooter } from "../lib/reportHeader";
import { num } from "../lib/ownerLedger";
import { fmtDate } from "../lib/format";

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

type GLAccount = { id: string; code: string | null; name: string; is_bank: boolean };

type GLLineRow = {
  code: string | null;
  name: string;
  description: string | null;
  amount: number;
};

type Disbursement = {
  jeId: string;
  date: string;
  checkNo: string;
  bank: string;
  description: string;
  invoiceDate: string | null;
  amount: number;
  lines: GLLineRow[];
};

type VendorGroup = { vendor: string; entries: Disbursement[]; subtotal: number };

const NO_VENDOR = "No vendor on record";

function monthStart() { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-01`; }
function today() { return new Date().toISOString().slice(0, 10); }

const acctLabel = (a: GLAccount | undefined) => (a ? `${a.code ? a.code + " - " : ""}${a.name}` : "Unknown account");

/**
 * Buildium-style Cash Disbursement: every payment out of a bank account in the
 * period, grouped by bank account, with the GL expense breakdown under each.
 * Built from the GL (bank-account credit lines), so it works for both
 * platform-managed and Buildium-imported companies. Platform entries are
 * enriched with check #, vendor and bill info from the banking register.
 */
export function CashDisbursementReport({ companyId, companyName, logoUrl, from: propFrom, to: propTo }: { companyId: string; companyName: string; logoUrl?: string | null; from?: string; to?: string }) {
  const [from, setFrom] = useState(propFrom ?? monthStart());
  const [to, setTo] = useState(propTo ?? today());
  useEffect(() => { if (propFrom) setFrom(propFrom); if (propTo) setTo(propTo); }, [propFrom, propTo]);

  const { data, isLoading } = useQuery({
    queryKey: ["cash-disbursement", companyId, from, to],
    enabled: !!companyId,
    queryFn: async () => {
      const [accountsRes, linesRes] = await Promise.all([
        accounting.from("accounts").select("id,code,name,is_bank").eq("company_id", companyId),
        accounting
          .from("journal_entry_lines")
          .select("id,debit,credit,description,account_id,journal_entries!inner(id,company_id,date,description,reference,external_source,external_id)")
          .eq("journal_entries.company_id", companyId)
          .gte("journal_entries.date", from)
          .lte("journal_entries.date", to)
          .limit(50000),
      ]);
      const accounts = (accountsRes.data ?? []) as GLAccount[];
      const lines = (linesRes.data ?? []) as any[];
      const bankIds = new Set(accounts.filter((a) => a.is_bank).map((a) => a.id));

      // Enrich app-posted (acmacc_txn) entries with check #/vendor/bill from banking.
      const txnIds = [...new Set(lines
        .filter((l) => l.journal_entries?.external_source === "acmacc_txn" && l.journal_entries?.external_id)
        .map((l) => String(l.journal_entries.external_id)))];
      let txns: any[] = [];
      if (txnIds.length > 0) {
        const { data: t } = await accounting
          .from("transactions")
          .select("id,reference,description,vendor_id,bill_id,vendors(name),bills(number,issue_date,vendors(name))")
          .in("id", txnIds);
        txns = t ?? [];
      }

      // Buildium-GL (and any) payments carry their vendor/bill on the register
      // transaction linked to the GL bank line via journal_entry_line_id. Pull those
      // so imported disbursements show the matched vendor/bill, not "No vendor".
      const bankLineIds = lines.filter((l) => Number(l.credit || 0) > 0 && bankIds.has(l.account_id)).map((l) => String(l.id));
      const linkedTxns: any[] = [];
      for (let i = 0; i < bankLineIds.length; i += 200) {
        const { data: tl } = await accounting
          .from("transactions")
          .select("journal_entry_line_id,reference,description,vendor_id,bill_id,vendors(name),bills(number,issue_date,vendors(name))")
          .in("journal_entry_line_id", bankLineIds.slice(i, i + 200));
        if (tl) linkedTxns.push(...tl);
      }
      return { accounts, lines, txns, linkedTxns };
    },
  });

  const report = useMemo(() => {
    if (!data) return null;
    const acctById = new Map<string, GLAccount>();
    for (const a of data.accounts) acctById.set(a.id, a);
    const txnById = new Map<string, any>();
    for (const t of data.txns) txnById.set(String(t.id), t);
    const txnByLineId = new Map<string, any>();
    for (const t of (data.linkedTxns ?? [])) if (t.journal_entry_line_id) txnByLineId.set(String(t.journal_entry_line_id), t);

    // Group lines per journal entry
    const byJe = new Map<string, { je: any; lines: any[] }>();
    for (const l of data.lines) {
      const je = l.journal_entries;
      if (!je?.id) continue;
      const g = byJe.get(je.id) ?? { je, lines: [] };
      g.lines.push(l);
      byJe.set(je.id, g);
    }

    const groups = new Map<string, VendorGroup>();
    let grandTotal = 0;

    for (const { je, lines } of byJe.values()) {
      if (je.external_source === "acmacc_xfer") continue; // bank-to-bank transfers aren't disbursements

      const bankCredits = lines.filter((l) => Number(l.credit || 0) > 0 && acctById.get(l.account_id)?.is_bank);
      if (bankCredits.length === 0) continue;

      const nonBankDebits = lines.filter((l) => Number(l.debit || 0) > 0 && !acctById.get(l.account_id)?.is_bank);
      if (nonBankDebits.length === 0) continue; // pure transfer between banks

      const amount = bankCredits.reduce((s, l) => s + Number(l.credit || 0), 0);
      // Which bank account paid (largest credited bank line)
      const mainBank = bankCredits.reduce((a, b) => (Number(b.credit) > Number(a.credit) ? b : a));
      const bankLabel = acctLabel(acctById.get(mainBank.account_id));

      let txn = je.external_source === "acmacc_txn" && je.external_id ? txnById.get(String(je.external_id)) : null;
      // For imported (Buildium GL) payments, resolve the register txn linked to a bank line.
      if (!txn) { for (const bc of bankCredits) { const lt = txnByLineId.get(String(bc.id)); if (lt) { txn = lt; break; } } }
      const bill = txn?.bills ?? null;
      // Only attribute a vendor when it's reliably linked (payment→vendor or
      // payment→bill→vendor). GL-pull (Buildium) disbursements carry no vendor,
      // so they fall into the "No vendor on record" group rather than a guess.
      const vendor = bill?.vendors?.name || txn?.vendors?.name || NO_VENDOR;

      let checkNo = (txn?.reference || je.reference || "").toString().trim();
      const descSource = `${txn?.description ?? ""} ${je.description ?? ""}`.toLowerCase();
      if (!checkNo && /\bach\b|autopay|auto-pay|eft/.test(descSource)) checkNo = "ACH";

      const description = bill?.number
        ? `Inv # ${bill.number}`
        : (txn?.description || je.description || "—");

      const entry: Disbursement = {
        jeId: je.id,
        date: String(je.date).slice(0, 10),
        checkNo: checkNo || "—",
        bank: bankLabel,
        description,
        invoiceDate: bill?.issue_date ? String(bill.issue_date).slice(0, 10) : null,
        amount,
        lines: nonBankDebits.map((l) => ({
          code: acctById.get(l.account_id)?.code ?? null,
          name: acctById.get(l.account_id)?.name ?? "Unknown account",
          description: l.description ?? null,
          amount: Number(l.debit || 0),
        })),
      };

      const g = groups.get(vendor) ?? { vendor, entries: [], subtotal: 0 };
      g.entries.push(entry);
      g.subtotal += amount;
      groups.set(vendor, g);
      grandTotal += amount;
    }

    // Vendors alphabetical; the unattributed bucket always sorts last.
    const out = [...groups.values()].sort((a, b) => {
      if (a.vendor === NO_VENDOR) return 1;
      if (b.vendor === NO_VENDOR) return -1;
      return a.vendor.localeCompare(b.vendor);
    });
    for (const g of out) g.entries.sort((a, b) => a.date.localeCompare(b.date));
    return { groups: out, grandTotal };
  }, [data]);

  const rangeLabel = `${fmtDate(from)} – ${fmtDate(to)}`;

  const exportPDF = async () => {
    if (!report) return;
    const doc = new jsPDF({ unit: "pt", format: "letter", orientation: "landscape" });
    const ML = 40;
    const logo = await loadBrandedLogo(logoUrl);
    const startY = drawBrandedHeader(doc, {
      logo, title: "Cash Disbursement", subtitle: rangeLabel,
      metaLines: [{ label: "Properties:", value: companyName || "" }],
    });

    const body: any[] = [];
    for (const g of report.groups) {
      body.push([{
        content: g.vendor,
        colSpan: 6,
        styles: { fontStyle: "bold", textColor: TEAL, fontSize: 10, fillColor: [255, 255, 255] },
      }]);
      for (const e of g.entries) {
        body.push([
          { content: fmtDate(e.date), styles: { fontStyle: "bold" } },
          { content: e.checkNo, styles: { fontStyle: "bold" } },
          { content: e.bank, styles: { fontStyle: "bold" } },
          { content: e.description, styles: { fontStyle: "bold" } },
          { content: e.invoiceDate ? fmtDate(e.invoiceDate) : "", styles: { fontStyle: "bold", halign: "right" } },
          { content: num(e.amount), styles: { fontStyle: "bold", halign: "right" } },
        ]);
        for (const l of e.lines) {
          body.push([
            "",
            "",
            "",
            { content: `      ${l.code ? l.code + " - " : ""}${l.name}${l.description ? " - " + l.description : ""}`, styles: { textColor: [90, 90, 90] } },
            "",
            { content: num(l.amount), styles: { halign: "right", textColor: [90, 90, 90] } },
          ]);
        }
      }
      body.push([
        { content: `Total — ${g.vendor}`, colSpan: 5, styles: { fontStyle: "bold", halign: "right" } },
        { content: num(g.subtotal), styles: { fontStyle: "bold", halign: "right" } },
      ]);
    }
    body.push([
      { content: "Total Disbursements", colSpan: 5, styles: { fontStyle: "bold", halign: "right", fillColor: [237, 239, 242] } },
      { content: num(report.grandTotal), styles: { fontStyle: "bold", halign: "right", fillColor: [237, 239, 242] } },
    ]);

    autoTable(doc, {
      startY,
      head: [["Paid Date", "CheckNo", "Bank", "Description", "Invoice Date", "Amount"]],
      body,
      styles: { fontSize: 8, cellPadding: 3 },
      headStyles: { fillColor: TEAL, textColor: 255 },
      columnStyles: { 0: { cellWidth: 64 }, 1: { cellWidth: 64 }, 2: { cellWidth: 110 }, 4: { cellWidth: 70, halign: "right" }, 5: { cellWidth: 75, halign: "right" } },
      margin: { left: ML, right: ML },
    });

    drawBrandedFooter(doc);
    doc.save(`cash-disbursement-${from}-to-${to}.pdf`);
  };

  const exportCSV = () => {
    if (!report) return;
    const q = (s: string) => `"${String(s).replace(/"/g, '""')}"`;
    const f = (n: number) => n.toFixed(2);
    const lines = [["Vendor", "Paid Date", "CheckNo", "Bank Account", "Description", "GL Account", "Invoice Date", "Amount"].join(",")];
    for (const g of report.groups) {
      for (const e of g.entries) {
        lines.push([q(g.vendor), e.date, q(e.checkNo), q(e.bank), q(e.description), "", e.invoiceDate ?? "", f(e.amount)].join(","));
        for (const l of e.lines) {
          lines.push([q(g.vendor), "", "", "", "", q(`${l.code ? l.code + " - " : ""}${l.name}${l.description ? " - " + l.description : ""}`), "", f(l.amount)].join(","));
        }
      }
      lines.push([q(g.vendor), "", "", "", q(`Total — ${g.vendor}`), "", "", f(g.subtotal)].join(","));
    }
    lines.push(["", "", "", "", "Total Disbursements", "", "", f(report.grandTotal)].join(","));
    const blob = new Blob([lines.join("\n")], { type: "text/csv" });
    const a = document.createElement("a");
    a.href = URL.createObjectURL(blob);
    a.download = `cash-disbursement-${from}-to-${to}.csv`;
    a.click();
    URL.revokeObjectURL(a.href);
  };

  const hasData = !!report && report.groups.length > 0;

  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">Period</Label>
            <div className="mt-1 text-sm font-medium">{fmtDate(from)} – {fmtDate(to)}</div>
          </div>
          {hasData && (
            <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>
      ) : !hasData ? (
        <Card><CardContent className="p-8 text-center text-sm text-muted-foreground">No disbursements in this period.</CardContent></Card>
      ) : (
        <ReportSheet title="Cash Disbursement" subtitle={rangeLabel} companyName={companyName} logoUrl={logoUrl}>
          <div className="overflow-x-auto">
            <table className="w-full text-sm">
              <thead>
                <tr className="border-y text-[11px] uppercase tracking-wide text-muted-foreground">
                  <th className="px-3 py-2 text-left font-semibold w-24">Paid Date</th>
                  <th className="px-3 py-2 text-left font-semibold w-24">CheckNo</th>
                  <th className="px-3 py-2 text-left font-semibold w-44">Bank</th>
                  <th className="px-3 py-2 text-left font-semibold">Description</th>
                  <th className="px-3 py-2 text-right font-semibold w-28">Invoice Date</th>
                  <th className="px-3 py-2 text-right font-semibold w-28">Amount</th>
                </tr>
              </thead>
              <tbody>
                {report!.groups.map((g) => (
                  <>
                    <tr key={g.vendor}>
                      <td colSpan={6} className="px-3 pt-5 pb-2 text-[15px] font-semibold" style={{ color: `rgb(${TEAL.join(",")})` }}>
                        {g.vendor}
                      </td>
                    </tr>
                    {g.entries.map((e) => (
                      <>
                        <tr key={e.jeId} className="border-t">
                          <td className="px-3 py-1.5 font-medium whitespace-nowrap">{fmtDate(e.date)}</td>
                          <td className="px-3 py-1.5 font-medium">{e.checkNo}</td>
                          <td className="px-3 py-1.5">{e.bank}</td>
                          <td className="px-3 py-1.5 font-medium">{e.description}</td>
                          <td className="px-3 py-1.5 text-right whitespace-nowrap">{e.invoiceDate ? fmtDate(e.invoiceDate) : ""}</td>
                          <td className="px-3 py-1.5 text-right tabular-nums font-medium">{num(e.amount)}</td>
                        </tr>
                        {e.lines.map((l, li) => (
                          <tr key={`${e.jeId}-${li}`}>
                            <td />
                            <td />
                            <td />
                            <td className="px-3 py-0.5 pl-8 text-muted-foreground text-[13px]">
                              {l.code ? `${l.code} - ` : ""}{l.name}{l.description ? ` - ${l.description}` : ""}
                            </td>
                            <td />
                            <td className="px-3 py-0.5 text-right tabular-nums text-muted-foreground text-[13px]">{num(l.amount)}</td>
                          </tr>
                        ))}
                      </>
                    ))}
                    <tr className="border-t font-semibold">
                      <td colSpan={5} className="px-3 py-1.5 text-right">Total — {g.vendor}</td>
                      <td className="px-3 py-1.5 text-right tabular-nums">{num(g.subtotal)}</td>
                    </tr>
                  </>
                ))}
              </tbody>
              <tfoot>
                <tr className="border-t-2 border-b-2 font-bold">
                  <td colSpan={5} className="px-3 py-2 text-right">Total Disbursements</td>
                  <td className="px-3 py-2 text-right tabular-nums">{num(report!.grandTotal)}</td>
                </tr>
              </tfoot>
            </table>
          </div>
        </ReportSheet>
      )}
    </div>
  );
}
