"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var InvoiceService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.InvoiceService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const invoice_entity_1 = require("../database/entities/invoice.entity");
const payment_entity_1 = require("../database/entities/payment.entity");
const business_entity_1 = require("../database/entities/business.entity");
const customer_entity_1 = require("../database/entities/customer.entity");
const PDFDocument = require("pdfkit");
const fs = require("fs");
const PDFDocumentClass = PDFDocument;
const pdfConstructor = typeof PDFDocumentClass === 'function' ? PDFDocumentClass : PDFDocumentClass.default || PDFDocumentClass;
let InvoiceService = InvoiceService_1 = class InvoiceService {
    constructor(invoiceRepo, paymentRepo, businessRepo, customerRepo) {
        this.invoiceRepo = invoiceRepo;
        this.paymentRepo = paymentRepo;
        this.businessRepo = businessRepo;
        this.customerRepo = customerRepo;
        this.logger = new common_1.Logger(InvoiceService_1.name);
    }
    async generateInvoice(paymentId) {
        const existingInvoice = await this.invoiceRepo.findOne({
            where: { paymentId },
            relations: ['payment', 'payment.customer', 'payment.package', 'business', 'customer'],
        });
        if (existingInvoice) {
            const pdfBuffer = await this.buildInvoicePdf(existingInvoice);
            return {
                invoiceNumber: existingInvoice.invoiceNumber,
                pdfBuffer,
            };
        }
        const payment = await this.paymentRepo.findOne({
            where: { id: paymentId },
            relations: ['customer', 'package', 'business'],
        });
        if (!payment) {
            throw new common_1.NotFoundException('Payment not found');
        }
        if (!payment.customerId) {
            throw new common_1.NotFoundException('Cannot generate invoice for a payment without a customer');
        }
        const business = await this.businessRepo.findOne({
            where: { id: payment.businessId },
        });
        const datePart = new Date().toISOString().slice(0, 10).replace(/-/g, '');
        const count = await this.invoiceRepo.count();
        const seq = String(count + 1).padStart(5, '0');
        const invoiceNumber = `INV-${datePart}-${seq}`;
        const subtotal = Number(payment.amount);
        const taxRate = payment.package?.taxRate ? Number(payment.package.taxRate) : 16;
        const taxInclusive = payment.package?.taxInclusive ?? true;
        let taxAmount;
        let totalAmount;
        if (taxInclusive) {
            totalAmount = subtotal;
            taxAmount = subtotal - subtotal / (1 + taxRate / 100);
        }
        else {
            taxAmount = subtotal * (taxRate / 100);
            totalAmount = subtotal + taxAmount;
        }
        const invoice = this.invoiceRepo.create({
            businessId: payment.businessId,
            paymentId: payment.id,
            customerId: payment.customerId,
            invoiceNumber,
            amount: subtotal,
            taxAmount: Math.round(taxAmount * 100) / 100,
            totalAmount: Math.round(totalAmount * 100) / 100,
            status: payment.status === payment_entity_1.PaymentStatus.SUCCESSFUL ? 'PAID' : 'PENDING',
            items: {
                description: payment.package?.name || 'WiFi Package',
                quantity: 1,
                unitPrice: Number(payment.amount),
                total: Number(payment.amount),
            },
            dueDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
            paidAt: payment.status === payment_entity_1.PaymentStatus.SUCCESSFUL ? new Date() : null,
        });
        const savedInvoice = await this.invoiceRepo.save(invoice);
        savedInvoice.payment = payment;
        savedInvoice.business = business;
        if (payment.customer) {
            savedInvoice.customer = payment.customer;
        }
        const pdfBuffer = await this.buildInvoicePdf(savedInvoice);
        this.logger.log(`Invoice generated: ${invoiceNumber} for payment ${paymentId}`);
        return { invoiceNumber, pdfBuffer };
    }
    async buildInvoicePdf(invoice) {
        return new Promise((resolve, reject) => {
            const chunks = [];
            const doc = new pdfConstructor({
                size: 'A4',
                margin: 50,
                info: {
                    Title: `Invoice ${invoice.invoiceNumber}`,
                    Author: invoice.business?.name || 'HBS WiFi',
                },
            });
            doc.on('data', (chunk) => chunks.push(chunk));
            doc.on('end', () => resolve(Buffer.concat(chunks)));
            doc.on('error', reject);
            this.renderInvoiceContent(doc, invoice);
        });
    }
    renderInvoiceContent(doc, invoice) {
        const business = invoice.business;
        const customer = invoice.customer;
        const payment = invoice.payment;
        if (business?.logo) {
            try {
                const logoPath = business.logo;
                if (fs.existsSync(logoPath)) {
                    doc.image(logoPath, 50, 40, { width: 100 });
                }
            }
            catch {
                this.logger.warn('Could not load business logo');
            }
        }
        doc.fontSize(18).font('Helvetica-Bold').fillColor('#0055AA');
        doc.text('INVOICE', { align: 'right' });
        doc.fontSize(12).font('Helvetica').fillColor('#000000');
        doc.text(`# ${invoice.invoiceNumber}`, { align: 'right' });
        doc.moveDown(0.5);
        doc
            .moveTo(50, doc.y)
            .lineTo(doc.page.width - 50, doc.y)
            .stroke('#cccccc');
        doc.moveDown(0.5);
        doc.fontSize(10).font('Helvetica-Bold');
        doc.text('FROM:', { continued: false });
        doc.font('Helvetica');
        if (business) {
            doc.text(business.name || '');
            if (business.address)
                doc.text(business.address);
            if (business.phone)
                doc.text(`Tel: ${business.phone}`);
            if (business.email)
                doc.text(`Email: ${business.email}`);
        }
        doc.moveDown(0.5);
        doc.font('Helvetica-Bold');
        doc.text('BILL TO:', { continued: false });
        doc.font('Helvetica');
        if (customer) {
            const name = `${customer.firstName || ''} ${customer.lastName || ''}`.trim();
            if (name)
                doc.text(name);
            if (customer.phoneNumber)
                doc.text(`Phone: ${customer.phoneNumber}`);
            if (customer.email)
                doc.text(`Email: ${customer.email}`);
        }
        doc.moveDown(0.5);
        doc.font('Helvetica-Bold');
        doc.text('INVOICE DETAILS:');
        doc.font('Helvetica');
        doc.fontSize(8);
        const leftX = 50;
        const rightX = 300;
        const col1X = leftX;
        const col2X = rightX;
        const addDetail = (label, value) => {
            doc.font('Helvetica-Bold').text(label, col1X, doc.y, { continued: true, width: 80 });
            doc.font('Helvetica').text(value, col2X);
        };
        addDetail('Invoice Date:', new Date().toLocaleDateString('en-KE'));
        addDetail('Due Date:', invoice.dueDate ? new Date(invoice.dueDate).toLocaleDateString('en-KE') : 'N/A');
        addDetail('Status:', invoice.status);
        if (payment?.mpesaReceipt) {
            addDetail('M-Pesa Receipt:', payment.mpesaReceipt);
        }
        if (payment?.internalReference) {
            addDetail('Reference:', payment.internalReference);
        }
        doc.moveDown(1);
        const tableTop = doc.y;
        const tableLeft = 50;
        const colWidths = [200, 60, 80, 120];
        const headers = ['Description', 'Qty', 'Unit Price', 'Total'];
        doc.fontSize(8).font('Helvetica-Bold').fillColor('#ffffff');
        doc
            .rect(tableLeft, tableTop, colWidths.reduce((a, b) => a + b, 0), 20)
            .fill('#0055AA');
        doc.fillColor('#ffffff');
        let currentX = tableLeft;
        headers.forEach((header, i) => {
            doc.text(header, currentX + 5, tableTop + 5, { width: colWidths[i] - 10, align: 'left' });
            currentX += colWidths[i];
        });
        doc.fillColor('#000000').font('Helvetica');
        const rowY = tableTop + 25;
        currentX = tableLeft;
        const items = invoice.items;
        const packageName = items?.description || (payment?.package?.name || 'WiFi Package');
        doc.text(packageName, currentX + 5, rowY, { width: colWidths[0] - 10 });
        currentX += colWidths[0];
        doc.text('1', currentX + 5, rowY, { width: colWidths[1] - 10, align: 'center' });
        currentX += colWidths[1];
        doc.text(`KES ${Number(invoice.amount).toFixed(2)}`, currentX + 5, rowY, {
            width: colWidths[2] - 10,
            align: 'right',
        });
        currentX += colWidths[2];
        doc.text(`KES ${Number(invoice.totalAmount).toFixed(2)}`, currentX + 5, rowY, {
            width: colWidths[3] - 10,
            align: 'right',
        });
        doc.moveDown(3);
        doc
            .moveTo(50, doc.y)
            .lineTo(doc.page.width - 50, doc.y)
            .stroke('#cccccc');
        doc.moveDown(0.5);
        const summaryX = doc.page.width - 50 - 200;
        doc.font('Helvetica').fontSize(9);
        doc.text('Subtotal:', summaryX, doc.y, { continued: true, width: 80 });
        doc.font('Helvetica-Bold').text(`KES ${Number(invoice.amount).toFixed(2)}`, { align: 'right' });
        doc.font('Helvetica');
        doc.text('Tax (16%):', summaryX, doc.y, { continued: true, width: 80 });
        doc.font('Helvetica-Bold').text(`KES ${Number(invoice.taxAmount).toFixed(2)}`, { align: 'right' });
        doc
            .moveTo(summaryX, doc.y)
            .lineTo(doc.page.width - 50, doc.y)
            .stroke('#0055AA');
        doc.moveDown(0.3);
        doc.font('Helvetica-Bold').fontSize(11).fillColor('#0055AA');
        doc.text('Total Due:', summaryX, doc.y, { continued: true, width: 80 });
        doc.text(`KES ${Number(invoice.totalAmount).toFixed(2)}`, { align: 'right' });
        doc.moveDown(1.5);
        doc.fontSize(8).font('Helvetica').fillColor('#999999');
        doc.text('Thank you for your business!', { align: 'center' });
        doc.text('This is a computer-generated invoice.', { align: 'center' });
        doc.end();
    }
};
exports.InvoiceService = InvoiceService;
exports.InvoiceService = InvoiceService = InvoiceService_1 = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(invoice_entity_1.Invoice)),
    __param(1, (0, typeorm_1.InjectRepository)(payment_entity_1.Payment)),
    __param(2, (0, typeorm_1.InjectRepository)(business_entity_1.Business)),
    __param(3, (0, typeorm_1.InjectRepository)(customer_entity_1.Customer)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository])
], InvoiceService);
