import PDFDocument from "pdfkit";
import type { Response } from "express";
import http from "http";
import https from "https";
import fs from "fs";
import path from "path";
import QRCode from "qrcode";
import { PdfHeader } from "./components/PdfHeader.js";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const FONTS_DIR = path.join(__dirname, "fonts");

interface InvoiceData {
    sale: any;
    shop: any;
    upis: any[];
}

const BASE_URL = (process.env.BASE_URL || "http://localhost:5000").replace(/\/$/, "");

const resolveFileUrl = (filename: string | null | undefined, folder: string): string | null => {
    if (!filename) return null;
    if (filename.startsWith("http")) return filename;
    return `${BASE_URL}/${folder}/${filename}`;
};

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

const PAGE_MARGIN = 40;

// Keep this area clear for QR blocks, totals and signatures.
const FOOTER_HEIGHT = 180;

export class InvoicePdf {
    static async generate(
        data: InvoiceData,
        res: Response,
        options: { lang: string; printSign: boolean } = { lang: "en", printSign: false } // TEMP: Digital signature disabled — change to true to re-enable
    ) {
        const { sale, shop, upis } = data;

        const doc = new PDFDocument({
            size: "A4",
            margin: PAGE_MARGIN,
            bufferPages: true,
        });

        // Register a custom font only for the vegetable name column when Hindi/Tamil is selected.
        let vegFont = 'Helvetica';
        if (options.lang === 'hi' && fs.existsSync(path.join(FONTS_DIR, "NotoSansDevanagari-Regular.ttf"))) {
            doc.registerFont("VegFont", path.join(FONTS_DIR, "NotoSansDevanagari-Regular.ttf"));
            vegFont = 'VegFont';
        } else if (options.lang === 'ta' && fs.existsSync(path.join(FONTS_DIR, "NotoSansTamil-Regular.ttf"))) {
            doc.registerFont("VegFont", path.join(FONTS_DIR, "NotoSansTamil-Regular.ttf"));
            vegFont = 'VegFont';
        } else if (options.lang === 'kn' && fs.existsSync(path.join(FONTS_DIR, "NotoSansKannada-Regular.ttf"))) {
            doc.registerFont("VegFont", path.join(FONTS_DIR, "NotoSansKannada-Regular.ttf"));
            vegFont = 'VegFont';
        } else if (options.lang === 'ml' && fs.existsSync(path.join(FONTS_DIR, "Manjari-Regular.ttf"))) {
            doc.registerFont("VegFont", path.join(FONTS_DIR, "Manjari-Regular.ttf"));
            vegFont = 'VegFont';
        } else if (options.lang === 'te' && fs.existsSync(path.join(FONTS_DIR, "NotoSansTelugu-Regular.ttf"))) {
            doc.registerFont("VegFont", path.join(FONTS_DIR, "NotoSansTelugu-Regular.ttf"));
            vegFont = 'VegFont';
        }

        const upi1 = upis?.[0]?.upiId;
        const upi2 = upis?.[1]?.upiId ?? upi1;
        const payeeName = shop?.shopName ?? "Merchant";

        let leftQrBuf: Buffer | null = null;
        let rightQrBuf: Buffer | null = null;

        if (upi1) {
            leftQrBuf = await this.generateQrBuffer(this.buildUpiUri(upi1, payeeName));

            const grandTotal = Number(sale?.grandTotal ?? sale?.subTotal ?? sale?.subtotal ?? 0);
            const receivedAmount = Number(sale?.receivedAmount ?? sale?.amountPaid ?? 0);
            const balanceDue = Number(
                sale?.balanceAmount ?? sale?.balanceDue ?? Math.max(grandTotal - receivedAmount, 0)
            );
            const billAmount = balanceDue > 0 ? balanceDue : grandTotal;

            if (upi2 && billAmount > 0) {
                rightQrBuf = await this.generateQrBuffer(this.buildUpiUri(upi2, payeeName, billAmount));
            }
        }

        let authSignBuf: Buffer | null = null;
        if (options.printSign && shop?.authorizedSign) {
            try {
                let signPath = shop.authorizedSign;
                // if (!signPath.startsWith("http")) {
                //     signPath = path.join(process.cwd(), "uploads", "signatures", shop.authorizedSign);
                // }
                signPath = path.join(process.cwd(), "uploads", "signatures", shop.authorizedSign);
                authSignBuf = await this.fetchImageBuffer(signPath);
            } catch (err) {
                console.error("[InvoicePdf] Failed to fetch authorizedSign:", shop.authorizedSign, err);
            }
        }

        res.setHeader("Content-Type", "application/pdf");
        res.setHeader(
            "Content-Disposition",
            `inline; filename=invoice-${sale?.invoiceNo ?? sale?.id}.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, "INVOICE");

        this.drawCustomerMeta(doc, sale, pageWidth);
        this.drawItemsTable(doc, sale, pageWidth, options.lang, vegFont);
        this.drawFooter(doc, pageWidth, sale, leftQrBuf, rightQrBuf, authSignBuf);
        this.drawAttachedImages(doc, sale, pageWidth);

        doc.end();
    }

    private static async fetchImageBuffer(url: string): Promise<Buffer> {
        if (!url.startsWith("http")) {
            return fs.promises.readFile(path.resolve(url));
        }

        return new Promise((resolve, reject) => {
            const client = url.startsWith("https") ? https : http;
            client
                .get(url, (res) => {
                    if (res.statusCode !== 200) {
                        reject(new Error(`Failed to fetch image: ${res.statusCode}`));
                        return;
                    }

                    const data: Buffer[] = [];
                    res.on("data", (chunk: Buffer) => data.push(chunk));
                    res.on("end", () => resolve(Buffer.concat(data)));
                })
                .on("error", reject);
        });
    }

    private static drawCustomerMeta(doc: PDFKit.PDFDocument, sale: any, pageWidth: number) {
        const boxY = doc.y;
        const boxHeight = 62;
        const rightColWidth = pageWidth * 0.4;
        const leftColWidth = pageWidth - rightColWidth;
        const boxX = PAGE_MARGIN;

        const dateSource = sale?.invoiceDate ?? sale?.createdAt;
        const date = dateSource ? new Date(dateSource).toLocaleDateString("en-GB").replace(/\//g, "-") : "-";

        doc.rect(boxX, boxY, pageWidth, boxHeight).lineWidth(1).strokeColor(COLORS.border).stroke();

        const dividerX = boxX + leftColWidth;
        doc
            .moveTo(dividerX, boxY)
            .lineTo(dividerX, boxY + boxHeight)
            .strokeColor(COLORS.border)
            .lineWidth(1)
            .stroke();

        const leftPad = 10;
        const leftWidth = leftColWidth - leftPad * 2;
        const customerName =
            sale?.customer?.name || (sale?.walkInName ? `${sale.walkInName} (Walk-in)` : "Walk-in Customer");
        const customerMobile = sale?.customer?.mobileNo || sale?.customer?.mobile || sale?.walkInMobile || "";

        doc
            .font("Helvetica-Bold")
            .fontSize(10.5)
            .fillColor(COLORS.primary)
            .text(customerName, boxX + leftPad, boxY + boxHeight / 2 - (customerMobile ? 10 : 5), {
                width: leftWidth,
                lineBreak: false,
                ellipsis: true,
            });

        if (customerMobile) {
            doc
                .font("Helvetica")
                .fontSize(8.5)
                .fillColor(COLORS.muted)
                .text(`Ph: ${customerMobile}`, boxX + leftPad, doc.y + 3, {
                    width: leftWidth,
                    lineBreak: false,
                    ellipsis: true,
                });
        }

        const rightPad = 16;
        const labelX = dividerX + rightPad;
        const labelWidth = 56;
        const colonX = labelX + labelWidth;
        const colonWidth = rightColWidth - rightPad * 2 - labelWidth;

        const inlineRow = (y: number, label: string, value: string) => {
            doc
                .font("Helvetica-Bold")
                .fontSize(10)
                .fillColor(COLORS.primary)
                .text(label, labelX, y, { width: labelWidth, lineBreak: false });

            doc
                .font("Helvetica-Bold")
                .fontSize(10)
                .fillColor(COLORS.text)
                .text(`: ${value}`, colonX, y, { width: colonWidth, lineBreak: false, ellipsis: true });
        };

        inlineRow(boxY + 14, "Invoice No", `${sale?.invoiceNo ?? sale?.id}`);
        inlineRow(boxY + 34, "Date", date);

        doc.y = boxY + boxHeight + 20;
    }

    private static drawItemsTable(
        doc: PDFKit.PDFDocument,
        sale: any,
        pageWidth: number,
        lang: string = "en",
        vegFont: string = "Helvetica"
    ) {
        const cols = {
            no: { x: PAGE_MARGIN, width: pageWidth * 0.06 },
            name: { x: PAGE_MARGIN + pageWidth * 0.06, width: pageWidth * 0.38 },
            bagBox: { x: PAGE_MARGIN + pageWidth * 0.44, width: pageWidth * 0.11 },
            kgsPcs: { x: PAGE_MARGIN + pageWidth * 0.55, width: pageWidth * 0.11 },
            rate: { x: PAGE_MARGIN + pageWidth * 0.66, width: pageWidth * 0.14 },
            total: { x: PAGE_MARGIN + pageWidth * 0.8, width: pageWidth * 0.2 },
        };

        const colBoundaries = [
            PAGE_MARGIN,
            cols.name.x,
            cols.bagBox.x,
            cols.kgsPcs.x,
            cols.rate.x,
            cols.total.x,
            PAGE_MARGIN + pageWidth,
        ];

        const MAX_ROWS_PER_PAGE = 15;
        const TOTAL_ROW_HEIGHT = 28;
        const MIN_ROW_HEIGHT = 16;
        const MAX_ROW_HEIGHT = 28;

        const drawRowGrid = (y: number, height: number) => {
            colBoundaries.forEach((x) => {
                doc
                    .moveTo(x, y)
                    .lineTo(x, y + height)
                    .strokeColor(COLORS.border)
                    .lineWidth(0.75)
                    .stroke();
            });
        };

        const drawTableHeader = (rowHeight: number) => {
            const y = doc.y;
            const textY = y + rowHeight / 2 - 5;

            drawRowGrid(y, rowHeight);

            doc
                .font("Helvetica-Bold")
                .fontSize(9)
                .fillColor(COLORS.primary)
                .text("SL#", cols.no.x, textY, { width: cols.no.width, align: "center" })
                .text("PRODUCT", cols.name.x + 6, textY, { width: cols.name.width - 6 })
                .text("Bag/Box", cols.bagBox.x, textY, { width: cols.bagBox.width, align: "center" })
                .text("Kgs/Pcs", cols.kgsPcs.x, textY, { width: cols.kgsPcs.width, align: "center" })
                .text("RATE", cols.rate.x, textY, { width: cols.rate.width - 8, align: "right" })
                .text("AMOUNT", cols.total.x, textY, { width: cols.total.width - 8, align: "right" });

            doc.y = y + rowHeight;

            doc
                .moveTo(PAGE_MARGIN, doc.y)
                .lineTo(PAGE_MARGIN + pageWidth, doc.y)
                .strokeColor(COLORS.primary)
                .lineWidth(1)
                .stroke();
        };

        const bottomLimit = doc.page.height - PAGE_MARGIN - FOOTER_HEIGHT;

        drawTableHeader(MAX_ROW_HEIGHT);

        const availableForItems = bottomLimit - doc.y - TOTAL_ROW_HEIGHT;
        const rowHeight = Math.min(
            MAX_ROW_HEIGHT,
            Math.max(MIN_ROW_HEIGHT, Math.floor(availableForItems / MAX_ROWS_PER_PAGE))
        );

        const items: any[] = sale?.items ?? [];
        let totalBags = 0;
        let totalKgs = 0;
        let totalAmount = 0;

        const drawItemRow = (item: any, indexInFullList: number) => {
            const y = doc.y;
            const textY = y + rowHeight / 2 - 5;

            // Pick the localised vegetable name from the master table; fall back to English name.
            const vegetable = item?.purchase?.vegetable;
            const defaultName = vegetable?.name ?? "Item";
            let name = defaultName;
            if (lang === "ta" && vegetable?.tamilName) name = vegetable.tamilName;
            else if (lang === "hi" && vegetable?.hindiName) name = vegetable.hindiName;
            else if (lang === "kn" && vegetable?.kannadaName) name = vegetable.kannadaName;
            else if (lang === "ml" && vegetable?.malayalamName) name = vegetable.malayalamName;
            else if (lang === "te" && vegetable?.teluguName) name = vegetable.teluguName;

            let currentFont = vegFont;
            if (name === defaultName) {
                currentFont = "Helvetica";
            }

            const lotCode = item?.purchase?.lotCode ?? item?.lotCode ?? null;
            const displayName = lotCode ? `${name} [${lotCode}]` : name;

            // --- OLD CODE (Commented per request) ---
            // const qtyValueOld = Number(item?.quantity ?? 0);
            // totalQty += Number.isFinite(qtyValueOld) ? qtyValueOld : 0;
            // const unit = item?.unit ? ` ${item.unit}` : " Kg";
            // const qty = `${item?.quantity ?? 0}${unit}`;
            // ----------------------------------------

            const isCountOnlyUnit = item?.saleUnit === 'PIECES' || item?.saleUnit === 'THAAR';
            const bagsValue = isCountOnlyUnit ? 0 : Number(item?.bags || 0);
            const qtyValue = Number(item?.quantity || (isCountOnlyUnit ? item?.bags : 0) || 0);

            totalBags += Number.isFinite(bagsValue) ? bagsValue : 0;
            totalKgs += Number.isFinite(qtyValue) ? qtyValue : 0;

            let bagBoxStr = '-';
            if (bagsValue > 0) {
                bagBoxStr = `${bagsValue}`;
            }

            let kgsPcsStr = '-';
            if (qtyValue > 0) {
                kgsPcsStr = `${qtyValue}`;
            }

            const rate = this.formatCurrency(item?.sellingRate);
            const amount = Number(item?.total ?? 0);
            totalAmount += amount;
            const total = this.formatCurrency(amount);

            if (indexInFullList % 2 === 1) {
                doc.rect(PAGE_MARGIN, y, pageWidth, rowHeight).fill(COLORS.rowAlt);
            }

            drawRowGrid(y, rowHeight);

            doc
                .font("Helvetica-Bold")
                .fontSize(9)
                .fillColor(COLORS.accent)
                .text(`${indexInFullList + 1}`, cols.no.x, textY, { width: cols.no.width, align: "center" });

            try {
                doc
                    .font(currentFont)
                    .fontSize(9)
                    .fillColor(COLORS.text);

                // Monkey-patch fontkit GPOSProcessor to fix Indic font crash
                try {
                    const gpos = (doc as any)._font?.font?._layoutEngine?.engine?.GPOSProcessor;
                    if (gpos && !gpos.__patched) {
                        const originalGetAnchor = gpos.getAnchor.bind(gpos);
                        gpos.getAnchor = function (anchor: any, ...args: any[]) {
                            if (!anchor) return null;
                            return originalGetAnchor(anchor, ...args);
                        };
                        gpos.__patched = true;
                    }
                } catch (e) { }

                doc.text(displayName, cols.name.x + 6, textY, { width: cols.name.width - 10, ellipsis: false });
            } catch (err) {
                try {
                    // Fallback for fontkit GPOS bugs with Indic fonts
                    doc
                        .font(currentFont)
                        .fontSize(9)
                        .fillColor(COLORS.text)
                        .text(displayName, cols.name.x + 6, textY, { width: cols.name.width - 10, ellipsis: false, features: ['-mark', '-mkmk', '-kern', '-blwm', '-abvm', '-dist', '-curs'] as any });
                } catch (err2) {
                    console.error("[InvoicePdf] Font shaping failed even without marks, falling back to Helvetica:", displayName, err2);
                    doc
                        .font("Helvetica")
                        .fontSize(9)
                        .fillColor(COLORS.text)
                        .text(defaultName, cols.name.x + 6, textY, { width: cols.name.width - 10, ellipsis: true });
                }
            }

            doc
                .font("Helvetica")
                .fontSize(9)
                .fillColor(COLORS.text)
                .text(bagBoxStr, cols.bagBox.x, textY, { width: cols.bagBox.width, align: "center" })
                .text(kgsPcsStr, cols.kgsPcs.x, textY, { width: cols.kgsPcs.width, align: "center" })
                .text(rate, cols.rate.x, textY, { width: cols.rate.width - 8, align: "right" })
                .text(total, cols.total.x, textY, { width: cols.total.width - 8, align: "right" });

            doc.y = y + rowHeight;
        };

        for (let pageStart = 0; pageStart < Math.max(items.length, 1); pageStart += MAX_ROWS_PER_PAGE) {
            const isFirstPage = pageStart === 0;
            if (!isFirstPage) {
                doc.addPage();
                doc.y = PAGE_MARGIN;
                drawTableHeader(rowHeight);
            }

            const chunk = items.slice(pageStart, pageStart + MAX_ROWS_PER_PAGE);
            chunk.forEach((item, i) => drawItemRow(item, pageStart + i));

            const isLastPage = pageStart + MAX_ROWS_PER_PAGE >= items.length;
            if (isLastPage) {
                for (let i = chunk.length; i < MAX_ROWS_PER_PAGE; i++) {
                    drawRowGrid(doc.y, rowHeight);
                    doc.y += rowHeight;
                }
            }
        }

        const totalY = doc.y;
        drawRowGrid(totalY, TOTAL_ROW_HEIGHT);
        const totalTextY = totalY + TOTAL_ROW_HEIGHT / 2 - 5;

        doc
            .font("Helvetica-Bold")
            .fontSize(10)
            .fillColor(COLORS.text)
            .text("Total", cols.name.x + 6, totalTextY, { width: cols.name.width - 10 })
            .text(`${totalBags}`, cols.bagBox.x, totalTextY, {
                width: cols.bagBox.width,
                align: "center",
            })
            .text(`${totalKgs}`, cols.kgsPcs.x, totalTextY, {
                width: cols.kgsPcs.width,
                align: "center",
            })
            .text(this.formatCurrency(totalAmount), cols.total.x, totalTextY, {
                width: cols.total.width - 8,
                align: "right",
            });

        doc.y = totalY + TOTAL_ROW_HEIGHT;

        doc
            .moveTo(PAGE_MARGIN, doc.y)
            .lineTo(PAGE_MARGIN + pageWidth, doc.y)
            .strokeColor(COLORS.primary)
            .lineWidth(1)
            .stroke();

        doc.y += 16;
    }

    private static drawFooter(
        doc: PDFKit.PDFDocument,
        pageWidth: number,
        sale: any,
        leftQrBuf: Buffer | null,
        rightQrBuf: Buffer | null,
        authSignBuf: Buffer | null
    ) {
        const footerTop = doc.page.height - PAGE_MARGIN - FOOTER_HEIGHT;
        const qrSize = 80;

        doc
            .moveTo(PAGE_MARGIN, footerTop)
            .lineTo(PAGE_MARGIN + pageWidth, footerTop)
            .strokeColor(COLORS.border)
            .lineWidth(1)
            .stroke();

        const qrY = footerTop + 16;
        const leftQrX = PAGE_MARGIN;
        const rightQrX = PAGE_MARGIN + pageWidth - qrSize;

        if (leftQrBuf) {
            doc.image(leftQrBuf, leftQrX, qrY, { width: qrSize, height: qrSize });
        } else {
            doc.rect(leftQrX, qrY, qrSize, qrSize).lineWidth(1).strokeColor(COLORS.border).stroke();
            doc
                .font("Helvetica")
                .fontSize(7)
                .fillColor(COLORS.muted)
                .text("QR", leftQrX, qrY + qrSize / 2 - 4, { width: qrSize, align: "center" });
        }

        doc
            .font("Helvetica")
            .fontSize(8)
            .fillColor(COLORS.muted)
            .text("Scan for Any Amount", leftQrX - 10, qrY + qrSize + 6, {
                width: qrSize + 20,
                align: "center",
            });

        if (rightQrBuf) {
            doc.image(rightQrBuf, rightQrX, qrY, { width: qrSize, height: qrSize });
        } else {
            doc.rect(rightQrX, qrY, qrSize, qrSize).lineWidth(1).strokeColor(COLORS.border).stroke();
            doc
                .font("Helvetica")
                .fontSize(7)
                .fillColor(COLORS.muted)
                .text("QR", rightQrX, qrY + qrSize / 2 - 4, { width: qrSize, align: "center" });
        }

        doc
            .font("Helvetica")
            .fontSize(8)
            .fillColor(COLORS.muted)
            .text("Scan for Bill Amount", rightQrX - 10, qrY + qrSize + 6, {
                width: qrSize + 20,
                align: "center",
            });

        const subtotal = Number(sale?.subTotal ?? sale?.subtotal ?? 0);
        const discount = Number(sale?.discount ?? 0);
        const freightCharge = Number(sale?.freightCharge ?? 0);
        const grandTotal = Number(sale?.grandTotal ?? Math.max(0, subtotal - discount) + freightCharge);
        const receivedAmount = Number(sale?.receivedAmount ?? sale?.amountPaid ?? 0);
        const balanceAmount = Number(
            sale?.balanceAmount ?? sale?.balanceDue ?? Math.max(grandTotal - receivedAmount, 0)
        );
        const customerOutstanding = Number(sale?.customer?.outstandingBalance ?? 0);
        const paymentMode = sale?.paymentMode ?? "CASH";

        const totalsX = leftQrX + qrSize + 14;
        const totalsWidth = rightQrX - totalsX - 14;
        let ty = qrY;

        const totalsRow = (
            label: string,
            value: string,
            opts: { bold?: boolean; size?: number; color?: string } = {}
        ) => {
            const size = opts.size ?? 8.5;
            doc
                .font(opts.bold ? "Helvetica-Bold" : "Helvetica")
                .fontSize(size)
                .fillColor(opts.color ?? COLORS.text)
                .text(label, totalsX, ty, { width: totalsWidth * 0.60, lineBreak: false })
                .text(value, totalsX, ty, { width: totalsWidth, align: "right", lineBreak: false });
            ty += size + 4;
        };

        totalsRow("Subtotal:", this.formatCurrency(subtotal), { color: COLORS.muted });

        if (discount > 0) {
            totalsRow("Discount:", `- ${this.formatCurrency(discount)}`, { color: COLORS.accent });
        }

        if (freightCharge > 0) {
            totalsRow("Freight:", `+ ${this.formatCurrency(freightCharge)}`, { color: COLORS.muted });
        }

        doc
            .moveTo(totalsX, ty + 1)
            .lineTo(totalsX + totalsWidth, ty + 1)
            .strokeColor(COLORS.primary)
            .lineWidth(0.75)
            .stroke();
        ty += 7;

        totalsRow("GRAND TOTAL:", this.formatCurrency(grandTotal), {
            bold: true,
            size: 11,
            color: COLORS.primary,
        });

        const cashAmount = Number(sale?.cashAmount ?? 0);
        const upiAmount = Number(sale?.upiAmount ?? 0);
        const bankAmount = Number(sale?.bankAmount ?? 0);
        const upiPlusBank = upiAmount + bankAmount;
        const effectiveCash = (cashAmount === 0 && upiPlusBank === 0 && receivedAmount > 0)
            ? receivedAmount
            : cashAmount;

        const previousOutstanding = sale?.customer
            ? customerOutstanding - balanceAmount
            : 0;
        const totalOutstanding = previousOutstanding + grandTotal;
        const remainingOutstanding = totalOutstanding - effectiveCash - upiPlusBank;
        const currentBillBalance = Math.max(0, grandTotal - effectiveCash - upiPlusBank);

        totalsRow("Previous Outstanding:", this.formatCurrency(previousOutstanding), {
            size: 8.5,
            color: COLORS.text,
        });

        totalsRow("Total Outstanding:", this.formatCurrency(totalOutstanding), {
            bold: true,
            size: 9,
            color: COLORS.text,
        });

        totalsRow("Cash:", this.formatCurrency(effectiveCash), {
            size: 8.5,
            color: COLORS.accent,
        });

        totalsRow("UPI + Bank:", this.formatCurrency(upiPlusBank), {
            size: 8.5,
            color: COLORS.accent,
        });

        // if (currentBillBalance > 0 && previousOutstanding !== 0) {
        //     totalsRow("Bill Amount:", this.formatCurrency(currentBillBalance), {
        //         size: 8.5,
        //         color: COLORS.text,
        //     });
        // }

        totalsRow("Remaining Outstanding:", this.formatCurrency(remainingOutstanding), {
            bold: true,
            size: 9.5,
            color: remainingOutstanding > 0 ? COLORS.primary : COLORS.muted,
        });

        // totalsRow("Payment Mode:", paymentMode, { size: 8, color: COLORS.text });

        const sigLineY = Math.max(qrY + qrSize + 34, ty + 20);
        const sigLineWidth = 150;

        doc
            .moveTo(leftQrX, sigLineY)
            .lineTo(leftQrX + sigLineWidth, sigLineY)
            .strokeColor(COLORS.border)
            .lineWidth(1)
            .stroke();
        doc
            .font("Helvetica-Bold")
            .fontSize(9)
            .fillColor(COLORS.text)
            .text("Party's Signature", leftQrX, sigLineY + 4, { width: sigLineWidth });

        const rightSigX = PAGE_MARGIN + pageWidth - sigLineWidth + 10; // Moved slightly right

        if (authSignBuf) {
            const sigImgW = 80;
            const sigImgH = 30;
            const sigImgX = rightSigX + (sigLineWidth - sigImgW) / 2;
            const sigImgY = sigLineY - sigImgH - 4;
            doc.image(authSignBuf, sigImgX, sigImgY, { width: sigImgW, height: sigImgH });
        }

        doc
            .moveTo(rightSigX, sigLineY)
            .lineTo(rightSigX + sigLineWidth, sigLineY)
            .strokeColor(COLORS.border)
            .lineWidth(1)
            .stroke();
        doc
            .font("Helvetica-Bold")
            .fontSize(9)
            .fillColor(COLORS.text)
            .text("Authorised Signatory", rightSigX, sigLineY + 4, {
                width: sigLineWidth,
                align: "right",
            });

        doc
            .font("Helvetica-Oblique")
            .fontSize(9)
            .fillColor(COLORS.muted)
            .text("This is a computer-generated invoice.", PAGE_MARGIN, doc.page.height - PAGE_MARGIN - 20, {
                width: pageWidth,
                align: "center",
            });
    }

    private static async generateQrBuffer(uri: string): Promise<Buffer | null> {
        try {
            return await QRCode.toBuffer(uri, { margin: 1, type: "png", width: 200 });
        } catch (err) {
            console.error("[InvoicePdf] Failed to generate UPI QR code:", err);
            return null;
        }
    }

    private static buildUpiUri(payeeVpa: string, payeeName: string, amount?: number): string {
        const params = new URLSearchParams();
        params.set("pa", payeeVpa);
        params.set("pn", payeeName || "Merchant");

        if (amount !== undefined) {
            params.set("am", amount.toFixed(2));
        }

        return `upi://pay?${params.toString()}`;
    }

    private static formatCurrency(val: any): string {
        const num = Number(val);
        if (Number.isNaN(num)) return "Rs. 0.00";
        return `Rs. ${num.toFixed(2)}`;
    }

    private static drawAttachedImages(doc: PDFKit.PDFDocument, sale: any, pageWidth: number) {
        let imageList: string[] = [];
        if (sale?.images) {
            try {
                imageList = typeof sale.images === "string" ? JSON.parse(sale.images) : sale.images;
                if (!Array.isArray(imageList)) imageList = [];
            } catch {
                imageList = [];
            }
        }

        if (!imageList || imageList.length === 0) return;

        for (let i = 0; i < imageList.length; i++) {
            const filename = imageList[i];
            if (!filename || typeof filename !== "string") continue;
            const fullPath = path.join(process.cwd(), "uploads/sales", filename);

            if (!fs.existsSync(fullPath)) continue;

            doc.addPage();

            doc
                .font("Helvetica-Bold")
                .fontSize(13)
                .fillColor(COLORS.primary)
                .text(`Invoice Attachment (${i + 1} of ${imageList.length})`, PAGE_MARGIN, PAGE_MARGIN, {
                    align: "left",
                });

            doc
                .font("Helvetica")
                .fontSize(9)
                .fillColor(COLORS.muted)
                .text(`Invoice No: ${sale?.invoiceNo ?? sale?.id}`, PAGE_MARGIN, PAGE_MARGIN + 18, {
                    align: "left",
                });

            doc
                .moveTo(PAGE_MARGIN, PAGE_MARGIN + 32)
                .lineTo(PAGE_MARGIN + pageWidth, PAGE_MARGIN + 32)
                .strokeColor(COLORS.border)
                .lineWidth(1)
                .stroke();

            const maxImageWidth = pageWidth;
            const maxImageHeight = doc.page.height - PAGE_MARGIN * 2 - 50;

            try {
                doc.image(fullPath, PAGE_MARGIN, PAGE_MARGIN + 42, {
                    fit: [maxImageWidth, maxImageHeight],
                    align: "center",
                    valign: "center",
                });
            } catch (err) {
                console.error("[InvoicePdf] Error embedding image into PDF:", fullPath, err);
            }
        }
    }
}
