/**
 * Invoice Generation Service
 * Generates invoices, invoice numbers, and PDF documents.
 */

import PDFDocument from 'pdfkit';
import { getModels } from '../models';
import fs from 'fs';
import path from 'path';

// Ensure invoices directory exists
const INVOICES_DIR = 'uploads/invoices';
if (!fs.existsSync(INVOICES_DIR)) {
  fs.mkdirSync(INVOICES_DIR, { recursive: true });
}

/**
 * Generate a unique invoice number in the format INV-YYYYMMDD-XXXX
 */
export function generateInvoiceNumber(): string {
  const now = new Date();
  const dateStr = now.toISOString().slice(0, 10).replace(/-/g, '');
  const random = Math.random().toString(36).substring(2, 6).toUpperCase();
  return `INV-${dateStr}-${random}`;
}

/**
 * Create an invoice record after a successful payment.
 */
export async function generateInvoice(params: {
  companyId: string;
  subscriptionId: string;
  packageName: string;
  billingCycle: 'monthly' | 'yearly';
  amount: number;
  currency: string;
  paymentGateway: string;
  transactionId?: string;
  taxRate?: number;
  initialStatus?: 'paid' | 'pending';
}): Promise<any> {
  const { Invoice, CompanySubscription } = getModels();

  // Prevent duplicate invoices for the same subscription and billing period
  // Check if there's already a paid/pending invoice for this subscription within the current period
  const subscription = await CompanySubscription.findById(params.subscriptionId);
  if (subscription) {
    const periodStart = subscription.currentPeriodStart || subscription.startDate;
    const periodEnd = subscription.currentPeriodEnd || subscription.endDate;
    const existingInvoice = await Invoice.findOne({
      subscriptionId: params.subscriptionId,
      status: { $in: ['paid', 'pending'] },
      createdAt: { $gte: periodStart, $lte: periodEnd },
    }).sort({ createdAt: -1 });

    if (existingInvoice) {
      console.log(`[Invoice] Skipping duplicate — invoice ${existingInvoice.invoiceNumber} already exists for subscription ${params.subscriptionId} in current period`);
      return existingInvoice;
    }
  }

  const taxRate = params.taxRate || 0;
  const taxAmount = Math.round(params.amount * taxRate * 100) / 100;
  const totalAmount = params.amount + taxAmount;

  // Determine invoice status: paid if transactionId provided or initialStatus='paid', else pending
  const invoiceStatus = params.initialStatus || (params.transactionId ? 'paid' : 'pending');
  const paidAt = invoiceStatus === 'paid' ? new Date() : undefined;

  const invoice = new Invoice({
    companyId: params.companyId,
    subscriptionId: params.subscriptionId,
    invoiceNumber: generateInvoiceNumber(),
    packageName: params.packageName,
    billingCycle: params.billingCycle,
    amount: params.amount,
    currency: params.currency,
    taxAmount,
    taxRate,
    totalAmount,
    paymentGateway: params.paymentGateway,
    transactionId: params.transactionId || undefined,
    status: invoiceStatus,
    paidAt,
  });

  await invoice.save();

  // Generate PDF asynchronously (don't block invoice creation)
  generateInvoicePdf(invoice.id).catch(err => {
    console.error(`[Invoice] Failed to generate PDF for invoice ${invoice.id}:`, err.message);
  });

  return invoice;
}

/**
 * Currency symbols for PDF rendering
 */
const CURRENCY_SYMBOLS: Record<string, string> = {
  USD: '$',
  INR: '₹',
  AED: 'د.إ',
};

/**
 * Generate a PDF invoice document and save it to disk.
 * Returns the relative URL path for downloading.
 */
export async function generateInvoicePdf(invoiceId: string): Promise<string | null> {
  try {
    const { Invoice, Company, SubscriptionPackage, CompanySubscription } = getModels();

    const invoice = await Invoice.findById(invoiceId);
    if (!invoice) {
      console.error(`[Invoice] Invoice not found: ${invoiceId}`);
      return null;
    }

    // Get company name
    const company = await Company.findById(invoice.companyId);
    const companyName = company?.name || 'Unknown Company';

    // Get subscription and package details
    const subscription = await CompanySubscription.findById(invoice.subscriptionId);
    const pkg = subscription ? await SubscriptionPackage.findById(subscription.packageId) : null;

    const currencySymbol = CURRENCY_SYMBOLS[invoice.currency] || '$';
    const billingLabel = invoice.billingCycle === 'yearly' ? 'Yearly' : 'Monthly';
    const statusLabel = invoice.status.charAt(0).toUpperCase() + invoice.status.slice(1);

    // PDF document setup
    const doc = new PDFDocument({
      size: 'A4',
      margins: { top: 60, bottom: 60, left: 60, right: 60 },
      info: {
        Title: `Invoice ${invoice.invoiceNumber}`,
        Author: 'Mengo AI-CMO',
        Subject: `Invoice for ${invoice.packageName}`,
      },
    });

    const filename = `${invoice.invoiceNumber}.pdf`;
    const filepath = path.join(INVOICES_DIR, filename);
    const stream = fs.createWriteStream(filepath);
    doc.pipe(stream);

    // Brand header - neon lime accent line
    const accentColor = '#C8FF2E';
    const darkColor = '#1a1a2e';
    const grayColor = '#666666';
    const lightGrayColor = '#999999';

    // Top accent bar
    doc.rect(0, 0, doc.page.width, 6).fill(accentColor);

    // Company name (Mengo branding)
    doc.fontSize(22).font('Helvetica-Bold').fillColor(darkColor);
    doc.text('Mengo', 60, 40);
    doc.fontSize(10).font('Helvetica').fillColor(grayColor);
    doc.text('AI-Powered Marketing Platform', 60, 65);

    // Invoice title (right-aligned)
    doc.fontSize(20).font('Helvetica-Bold').fillColor(darkColor);
    doc.text('INVOICE', 60, 45, { align: 'right' });

    // Invoice details (right side)
    const detailsY = 72;
    doc.fontSize(9).font('Helvetica').fillColor(grayColor);
    doc.text(`Invoice #: ${invoice.invoiceNumber}`, 400, detailsY, { align: 'right' });
    doc.text(`Date: ${new Date(invoice.paidAt || invoice.createdAt).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}`, 400, detailsY + 14, { align: 'right' });
    doc.text(`Status: ${statusLabel}`, 400, detailsY + 28, { align: 'right' });

    // Horizontal separator
    const separatorY = 110;
    doc.moveTo(60, separatorY).lineTo(doc.page.width - 60, separatorY).strokeColor('#e0e0e0').lineWidth(0.5).stroke();

    // Bill To section
    let y = separatorY + 20;
    doc.fontSize(8).font('Helvetica-Bold').fillColor(grayColor);
    doc.text('BILL TO', 60, y);
    doc.fontSize(12).font('Helvetica-Bold').fillColor(darkColor);
    doc.text(companyName, 60, y + 14);
    doc.fontSize(9).font('Helvetica').fillColor(grayColor);
    if (company?.notificationEmail) {
      doc.text(company.notificationEmail, 60, y + 30);
    }

    // Subscription details (right side)
    doc.fontSize(8).font('Helvetica-Bold').fillColor(grayColor);
    doc.text('SUBSCRIPTION', 400, y, { align: 'right' });
    doc.fontSize(9).font('Helvetica').fillColor(darkColor);
    doc.text(invoice.packageName, 400, y + 14, { align: 'right' });
    doc.text(`${billingLabel} Billing`, 400, y + 28, { align: 'right' });

    if (subscription) {
      const periodStart = subscription.currentPeriodStart
        ? new Date(subscription.currentPeriodStart).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })
        : new Date(subscription.startDate).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
      const periodEnd = subscription.currentPeriodEnd
        ? new Date(subscription.currentPeriodEnd).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' })
        : new Date(subscription.endDate).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
      doc.fontSize(8).font('Helvetica').fillColor(grayColor);
      doc.text(`${periodStart} — ${periodEnd}`, 400, y + 42, { align: 'right' });
    }

    // Line items table header
    const tableY = y + 75;
    doc.moveTo(60, tableY).lineTo(doc.page.width - 60, tableY).strokeColor('#e0e0e0').lineWidth(0.5).stroke();

    doc.fontSize(8).font('Helvetica-Bold').fillColor(grayColor);
    doc.text('DESCRIPTION', 60, tableY + 8);
    doc.text('BILLING', 320, tableY + 8);
    doc.text('AMOUNT', 450, tableY + 8, { align: 'right' });

    doc.moveTo(60, tableY + 24).lineTo(doc.page.width - 60, tableY + 24).strokeColor('#e0e0e0').lineWidth(0.5).stroke();

    // Line item
    const itemY = tableY + 32;
    doc.fontSize(10).font('Helvetica').fillColor(darkColor);
    doc.text(invoice.packageName, 60, itemY);
    doc.text(billingLabel, 320, itemY);
    doc.text(`${currencySymbol}${invoice.amount.toFixed(2)}`, 450, itemY, { align: 'right' });

    // Bottom of line items
    const bottomY = itemY + 30;
    doc.moveTo(60, bottomY).lineTo(doc.page.width - 60, bottomY).strokeColor('#e0e0e0').lineWidth(0.5).stroke();

    // Totals section
    const totalsX = 340;
    let totalsY = bottomY + 12;

    // Subtotal
    doc.fontSize(9).font('Helvetica').fillColor(grayColor);
    doc.text('Subtotal:', totalsX, totalsY, { continued: false, align: 'left' });
    doc.fontSize(9).font('Helvetica').fillColor(darkColor);
    doc.text(`${currencySymbol}${invoice.amount.toFixed(2)}`, 450, totalsY, { align: 'right' });

    // Tax (if applicable)
    if (invoice.taxRate && invoice.taxRate > 0) {
      totalsY += 18;
      doc.fontSize(9).font('Helvetica').fillColor(grayColor);
      doc.text(`Tax (${(invoice.taxRate * 100).toFixed(1)}%):`, totalsX, totalsY);
      doc.fontSize(9).font('Helvetica').fillColor(darkColor);
      doc.text(`${currencySymbol}${(invoice.taxAmount || 0).toFixed(2)}`, 450, totalsY, { align: 'right' });
    }

    // Total line
    totalsY += 22;
    doc.moveTo(totalsX, totalsY).lineTo(doc.page.width - 60, totalsY).strokeColor(darkColor).lineWidth(1).stroke();

    totalsY += 8;
    doc.fontSize(11).font('Helvetica-Bold').fillColor(darkColor);
    doc.text('Total:', totalsX, totalsY);
    doc.text(`${currencySymbol}${invoice.totalAmount.toFixed(2)}`, 450, totalsY, { align: 'right' });

    // Payment info
    totalsY += 35;
    doc.fontSize(8).font('Helvetica').fillColor(grayColor);
    doc.text(`Payment Method: ${invoice.paymentGateway.charAt(0).toUpperCase() + invoice.paymentGateway.slice(1)}`, 60, totalsY);
    if (invoice.paidAt) {
      doc.text(`Paid on: ${new Date(invoice.paidAt).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}`, 60, totalsY + 14);
    }

    // Footer
    const footerY = doc.page.height - 80;
    doc.moveTo(60, footerY).lineTo(doc.page.width - 60, footerY).strokeColor('#e0e0e0').lineWidth(0.5).stroke();
    doc.fontSize(8).font('Helvetica').fillColor(lightGrayColor);
    doc.text('This is a system-generated invoice from Mengo AI-CMO.', 60, footerY + 10, { align: 'center' });
    doc.text(`Invoice ${invoice.invoiceNumber} • ${new Date(invoice.createdAt).toLocaleDateString('en-US', { year: 'numeric', month: 'long' })}`, 60, footerY + 24, { align: 'center' });

    // Finalize
    doc.end();

    // Wait for the stream to finish writing
    await new Promise<void>((resolve, reject) => {
      stream.on('finish', resolve);
      stream.on('error', reject);
    });

    // Update invoice with pdfUrl
    const pdfUrl = `/uploads/invoices/${filename}`;
    invoice.pdfUrl = pdfUrl;
    await invoice.save();

    console.log(`[Invoice] PDF generated successfully: ${filepath}`);
    return pdfUrl;
  } catch (error: any) {
    console.error(`[Invoice] Error generating PDF for invoice ${invoiceId}:`, error.message);
    return null;
  }
}

/**
 * Get the file path for an invoice PDF on disk.
 * Returns null if the file does not exist.
 */
export function getInvoicePdfPath(invoiceNumber: string): string | null {
  const filepath = path.join(INVOICES_DIR, `${invoiceNumber}.pdf`);
  if (fs.existsSync(filepath)) {
    return filepath;
  }
  return null;
}