import jsPDF from "jspdf";
import autoTable from "jspdf-autotable";
import { parseLocalDate } from "@/lib/dateUtils";

interface StatementOptions {
  unitData: any;
  owners: any[];
  entries: any[];
  associationName: string;
}

const formatCurrency = (val: number) => {
  if (val < 0) return `-$${Math.abs(val).toFixed(2)}`;
  return `$${val.toFixed(2)}`;
};

export function generateLedgerStatement({ unitData, owners, entries, associationName }: StatementOptions) {
  if (!entries || entries.length === 0) {
    throw new Error("No ledger entries to export.");
  }

  const doc = new jsPDF({ unit: "pt", format: "letter" });
  const m = 40;
  const pageW = doc.internal.pageSize.getWidth();
  const primaryOwner = owners.find((o: any) => o.is_primary) || owners[0];
  const ownerName = primaryOwner ? `${primaryOwner.first_name} ${primaryOwner.last_name}` : "Unassigned";
  const propertyAddress = unitData?.address || "N/A";

  // Sort entries by date ascending
  const sorted = [...entries].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());

  // Categorize and calculate totals + running balance
  const totals = { assess: 0, late: 0, admin: 0, legal: 0, viol: 0, interest: 0, bank: 0, payAR: 0 };
  let runningBalance = 0;

  // Running buckets for payment allocation waterfall
  const buckets = { bank: 0, interest: 0, late: 0, legal: 0, admin: 0, viol: 0, assess: 0 };

  // Map transaction_type values to statement columns
  const typeMap: Record<string, keyof typeof totals> = {
    assessment: "assess", assessments: "assess", "monthly assessment": "assess",
    special_assessment: "assess", dues: "assess",
    late_fee: "late", "late fee": "late", "late fees": "late", late: "late",
    admin_fee: "admin", "admin fee": "admin", "admin fees": "admin", admin: "admin", administrative: "admin",
    legal_fee: "legal", "legal fee": "legal", "legal fees": "legal", legal: "legal",
    violation: "viol", violations: "viol", fine: "viol", fines: "viol",
    interest: "interest",
    bank_fee: "bank", "bank fee": "bank", "bank fees": "bank", bank: "bank", "nsf fee": "bank",
  };

  // Payment allocation waterfall helper (Bank → Interest → Late → Legal → Admin → Viol → Assess)
  const allocatePayment = (amount: number) => {
    let remaining = amount;
    const alloc = { bank: 0, interest: 0, late: 0, legal: 0, admin: 0, viol: 0, assess: 0 };
    const order: (keyof typeof alloc)[] = ["bank", "interest", "late", "legal", "admin", "viol", "assess"];
    for (const key of order) {
      if (remaining <= 0) break;
      const applied = Math.min(remaining, Math.max(0, buckets[key]));
      alloc[key] = applied;
      buckets[key] -= applied;
      remaining -= applied;
    }
    // If overpayment, reduce assess (credit on account)
    if (remaining > 0) {
      alloc.assess += remaining;
      buckets.assess -= remaining;
    }
    return alloc;
  };

  const rows = sorted.map((e: any) => {
    const debit = Number(e.debit || 0);
    const credit = Number(e.credit || 0);
    const txType = (e.transaction_type || e.category || "").toLowerCase().trim();
    const catKey = typeMap[txType];

    // Determine column amounts
    let assess = 0, late = 0, admin = 0, legal = 0, viol = 0, interest = 0, bank = 0, payAR = 0;

    const isPayment = ["payment", "prepayment", "refund"].includes(txType);

    if (credit > 0 || isPayment) {
      const paymentAmount = credit || debit;
      payAR = -paymentAmount;
      // Allocate payment across buckets
      const alloc = allocatePayment(paymentAmount);
      // Show negative values in the respective columns for the allocation
      assess = -alloc.assess;
      late = -alloc.late;
      admin = -alloc.admin;
      legal = -alloc.legal;
      viol = -alloc.viol;
      interest = -alloc.interest;
      bank = -alloc.bank;
    } else if (catKey) {
      const amt = debit;
      switch (catKey) {
        case "assess": assess = amt; buckets.assess += amt; break;
        case "late": late = amt; buckets.late += amt; break;
        case "admin": admin = amt; buckets.admin += amt; break;
        case "legal": legal = amt; buckets.legal += amt; break;
        case "viol": viol = amt; buckets.viol += amt; break;
        case "interest": interest = amt; buckets.interest += amt; break;
        case "bank": bank = amt; buckets.bank += amt; break;
      }
    } else {
      assess = debit;
      buckets.assess += debit;
    }

    totals.assess += assess;
    totals.late += late;
    totals.admin += admin;
    totals.legal += legal;
    totals.viol += viol;
    totals.interest += interest;
    totals.bank += bank;
    totals.payAR += payAR;

    runningBalance += debit - credit;

    const fmtDate = e.date ? parseLocalDate(e.date).toLocaleDateString("en-US", { month: "2-digit", day: "2-digit", year: "numeric" }) : "-";

    return {
      date: fmtDate,
      description: e.description || "N/A",
      assess, late, admin, legal, viol, interest, bank, payAR,
      balance: runningBalance,
    };
  });

  const totalDue = runningBalance;

  // --- Header: left side ---
  doc.setFont("helvetica", "bold");
  doc.setFontSize(10);
  doc.text("ACCOUNT HOLDER:", m, m + 10);
  doc.setFont("helvetica", "normal");
  doc.text(ownerName, m, m + 22);

  doc.setFont("helvetica", "bold");
  doc.text("PROPERTY ADDRESS:", m, m + 42);
  doc.setFont("helvetica", "normal");
  doc.text(propertyAddress, m, m + 54);

  // --- Header: right side ---
  doc.setFont("helvetica", "bold");
  doc.setFontSize(18);
  doc.text("ACCOUNT STATEMENT", pageW - m, m + 10, { align: "right" });
  doc.setFont("helvetica", "normal");
  doc.setFontSize(9);
  const today = new Date();
  doc.text(`Statement Date: ${today.getMonth() + 1}/${today.getDate()}/${today.getFullYear()}`, pageW - m, m + 26, { align: "right" });
  doc.text(`Association: ${associationName || "N/A"}`, pageW - m, m + 38, { align: "right" });
  if (unitData?.unit_number) {
    doc.text(`Unit: ${unitData.unit_number}`, pageW - m, m + 50, { align: "right" });
  }

  const startY = m + 75;

  // --- Amounts Due Breakdown table (left) ---
  const breakdownData = [
    ["Assessments", formatCurrency(totals.assess)],
    ["Late Fees", formatCurrency(totals.late)],
    ["Admin Fees", formatCurrency(totals.admin)],
    ["Legal Fees", formatCurrency(totals.legal)],
    ["Violations", formatCurrency(totals.viol)],
    ["Interest", formatCurrency(totals.interest)],
    ["Bank Fees", formatCurrency(totals.bank)],
  ];

  autoTable(doc, {
    startY,
    head: [["Amounts Due Breakdown", "Amount"]],
    body: breakdownData,
    theme: "grid",
    styles: { fontSize: 8, cellPadding: 4 },
    headStyles: { fillColor: [0, 0, 0], textColor: 255, fontStyle: "bold" },
    columnStyles: { 1: { halign: "right" } },
    margin: { left: m, right: pageW / 2 + 20 },
    tableWidth: (pageW / 2) - m - 30,
  });

  // --- Account summary table (right) ---
  autoTable(doc, {
    startY,
    head: [["Account Number", "Due Date", "Total Due"]],
    body: [[unitData?.account_number || "N/A", "Upon Receipt", formatCurrency(totalDue)]],
    theme: "grid",
    styles: { fontSize: 8, cellPadding: 4 },
    headStyles: { fillColor: [0, 0, 0], textColor: 255, fontStyle: "bold" },
    columnStyles: { 2: { halign: "right", fontStyle: "bold" } },
    margin: { left: pageW / 2 + 20, right: m },
    tableWidth: (pageW / 2) - m - 30,
  });

  // --- Main ledger table ---
  const ledgerStartY = Math.max((doc as any).lastAutoTable?.finalY || startY + 120, startY + 120) + 25;
  const columns = ["Date", "Description", "Assess", "Late", "Admin", "Legal", "Viol", "Int", "Bank", "Pay (AR)", "Balance"];

  const bodyData = rows.map((r) => [
    r.date,
    r.description,
    formatCurrency(r.assess),
    formatCurrency(r.late),
    formatCurrency(r.admin),
    formatCurrency(r.legal),
    formatCurrency(r.viol),
    formatCurrency(r.interest),
    formatCurrency(r.bank),
    formatCurrency(r.payAR),
    formatCurrency(r.balance),
  ]);

  // Totals row — show remaining bucket balances (charges minus applied payments), total payments, and final balance
  bodyData.push([
    "TOTALS", "",
    formatCurrency(Math.max(0, buckets.assess)),
    formatCurrency(Math.max(0, buckets.late)),
    formatCurrency(Math.max(0, buckets.admin)),
    formatCurrency(Math.max(0, buckets.legal)),
    formatCurrency(Math.max(0, buckets.viol)),
    formatCurrency(Math.max(0, buckets.interest)),
    formatCurrency(Math.max(0, buckets.bank)),
    formatCurrency(totals.payAR),
    formatCurrency(totalDue),
  ]);

  autoTable(doc, {
    startY: ledgerStartY,
    head: [columns],
    body: bodyData,
    theme: "grid",
    styles: { fontSize: 7, cellPadding: 3 },
    headStyles: { fillColor: [0, 0, 0], textColor: 255, fontStyle: "bold" },
    columnStyles: {
      0: { cellWidth: 60 },
      1: { cellWidth: 90 },
      2: { halign: "right" },
      3: { halign: "right" },
      4: { halign: "right" },
      5: { halign: "right" },
      6: { halign: "right" },
      7: { halign: "right" },
      8: { halign: "right" },
      9: { halign: "right", textColor: [34, 139, 34] },
      10: { halign: "right", fontStyle: "bold" },
    },
    margin: { left: m, right: m },
    didParseCell: (data: any) => {
      if (data.row.index === bodyData.length - 1) {
        data.cell.styles.fontStyle = "bold";
      }
    },
  });

  // --- Footer ---
  const pageH = doc.internal.pageSize.getHeight();
  doc.setFont("helvetica", "normal");
  doc.setFontSize(7);
  doc.setTextColor(130, 130, 130);
  const footerText = `Generated by Avria Community Management • Page 1 of 1 • Printed on ${today.toLocaleDateString()} ${today.toLocaleTimeString()}`;
  doc.text(footerText, pageW / 2, pageH - 20, { align: "center" });

  // --- Save ---
  const unitNum = unitData?.unit_number || "NA";
  doc.save(`account-statement-unit-${unitNum}-${today.toISOString().split("T")[0]}.pdf`);
}
