import jsPDF from "jspdf";
import autoTable, { RowInput } from "jspdf-autotable";

type BudgetLine = {
  id: string;
  category: string;
  budgeted_amount: number | null;
  actual_amount: number | null;
  account_type: string;
  is_parent: boolean;
  parent_id: string | null;
};

type Association = {
  id: string;
  name: string;
  logo_url?: string | null;
};

const fmt = (n: number) =>
  n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });

async function loadImageAsDataURL(url: string): Promise<{ data: string; format: string } | null> {
  try {
    const res = await fetch(url, { mode: "cors" });
    const blob = await res.blob();
    const format = blob.type.includes("png") ? "PNG" : blob.type.includes("jpeg") || blob.type.includes("jpg") ? "JPEG" : "PNG";
    const data: string = await new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result as string);
      reader.onerror = reject;
      reader.readAsDataURL(blob);
    });
    return { data, format };
  } catch {
    return null;
  }
}

export async function generateBudgetReportPdf(
  association: Association,
  fiscalYear: number,
  lines: BudgetLine[],
) {
  const doc = new jsPDF({ unit: "pt", format: "letter" });
  const pageWidth = doc.internal.pageSize.getWidth();

  // Logo (top-left)
  if (association.logo_url) {
    const img = await loadImageAsDataURL(association.logo_url);
    if (img) {
      try {
        doc.addImage(img.data, img.format, 40, 30, 70, 70, undefined, "FAST");
      } catch {
        // ignore image errors
      }
    }
  }

  // Title block (centered)
  doc.setFont("helvetica", "bold");
  doc.setFontSize(13);
  doc.text(association.name, pageWidth / 2, 55, { align: "center" });
  doc.setFontSize(11);
  doc.text(`${fiscalYear} Budget`, pageWidth / 2, 73, { align: "center" });
  doc.setFont("helvetica", "normal");
  doc.setFontSize(10);
  doc.text(`Jan ${fiscalYear} - Dec ${fiscalYear}`, pageWidth / 2, 89, { align: "center" });

  // Build hierarchical rows
  const childrenByParent: Record<string, BudgetLine[]> = {};
  const roots: BudgetLine[] = [];
  lines.forEach((l) => {
    if (l.parent_id) (childrenByParent[l.parent_id] ||= []).push(l);
    else roots.push(l);
  });
  const filteredIds = new Set(lines.map((l) => l.id));
  Object.keys(childrenByParent).forEach((pid) => {
    if (!filteredIds.has(pid)) {
      childrenByParent[pid].forEach((c) => roots.push(c));
      delete childrenByParent[pid];
    }
  });

  const rollup = (id: string): number => {
    const kids = childrenByParent[id] || [];
    return kids.reduce(
      (sum, k) => sum + (k.is_parent ? rollup(k.id) : Number(k.budgeted_amount || 0)),
      0,
    );
  };

  const body: RowInput[] = [];
  const sectionStyle = { fontStyle: "bold" as const, fillColor: [240, 240, 240] as [number, number, number] };
  const totalStyle = { fontStyle: "bold" as const };

  const sortFn = (a: BudgetLine, b: BudgetLine) => {
    if (a.is_parent !== b.is_parent) return a.is_parent ? -1 : 1;
    return a.category.localeCompare(b.category);
  };

  const pushNode = (node: BudgetLine, depth: number) => {
    const kids = (childrenByParent[node.id] || []).slice().sort(sortFn);
    const amount = node.is_parent ? rollup(node.id) : Number(node.budgeted_amount || 0);
    const indent = "    ".repeat(depth);

    body.push([
      { content: `${indent}${node.category}`, styles: node.is_parent ? { fontStyle: "bold" } : {} },
      { content: fmt(amount), styles: { halign: "right", ...(node.is_parent ? { fontStyle: "bold" } : {}) } },
    ]);

    kids.forEach((k) => pushNode(k, depth + 1));

    if (node.is_parent && kids.length > 0) {
      body.push([
        { content: `${indent}Total for ${node.category}`, styles: totalStyle },
        { content: fmt(amount), styles: { halign: "right", ...totalStyle } },
      ]);
    }
  };

  // Profit and Loss section header
  body.push([{ content: "Profit and Loss", colSpan: 2, styles: sectionStyle }]);

  // Income
  const incomeRoots = roots.filter((r) => r.account_type === "income").sort(sortFn);
  const expenseRoots = roots.filter((r) => r.account_type !== "income").sort(sortFn);

  body.push([{ content: "Income", colSpan: 2, styles: { fontStyle: "bold" } }]);
  incomeRoots.forEach((r) => pushNode(r, 1));
  const totalIncome = incomeRoots.reduce(
    (s, r) => s + (r.is_parent ? rollup(r.id) : Number(r.budgeted_amount || 0)),
    0,
  );
  body.push([
    { content: "Total for Income", styles: totalStyle },
    { content: fmt(totalIncome), styles: { halign: "right", ...totalStyle } },
  ]);

  // Expenses
  body.push([{ content: "Expense", colSpan: 2, styles: { fontStyle: "bold" } }]);
  expenseRoots.forEach((r) => pushNode(r, 1));
  const totalExpense = expenseRoots.reduce(
    (s, r) => s + (r.is_parent ? rollup(r.id) : Number(r.budgeted_amount || 0)),
    0,
  );
  body.push([
    { content: "Total for Expense", styles: totalStyle },
    { content: fmt(totalExpense), styles: { halign: "right", ...totalStyle } },
  ]);

  // Net
  const net = totalIncome - totalExpense;
  body.push([
    { content: "Net Operating Income", styles: totalStyle },
    { content: fmt(net), styles: { halign: "right", ...totalStyle } },
  ]);

  autoTable(doc, {
    startY: 110,
    head: [
      [
        { content: "Account", styles: { halign: "left" } },
        { content: `Jan ${fiscalYear} - Dec ${fiscalYear}\nBudget`, styles: { halign: "right" } },
      ],
    ],
    body,
    theme: "plain",
    styles: { font: "helvetica", fontSize: 9, cellPadding: 4, lineColor: [220, 220, 220], lineWidth: 0.25 },
    headStyles: { fillColor: [235, 235, 235], textColor: [0, 0, 0], fontStyle: "bold", lineWidth: 0.25 },
    columnStyles: { 0: { cellWidth: "auto" }, 1: { cellWidth: 140, halign: "right" } },
    margin: { left: 40, right: 40 },
    didDrawPage: () => {
      const pageHeight = doc.internal.pageSize.getHeight();
      doc.setFontSize(8);
      doc.setTextColor(120);
      doc.text(
        `Generated ${new Date().toLocaleDateString("en-US")}`,
        40,
        pageHeight - 20,
      );
      doc.text(
        `Page ${doc.getNumberOfPages()}`,
        pageWidth - 40,
        pageHeight - 20,
        { align: "right" },
      );
      doc.setTextColor(0);
    },
  });

  doc.save(`${association.name.replace(/[^a-z0-9]+/gi, "_")}_${fiscalYear}_Budget.pdf`);
}
