import PDFDocument from "pdfkit";
import type { Response } from "express";
import { PdfHeader } from "./components/PdfHeader.js";

const COLORS = {
  primary: "#0F5132",
  accent: "#198754",
  text: "#212529",
  muted: "#6C757D",
  border: "#DEE2E6",
  rowAlt: "#F8F9FA",
  opening: "#EEF2FF",
  debit: "#FFF3E0",
  credit: "#E8F5E9",
};

const PAGE_MARGIN = 40;

type SupplierLedgerRow = {
  date: string | Date | null;
  voucherNo?: string;
  supplierName?: string;
  transactionType?: string;
  description?: string;
  debit?: number | string;
  credit?: number | string;
  runningBalance?: number | string;
};

type SupplierLedgerSummary = {
  openingBalance: number;
  totalDebit: number;
  totalCredit: number;
  closingBalance: number;
};

function asNumber(value: number | string | undefined | null): number {
  const n = Number(value ?? 0);
  return Number.isFinite(n) ? n : 0;
}

function formatAmount(value: number | string | undefined | null): string {
  const n = asNumber(value);
  return n.toLocaleString("en-IN", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}

function formatDate(value: string | Date | null | undefined): string {
  if (!value) return "-";
  const d = value instanceof Date ? value : new Date(value);
  return Number.isNaN(d.getTime()) ? "-" : d.toLocaleDateString("en-GB").replace(/\//g, "-");
}

function rowFillColor(row: SupplierLedgerRow, index: number): string {
  if ((row.transactionType ?? "") === "Opening Balance") return COLORS.opening;
  if (asNumber(row.debit) > 0 && asNumber(row.credit) === 0) return COLORS.debit;
  if (asNumber(row.credit) > 0 && asNumber(row.debit) === 0) return COLORS.credit;
  return index % 2 === 1 ? COLORS.rowAlt : "#FFFFFF";
}

export class SupplierLedgerPdf {
  static async generate(
    rows: SupplierLedgerRow[],
    summary: SupplierLedgerSummary,
    res: Response,
    filters?: {
      supplierId?: string;
      dateFrom?: string;
      dateTo?: string;
    }
  ) {
    const doc = new PDFDocument({
      size: "A4",
      layout: "landscape",
      margin: PAGE_MARGIN,
      bufferPages: true,
    });

    res.setHeader("Content-Type", "application/pdf");
    res.setHeader("Content-Disposition", `attachment; filename="supplier-ledger-${Date.now()}.pdf"`);
    res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
    doc.pipe(res);

    const pageWidth = doc.page.width - PAGE_MARGIN * 2;

    await PdfHeader.draw(doc, pageWidth, PAGE_MARGIN, "SUPPLIER LEDGER REPORT");

    this.drawFilterLine(doc, pageWidth, filters);
    this.drawTable(doc, rows, pageWidth);
    this.drawSummary(doc, summary, pageWidth);
    this.drawFooter(doc, pageWidth);

    doc.end();
  }

  private static drawFilterLine(
    doc: PDFKit.PDFDocument,
    pageWidth: number,
    filters?: {
      supplierId?: string;
      dateFrom?: string;
      dateTo?: string;
    }
  ) {
    const parts: string[] = [];
    if (filters?.dateFrom) parts.push(`From: ${new Date(filters.dateFrom).toLocaleDateString("en-GB").replace(/\//g, "-")}`);
    if (filters?.dateTo) parts.push(`To: ${new Date(filters.dateTo).toLocaleDateString("en-GB").replace(/\//g, "-")}`);
    if (filters?.supplierId) parts.push(`Supplier Id: ${filters.supplierId}`);

    if (parts.length > 0) {
      doc
        .font("Helvetica")
        .fontSize(8.5)
        .fillColor(COLORS.muted)
        .text(parts.join("   |   "), PAGE_MARGIN, doc.y, {
          width: pageWidth,
          align: "right",
        });
      doc.y += 4;
    }

    doc.y += 10;
  }

  private static drawTable(doc: PDFKit.PDFDocument, rows: SupplierLedgerRow[], pageWidth: number) {
    const pw = pageWidth;
    const cols = {
      no: { x: PAGE_MARGIN + pw * 0.0, width: pw * 0.04 },
      date: { x: PAGE_MARGIN + pw * 0.04, width: pw * 0.09 },
      voucherNo: { x: PAGE_MARGIN + pw * 0.13, width: pw * 0.11 },
      supplierName: { x: PAGE_MARGIN + pw * 0.24, width: pw * 0.18 },
      transactionType: { x: PAGE_MARGIN + pw * 0.42, width: pw * 0.13 },
      description: { x: PAGE_MARGIN + pw * 0.55, width: pw * 0.2 },
      debit: { x: PAGE_MARGIN + pw * 0.75, width: pw * 0.08 },
      credit: { x: PAGE_MARGIN + pw * 0.83, width: pw * 0.08 },
      balance: { x: PAGE_MARGIN + pw * 0.91, width: pw * 0.09 },
    };

    const rowHeight = 22;
    const headerHeight = 24;

    const drawHeader = () => {
      const y = doc.y;
      doc
        .font("Helvetica-Bold")
        .fontSize(7.5)
        .fillColor(COLORS.primary)
        .text("#", cols.no.x, y + 5, { width: cols.no.width, align: "center" })
        .text("DATE", cols.date.x, y + 5, { width: cols.date.width })
        .text("VOUCHER", cols.voucherNo.x, y + 5, { width: cols.voucherNo.width })
        .text("SUPPLIER", cols.supplierName.x, y + 5, { width: cols.supplierName.width })
        .text("TYPE", cols.transactionType.x, y + 5, { width: cols.transactionType.width })
        .text("DESCRIPTION", cols.description.x, y + 5, { width: cols.description.width })
        .text("DEBIT", cols.debit.x, y + 5, { width: cols.debit.width - 2, align: "right" })
        .text("CREDIT", cols.credit.x, y + 5, { width: cols.credit.width - 2, align: "right" })
        .text("BALANCE", cols.balance.x, y + 5, { width: cols.balance.width - 4, align: "right" });

      doc.y = y + headerHeight;
      doc.moveTo(PAGE_MARGIN, doc.y).lineTo(PAGE_MARGIN + pw, doc.y).strokeColor(COLORS.primary).lineWidth(1).stroke();
      doc.y += 4;
    };

    const ensureSpace = (needed: number) => {
      const bottomLimit = doc.page.height - PAGE_MARGIN - 70;
      if (doc.y + needed > bottomLimit) {
        doc.addPage();
        doc.y = PAGE_MARGIN;
        drawHeader();
      }
    };

    drawHeader();

    rows.forEach((row, index) => {
      ensureSpace(rowHeight);
      const y = doc.y;

      const fill = rowFillColor(row, index);
      doc.rect(PAGE_MARGIN, y, pw, rowHeight).fill(fill);

      doc
        .font("Helvetica-Bold")
        .fontSize(7.5)
        .fillColor(COLORS.accent)
        .text(String(index + 1), cols.no.x, y + 4, { width: cols.no.width, align: "center" });

      doc.font("Helvetica").fontSize(8).fillColor(COLORS.text);
      doc.text(formatDate(row.date), cols.date.x, y + 4, { width: cols.date.width - 2, lineBreak: false });
      doc.text(row.voucherNo ?? "-", cols.voucherNo.x, y + 4, { width: cols.voucherNo.width - 2, ellipsis: true, lineBreak: false });
      doc.text(row.supplierName ?? "-", cols.supplierName.x, y + 4, { width: cols.supplierName.width - 2, ellipsis: true, lineBreak: false });
      doc.text(row.transactionType ?? "-", cols.transactionType.x, y + 4, { width: cols.transactionType.width - 2, ellipsis: true, lineBreak: false });
      doc.text(row.description ?? "-", cols.description.x, y + 4, { width: cols.description.width - 2, ellipsis: true, lineBreak: false });
      doc.text(formatAmount(row.debit), cols.debit.x, y + 4, { width: cols.debit.width - 2, align: "right", lineBreak: false });
      doc.text(formatAmount(row.credit), cols.credit.x, y + 4, { width: cols.credit.width - 2, align: "right", lineBreak: false });

      doc
        .font("Helvetica-Bold")
        .fillColor(COLORS.primary)
        .text(formatAmount(row.runningBalance), cols.balance.x, y + 4, {
          width: cols.balance.width - 4,
          align: "right",
          lineBreak: false,
        });

      doc.y = y + rowHeight;
    });

    doc.moveTo(PAGE_MARGIN, doc.y).lineTo(PAGE_MARGIN + pw, doc.y).strokeColor(COLORS.border).lineWidth(0.5).stroke();
    doc.y += 12;
  }

  private static drawSummary(doc: PDFKit.PDFDocument, summary: SupplierLedgerSummary, pageWidth: number) {
    const boxWidth = pageWidth * 0.42;
    const left = PAGE_MARGIN + pageWidth - boxWidth;
    const valueX = left + boxWidth * 0.58;
    const valueWidth = boxWidth * 0.42;

    const y0 = doc.y;
    doc.moveTo(left, y0).lineTo(PAGE_MARGIN + pageWidth, y0).strokeColor(COLORS.primary).lineWidth(0.75).stroke();

    const drawRow = (label: string, value: number | string, bold = false) => {
      doc.font(bold ? "Helvetica-Bold" : "Helvetica").fontSize(9).fillColor(COLORS.text)
        .text(label, left, doc.y, { width: boxWidth * 0.58 });
      doc.font("Helvetica-Bold").fontSize(9).fillColor(COLORS.primary)
        .text(formatAmount(value), valueX, doc.y - 12.5, { width: valueWidth, align: "right" });
      doc.y += 4;
    };

    doc.y = y0 + 6;
    drawRow("Opening Balance:", summary.openingBalance);
    drawRow("Total Debit:", summary.totalDebit);
    drawRow("Total Credit:", summary.totalCredit);

    doc.font("Helvetica-Bold").fontSize(10).fillColor(COLORS.primary)
      .text("Closing Balance:", left, doc.y, { width: boxWidth * 0.58 });
    doc.font("Helvetica-Bold").fontSize(10).fillColor(COLORS.primary)
      .text(formatAmount(summary.closingBalance), valueX, doc.y - 14, { width: valueWidth, align: "right" });

    doc.y += 18;
  }

  private static drawFooter(doc: PDFKit.PDFDocument, pageWidth: number) {
    const y = doc.page.height - PAGE_MARGIN - 20;
    doc
      .font("Helvetica-Oblique")
      .fontSize(8.5)
      .fillColor(COLORS.muted)
      .text("This is a computer-generated supplier ledger report.", PAGE_MARGIN, y, {
        width: pageWidth,
        align: "center",
      });
  }
}
