import jsPDF from "jspdf";
import autoTable from "jspdf-autotable";
import { PDFDocument } from "pdf-lib";
import acmLogoFallback from "@/assets/acm-invoice-logo.png";

interface InvoiceItem {
  date: string;
  description: string;
  category: string;
  quantity: number;
  unitPrice: number;
  amount: number;
  isCredit: boolean;
  /** Property address attributed to this charge */
  address?: string;
  /** Vendor name attributed to this charge */
  vendor?: string;
  /** URL of the supporting receipt (PDF or image) for this charge */
  receiptUrl?: string;
}

interface InvoiceData {
  invoiceNumber: string;
  invoiceDate: string;
  dueDate: string;
  clientName: string;
  clientAddress?: string;
  clientCity?: string;
  clientState?: string;
  clientZip?: string;
  items: InvoiceItem[];
  companyName?: string;
  companyAddress?: string;
  companyEmail?: string;
  companyPhone?: string;
  companyWebsite?: string;
  logoUrl?: string;
  notes?: string;
  terms?: string;
}

const fmtCurrency = (v: number) =>
  new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(v);

const fmtDate = (d: string) => {
  if (!d) return "";
  const date = new Date(d + "T12:00:00");
  return `${String(date.getMonth() + 1).padStart(2, "0")}/${String(date.getDate()).padStart(2, "0")}/${date.getFullYear()}`;
};

async function loadImage(url: string): Promise<string | null> {
  try {
    const response = await fetch(url);
    const blob = await response.blob();
    return new Promise((resolve) => {
      const reader = new FileReader();
      reader.onloadend = () => resolve(reader.result as string);
      reader.onerror = () => resolve(null);
      reader.readAsDataURL(blob);
    });
  } catch {
    return null;
  }
}

export async function generateInvoicePdf(data: InvoiceData) {
  const doc = new jsPDF();
  const pageWidth = doc.internal.pageSize.getWidth();
  const pageHeight = doc.internal.pageSize.getHeight();
  const margin = 18;
  const rightEdge = pageWidth - margin;
  const companyName = data.companyName || "Avria Community Management, LLC";

  // ═══════════════════════════════════════════
  // HEADER SECTION — light gray background
  // ═══════════════════════════════════════════
  const headerHeight = 58;
  doc.setFillColor(245, 245, 245);
  doc.rect(0, 0, pageWidth, headerHeight, "F");

  // "INVOICE" title
  doc.setFontSize(16);
  doc.setFont("helvetica", "bold");
  doc.setTextColor(30, 30, 30);
  doc.text("INVOICE", margin, 18);

  // Company name (bold, under INVOICE)
  doc.setFontSize(8.5);
  doc.setFont("helvetica", "bold");
  doc.setTextColor(40, 40, 40);
  doc.text(companyName, margin, 26);

  // Company address lines
  doc.setFont("helvetica", "normal");
  doc.setFontSize(8);
  doc.setTextColor(80, 80, 80);
  let addrY = 31;
  if (data.companyAddress) {
    const addrLines = doc.splitTextToSize(data.companyAddress, 50);
    addrLines.forEach((line: string) => {
      doc.text(line, margin, addrY);
      addrY += 4;
    });
  }

  // Contact info column (middle)
  const midColX = 76;
  let contactY = 26;
  doc.setFontSize(8);
  doc.setTextColor(80, 80, 80);
  doc.setFont("helvetica", "normal");
  if (data.companyEmail) {
    doc.text(data.companyEmail, midColX, contactY);
    contactY += 4.5;
  }
  if (data.companyPhone) {
    doc.text(data.companyPhone, midColX, contactY);
    contactY += 4.5;
  }
  if (data.companyWebsite) {
    doc.text(data.companyWebsite, midColX, contactY);
  }

  // Logo (top-right corner) — use provided URL or bundled fallback
  const logoSrc = data.logoUrl || acmLogoFallback;
  {
    const logoData = logoSrc.startsWith("data:") ? logoSrc : await loadImage(logoSrc);
    if (logoData) {
      try {
        const img = new Image();
        img.src = logoData;
        await new Promise<void>((resolve) => {
          img.onload = () => resolve();
          img.onerror = () => resolve();
        });
        const ratio = img.naturalWidth / img.naturalHeight;
        let logoH = 30;
        let logoW = logoH * ratio;
        if (logoW > 60) { logoW = 60; logoH = logoW / ratio; }
        doc.addImage(logoData, "PNG", rightEdge - logoW, 10, logoW, logoH);
      } catch {
        // skip
      }
    }
  }

  // ═══════════════════════════════════════════
  // BILL TO SECTION
  // ═══════════════════════════════════════════
  let y = headerHeight + 14;

  doc.setFontSize(9);
  doc.setFont("helvetica", "bold");
  doc.setTextColor(40, 40, 40);
  doc.text("Bill to", margin, y);
  y += 6;

  doc.setFont("helvetica", "normal");
  doc.setFontSize(9);
  doc.setTextColor(50, 50, 50);
  doc.text(data.clientName, margin, y);
  y += 5;
  if (data.clientAddress) {
    doc.text(data.clientAddress, margin, y);
    y += 5;
  }
  const cityLine = [data.clientCity, data.clientState, data.clientZip].filter(Boolean).join(", ");
  if (cityLine) {
    doc.text(cityLine, margin, y);
    y += 5;
  }

  // Dotted separator
  y += 8;
  doc.setDrawColor(190, 190, 190);
  doc.setLineDashPattern([1, 1.5], 0);
  doc.setLineWidth(0.3);
  doc.line(margin, y, rightEdge, y);
  doc.setLineDashPattern([], 0);

  // ═══════════════════════════════════════════
  // INVOICE DETAILS SECTION
  // ═══════════════════════════════════════════
  y += 12;
  doc.setFontSize(10);
  doc.setFont("helvetica", "bold");
  doc.setTextColor(30, 30, 30);
  doc.text("Invoice details", margin, y);
  y += 7;

  doc.setFontSize(8.5);
  doc.setFont("helvetica", "normal");
  doc.setTextColor(60, 60, 60);
  doc.text(`Invoice no.: ${data.invoiceNumber}`, margin, y); y += 5;
  doc.text(`Terms: ${data.terms || "Due on receipt"}`, margin, y); y += 5;
  doc.text(`Invoice date: ${fmtDate(data.invoiceDate)}`, margin, y); y += 5;
  doc.text(`Due date: ${fmtDate(data.dueDate)}`, margin, y); y += 5;

  // Thin separator
  y += 4;
  doc.setDrawColor(220, 220, 220);
  doc.setLineWidth(0.3);
  doc.line(margin, y, rightEdge, y);

  // ═══════════════════════════════════════════
  // LINE ITEMS TABLE
  // ═══════════════════════════════════════════
  y += 6;

  // Group items by category for subcategorized display
  const categoryMap = new Map<string, InvoiceItem[]>();
  data.items.forEach((item) => {
    const cat = item.category || "Uncategorized";
    if (!categoryMap.has(cat)) categoryMap.set(cat, []);
    categoryMap.get(cat)!.push(item);
  });

  const tableBody: (string | { content: string; colSpan?: number; styles?: any })[][] = [];
  const annotationRows = new Map<number, string>(); // body row index -> annotation text
  let lineNum = 1;

  categoryMap.forEach((items, category) => {
    // Category header row
    tableBody.push([
      { content: category.toUpperCase(), colSpan: 6, styles: { fontStyle: "bold", fillColor: [240, 240, 240], textColor: [50, 50, 50], fontSize: 8 } },
    ]);

    // Item rows
    items.forEach((item) => {
      const annotations: string[] = [];
      if (item.address) annotations.push(item.address);
      if (item.vendor) annotations.push(item.vendor);

      tableBody.push([
        `${lineNum}.`,
        item.date || "",
        item.description || "",
        String(item.quantity),
        fmtCurrency(Math.abs(item.unitPrice)),
        (item.isCredit ? "-" : "") + fmtCurrency(Math.abs(item.amount)),
      ] as any);

      if (annotations.length > 0) {
        annotationRows.set(tableBody.length - 1, annotations.join(" • "));
      }
      lineNum++;
    });

    // Category subtotal row
    const catTotal = items.reduce((s, i) => s + (i.isCredit ? -Math.abs(i.amount) : Math.abs(i.amount)), 0);
    tableBody.push([
      { content: `${category} Subtotal`, colSpan: 5, styles: { fontStyle: "bold", fontSize: 8, halign: "right" as const } },
      { content: fmtCurrency(catTotal), styles: { fontStyle: "bold", fontSize: 8, halign: "right" as const } },
    ] as any);
  });

  // Description column width (must match columnStyles below) used for wrapping annotations
  const DESC_COL_WIDTH = pageWidth - margin * 2 - (10 + 22 + 14 + 24 + 26);
  const ANNOT_FONT_SIZE = 6.5;
  const ANNOT_LINE_HEIGHT = 2.6; // mm per wrapped line
  const ANNOT_TOP_GAP = 1.5;     // gap between description and first annotation line

  // Pre-compute wrapped annotation lines per row using a temp font setup
  const annotationLines = new Map<number, string[]>();
  doc.setFontSize(ANNOT_FONT_SIZE);
  doc.setFont("helvetica", "italic");
  annotationRows.forEach((text, rowIdx) => {
    const wrapped = doc.splitTextToSize(text, DESC_COL_WIDTH - 4) as string[];
    annotationLines.set(rowIdx, wrapped);
  });

  autoTable(doc, {
    startY: y,
    head: [["#", "Date", "Description", "Qty", "Rate", "Amount"]],
    body: tableBody as any,
    theme: "plain",
    headStyles: {
      fillColor: [255, 255, 255],
      textColor: [70, 70, 70],
      fontStyle: "normal",
      fontSize: 8,
      cellPadding: { top: 3, bottom: 3, left: 2, right: 2 },
    },
    bodyStyles: {
      fontSize: 8.5,
      textColor: [40, 40, 40],
      cellPadding: { top: 4, bottom: 4, left: 2, right: 2 },
    },
    columnStyles: {
      0: { cellWidth: 10, halign: "left" },
      1: { cellWidth: 22 },
      2: { cellWidth: "auto" },
      3: { cellWidth: 14, halign: "right" },
      4: { cellWidth: 24, halign: "right" },
      5: { cellWidth: 26, halign: "right" },
    },
    margin: { left: margin, right: margin },
    tableLineWidth: 0,
    didDrawPage: (hookData) => {
      if (hookData.table.head && hookData.table.head.length > 0) {
        const headRow = hookData.table.head[0];
        const firstCell = Object.values(headRow.cells)[0];
        if (firstCell) {
          const lineY = (firstCell as any).y + (headRow as any).height;
          doc.setDrawColor(200, 200, 200);
          doc.setLineWidth(0.3);
          doc.line(margin, lineY, rightEdge, lineY);
        }
      }
    },
    didDrawCell: (hookData) => {
      if (hookData.section === "body") {
        const lastCol = hookData.row.cells[5];
        if (hookData.column.index === 0 && lastCol) {
          doc.setDrawColor(230, 230, 230);
          doc.setLineWidth(0.15);
          doc.line(margin, hookData.cell.y + hookData.cell.height, rightEdge, hookData.cell.y + hookData.cell.height);
        }
        // Draw annotation sub-line(s) for description column with wrapping
        if (hookData.column.index === 2) {
          const lines = annotationLines.get(hookData.row.index);
          if (lines && lines.length > 0) {
            doc.setFontSize(ANNOT_FONT_SIZE);
            doc.setFont("helvetica", "italic");
            doc.setTextColor(120, 120, 120);
            const totalAnnotHeight = lines.length * ANNOT_LINE_HEIGHT;
            // Anchor block to bottom of cell
            const startY = hookData.cell.y + hookData.cell.height - totalAnnotHeight - 1;
            lines.forEach((ln, idx) => {
              doc.text(ln, hookData.cell.x + 4, startY + (idx + 1) * ANNOT_LINE_HEIGHT - 0.5);
            });
          }
        }
      }
    },
    didParseCell: (hookData) => {
      if (hookData.section === "body") {
        // Add bottom padding for rows with annotations, scaled to wrapped line count
        const lines = annotationLines.get(hookData.row.index);
        if (lines && lines.length > 0) {
          const extra = lines.length * ANNOT_LINE_HEIGHT + ANNOT_TOP_GAP + 4;
          hookData.cell.styles.cellPadding = { top: 4, bottom: extra, left: 2, right: 2 };
        }
        if (hookData.column.index === 5) {
          const val = hookData.cell.raw as string;
          if (typeof val === "string" && val.startsWith("-")) {
            hookData.cell.styles.textColor = [220, 38, 38];
          }
        }
      }
    },
  });

  const tableEndY = (doc as any).lastAutoTable?.finalY || y + 30;

  // ═══════════════════════════════════════════
  // TOTAL SECTION
  // ═══════════════════════════════════════════
  const sectionY = tableEndY + 10;

  // --- Total section (right side) ---
  const totalSectionX = rightEdge - 70;
  let totalY = sectionY;

  const charges = data.items.filter(i => !i.isCredit);
  const credits = data.items.filter(i => i.isCredit);
  const subtotalCharges = charges.reduce((s, i) => s + Math.abs(i.amount), 0);
  const subtotalCredits = credits.reduce((s, i) => s + Math.abs(i.amount), 0);
  const total = subtotalCharges - subtotalCredits;

  // Subtotal/credits if applicable
  if (subtotalCredits > 0) {
    doc.setFontSize(8.5);
    doc.setFont("helvetica", "normal");
    doc.setTextColor(80, 80, 80);
    doc.text("Subtotal", totalSectionX, totalY);
    doc.text(fmtCurrency(subtotalCharges), rightEdge, totalY, { align: "right" });
    totalY += 6;

    doc.setTextColor(220, 38, 38);
    doc.text("Credits", totalSectionX, totalY);
    doc.text(`-${fmtCurrency(subtotalCredits)}`, rightEdge, totalY, { align: "right" });
    totalY += 8;

    doc.setDrawColor(200, 200, 200);
    doc.setLineWidth(0.3);
    doc.line(totalSectionX, totalY - 3, rightEdge, totalY - 3);
  }

  // Total label
  doc.setFontSize(9);
  doc.setFont("helvetica", "normal");
  doc.setTextColor(80, 80, 80);
  doc.text("Total", totalSectionX, totalY);

  // Total amount (large, bold)
  doc.setFontSize(18);
  doc.setFont("helvetica", "bold");
  doc.setTextColor(20, 20, 20);
  doc.text(fmtCurrency(total), rightEdge, totalY + 1, { align: "right" });

  // Separator under total
  totalY += 6;
  doc.setDrawColor(200, 200, 200);
  doc.setLineWidth(0.3);
  doc.line(totalSectionX, totalY, rightEdge, totalY);

  // Overdue / Due status
  totalY += 8;
  const today = new Date();
  const dueDate = data.dueDate ? new Date(data.dueDate + "T12:00:00") : null;
  const isOverdue = dueDate && dueDate < today;

  if (isOverdue) {
    doc.setFontSize(11);
    doc.setFont("helvetica", "bold");
    doc.setTextColor(220, 53, 0);
    doc.text("Overdue", totalSectionX, totalY);
    doc.setFontSize(8.5);
    doc.setFont("helvetica", "normal");
    doc.setTextColor(80, 80, 80);
    doc.text(fmtDate(data.dueDate), rightEdge, totalY, { align: "right" });
  } else if (data.dueDate) {
    doc.setFontSize(9);
    doc.setFont("helvetica", "normal");
    doc.setTextColor(80, 80, 80);
    doc.text("Due date", totalSectionX, totalY);
    doc.text(fmtDate(data.dueDate), rightEdge, totalY, { align: "right" });
  }

  // ═══════════════════════════════════════════
  // NOTES
  // ═══════════════════════════════════════════
  if (data.notes) {
    const notesY = totalY + 18;
    doc.setFontSize(8);
    doc.setFont("helvetica", "normal");
    doc.setTextColor(120, 120, 120);
    doc.text("Notes:", margin, notesY);
    doc.setTextColor(80, 80, 80);
    const noteLines = doc.splitTextToSize(data.notes, rightEdge - margin);
    doc.text(noteLines, margin, notesY + 5);
  }

  // ═══════════════════════════════════════════
  // FOOTER
  // ═══════════════════════════════════════════
  const footerY = pageHeight - 12;
  doc.setFontSize(7);
  doc.setTextColor(170, 170, 170);
  doc.text(`Generated ${new Date().toLocaleDateString()}`, rightEdge, footerY, { align: "right" });

  // ── Collect unique receipt URLs from line items and append as attachments ──
  const fileName = `Invoice_${data.invoiceNumber}_${data.clientName.replace(/\s+/g, "_")}.pdf`;
  const receiptUrls = Array.from(
    new Set(
      data.items
        .map((i) => i.receiptUrl)
        .filter((u): u is string => !!u && typeof u === "string")
    )
  );

  if (receiptUrls.length === 0) {
    doc.save(fileName);
    return;
  }

  try {
    const baseBytes = doc.output("arraybuffer");
    const merged = await PDFDocument.create();
    const basePdf = await PDFDocument.load(baseBytes);
    const baseCopied = await merged.copyPages(basePdf, basePdf.getPageIndices());
    baseCopied.forEach((p) => merged.addPage(p));

    for (const url of receiptUrls) {
      try {
        const res = await fetch(url);
        if (!res.ok) continue;
        const contentType = (res.headers.get("content-type") || "").toLowerCase();
        const buf = await res.arrayBuffer();
        const lowerUrl = url.toLowerCase();
        const isPdf = contentType.includes("pdf") || lowerUrl.includes(".pdf");
        const isPng = contentType.includes("png") || lowerUrl.endsWith(".png");
        const isJpg =
          contentType.includes("jpeg") ||
          contentType.includes("jpg") ||
          lowerUrl.endsWith(".jpg") ||
          lowerUrl.endsWith(".jpeg");

        if (isPdf) {
          const attachPdf = await PDFDocument.load(buf);
          const copied = await merged.copyPages(attachPdf, attachPdf.getPageIndices());
          copied.forEach((p) => merged.addPage(p));
        } else if (isPng || isJpg) {
          const img = isPng ? await merged.embedPng(buf) : await merged.embedJpg(buf);
          // Letter size page (points): 612 x 792
          const page = merged.addPage([612, 792]);
          const margin = 36;
          const maxW = 612 - margin * 2;
          const maxH = 792 - margin * 2 - 24; // leave room for caption
          const scale = Math.min(maxW / img.width, maxH / img.height, 1);
          const w = img.width * scale;
          const h = img.height * scale;
          page.drawImage(img, {
            x: (612 - w) / 2,
            y: (792 - h) / 2 - 12,
            width: w,
            height: h,
          });
        }
      } catch (e) {
        console.warn("[Invoice] Failed to attach receipt:", url, e);
      }
    }

    const mergedBytes = await merged.save();
    const blob = new Blob([mergedBytes as BlobPart], { type: "application/pdf" });
    const link = document.createElement("a");
    link.href = URL.createObjectURL(blob);
    link.download = fileName;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    URL.revokeObjectURL(link.href);
  } catch (e) {
    console.error("[Invoice] Receipt merge failed, downloading invoice without receipts.", e);
    doc.save(fileName);
  }
}
