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

const COLORS = {
  primary: "#1B5E20",
  accent: "#2E7D32",
  text: "#212529",
  muted: "#6C757D",
  border: "#DEE2E6",
  rowAlt: "#F8F9FA",
  varavu: "#E8F8F5",
  patru: "#FFF3E0",
};

const PAGE_MARGIN = 36;

export type CashBankPdfRow = {
  date: string | Date | null;
  voucherNo?: string;
  transactionType?: string;
  account?: string;
  paymentMode?: string;
  party?: string;
  description?: string;
  varavu?: number | string;
  patru?: number | string;
  balance?: number | string;
};

export type CashBankPdfSummary = {
  openingBalance: number;
  totalVaravu: number;
  totalPatru: number;
  closingBalance: number;
  currentCashInHand?: number;
  currentCashInBank?: number;
  totalCombined?: 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: CashBankPdfRow, index: number): string {
  if (asNumber(row.varavu) > 0 && asNumber(row.patru) === 0) return COLORS.varavu;
  if (asNumber(row.patru) > 0 && asNumber(row.varavu) === 0) return COLORS.patru;
  return index % 2 === 1 ? COLORS.rowAlt : "#FFFFFF";
}

export class CashBankPdf {
  static async generate(
    rows: CashBankPdfRow[],
    summary: CashBankPdfSummary,
    res: Response,
    filters?: {
      account?: string | undefined;
      type?: string | undefined;
      dateFrom?: string | undefined;
      dateTo?: string | undefined;
      search?: string | undefined;
    }
  ) {
    const doc = new PDFDocument({
      size: "A4",
      layout: "landscape",
      margin: PAGE_MARGIN,
      bufferPages: true,
    });

    const reportTitle =
      filters?.account === "CASH"
        ? "CASH REPORT"
        : filters?.account === "BANK"
        ? "BANK REPORT"
        : "CASH & BANK LEDGER REPORT";

    const reportFile =
      filters?.account === "CASH"
        ? `Cash_Report_${Date.now()}.pdf`
        : filters?.account === "BANK"
        ? `Bank_Report_${Date.now()}.pdf`
        : `Cash_Bank_Ledger_${Date.now()}.pdf`;

    res.setHeader("Content-Type", "application/pdf");
    res.setHeader("Content-Disposition", `attachment; filename="${reportFile}"`);
    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, reportTitle);

    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?: {
      account?: string | undefined;
      type?: string | undefined;
      dateFrom?: string | undefined;
      dateTo?: string | undefined;
      search?: string | undefined;
    }
  ) {
    const parts: string[] = [];
    if (filters?.account) parts.push(`Account: ${filters.account}`);
    if (filters?.type) parts.push(`Type: ${filters.type}`);
    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 (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: CashBankPdfRow[], pageWidth: number) {
    const pw = pageWidth;
    const cols = {
      no: { x: PAGE_MARGIN + pw * 0.0, width: pw * 0.035 },
      date: { x: PAGE_MARGIN + pw * 0.035, width: pw * 0.085 },
      voucherNo: { x: PAGE_MARGIN + pw * 0.12, width: pw * 0.10 },
      type: { x: PAGE_MARGIN + pw * 0.22, width: pw * 0.11 },
      account: { x: PAGE_MARGIN + pw * 0.33, width: pw * 0.07 },
      mode: { x: PAGE_MARGIN + pw * 0.40, width: pw * 0.06 },
      party: { x: PAGE_MARGIN + pw * 0.46, width: pw * 0.16 },
      description: { x: PAGE_MARGIN + pw * 0.62, width: pw * 0.14 },
      varavu: { x: PAGE_MARGIN + pw * 0.76, width: pw * 0.08 },
      patru: { x: PAGE_MARGIN + pw * 0.84, width: pw * 0.08 },
      balance: { x: PAGE_MARGIN + pw * 0.92, width: pw * 0.08 },
    };

    const rowHeight = 20;
    const headerHeight = 22;

    const drawHeader = () => {
      const y = doc.y;
      doc
        .font("Helvetica-Bold")
        .fontSize(7.5)
        .fillColor(COLORS.primary)
        .text("#", cols.no.x, y + 4, { width: cols.no.width, align: "center" })
        .text("DATE", cols.date.x, y + 4, { width: cols.date.width })
        .text("VOUCHER", cols.voucherNo.x, y + 4, { width: cols.voucherNo.width })
        .text("TYPE", cols.type.x, y + 4, { width: cols.type.width })
        .text("ACCOUNT", cols.account.x, y + 4, { width: cols.account.width })
        .text("MODE", cols.mode.x, y + 4, { width: cols.mode.width })
        .text("PARTY", cols.party.x, y + 4, { width: cols.party.width })
        .text("DESCRIPTION", cols.description.x, y + 4, { width: cols.description.width })
        .text("CREDIT (+)", cols.varavu.x, y + 4, { width: cols.varavu.width - 2, align: "right" })
        .text("DEBIT (-)", cols.patru.x, y + 4, { width: cols.patru.width - 2, align: "right" })
        .text("TALLY BAL", cols.balance.x, y + 4, { width: cols.balance.width - 2, 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)
        .fillColor(COLORS.accent)
        .text(String(index + 1), cols.no.x, y + 4, { width: cols.no.width, align: "center" });

      doc.font("Helvetica").fontSize(7.5).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.transactionType ?? "-", cols.type.x, y + 4, { width: cols.type.width - 2, ellipsis: true, lineBreak: false });
      doc.text(row.account ?? "-", cols.account.x, y + 4, { width: cols.account.width - 2, lineBreak: false });
      doc.text(row.paymentMode ?? "-", cols.mode.x, y + 4, { width: cols.mode.width - 2, lineBreak: false });
      doc.text(row.party ?? "-", cols.party.x, y + 4, { width: cols.party.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.font("Helvetica-Bold").fillColor(asNumber(row.varavu) > 0 ? "#2E7D32" : COLORS.text);
      doc.text(asNumber(row.varavu) > 0 ? formatAmount(row.varavu) : "-", cols.varavu.x, y + 4, { width: cols.varavu.width - 2, align: "right", lineBreak: false });

      doc.fillColor(asNumber(row.patru) > 0 ? "#C62828" : COLORS.text);
      doc.text(asNumber(row.patru) > 0 ? formatAmount(row.patru) : "-", cols.patru.x, y + 4, { width: cols.patru.width - 2, align: "right", lineBreak: false });

      doc.fillColor(COLORS.primary);
      doc.text(formatAmount(row.balance), cols.balance.x, y + 4, { width: cols.balance.width - 2, 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: CashBankPdfSummary, pageWidth: number) {
    const boxWidth = pageWidth * 0.45;
    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(8.5).fillColor(COLORS.text)
        .text(label, left, doc.y, { width: boxWidth * 0.58 });
      doc.font("Helvetica-Bold").fontSize(8.5).fillColor(COLORS.primary)
        .text(formatAmount(value), valueX, doc.y - 11.5, { width: valueWidth, align: "right" });
      doc.y += 3;
    };

    doc.y = y0 + 6;
    drawRow("Opening Balance:", summary.openingBalance);
    drawRow("Total Varavu (Inflow):", summary.totalVaravu);
    drawRow("Total Patru (Outflow):", summary.totalPatru);

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

    doc.y += 16;
  }

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