// Email service for Spanglish platform
// Supports multiple email providers: Resend, SMTP (Nodemailer)

import { db, dbGet, dbAll, emailTemplates, emailLogs, events, tickets, payments, users, paymentOptions, eventPaymentOverrides, siteSettings } from '../db/index.js';
import { eq, and } from 'drizzle-orm';
import { getNow, generateId } from './utils.js';
import { 
  replaceTemplateVariables, 
  wrapInBaseTemplate, 
  defaultTemplates,
  type DefaultTemplate 
} from './emailTemplates.js';
import { enqueueBulkEmails, type TemplateEmailJobParams } from './emailQueue.js';
import nodemailer from 'nodemailer';
import type { Transporter } from 'nodemailer';

// ==================== Types ====================

interface SendEmailOptions {
  to: string | string[];
  subject: string;
  html: string;
  text?: string;
  replyTo?: string;
}

interface SendEmailResult {
  success: boolean;
  messageId?: string;
  error?: string;
}

type EmailProvider = 'resend' | 'smtp' | 'console';

// ==================== Provider Configuration ====================

function getEmailProvider(): EmailProvider {
  const provider = (process.env.EMAIL_PROVIDER || 'console').toLowerCase();
  if (provider === 'resend' || provider === 'smtp' || provider === 'console') {
    return provider;
  }
  console.warn(`[Email] Unknown provider "${provider}", falling back to console`);
  return 'console';
}

function getFromEmail(): string {
  return process.env.EMAIL_FROM || 'noreply@spanglish.com';
}

function getFromName(): string {
  return process.env.EMAIL_FROM_NAME || 'Spanglish';
}

// ==================== SMTP Configuration ====================

interface SMTPConfig {
  host: string;
  port: number;
  secure: boolean;
  auth?: {
    user: string;
    pass: string;
  };
}

function getSMTPConfig(): SMTPConfig | null {
  const host = process.env.SMTP_HOST;
  const port = parseInt(process.env.SMTP_PORT || '587');
  const user = process.env.SMTP_USER;
  const pass = process.env.SMTP_PASS;
  const secure = process.env.SMTP_SECURE === 'true' || port === 465;
  
  if (!host) {
    return null;
  }
  
  const config: SMTPConfig = {
    host,
    port,
    secure,
  };
  
  if (user && pass) {
    config.auth = { user, pass };
  }
  
  return config;
}

// Cached SMTP transporter
let smtpTransporter: Transporter | null = null;

function getSMTPTransporter(): Transporter | null {
  if (smtpTransporter) {
    return smtpTransporter;
  }
  
  const config = getSMTPConfig();
  if (!config) {
    console.error('[Email] SMTP configuration missing');
    return null;
  }
  
  smtpTransporter = nodemailer.createTransport({
    host: config.host,
    port: config.port,
    secure: config.secure,
    auth: config.auth,
    // Additional options for better deliverability
    pool: true,
    maxConnections: 5,
    maxMessages: 100,
    // TLS options
    tls: {
      rejectUnauthorized: process.env.SMTP_TLS_REJECT_UNAUTHORIZED !== 'false',
    },
  });
  
  // Verify connection configuration
  smtpTransporter.verify((error, success) => {
    if (error) {
      console.error('[Email] SMTP connection verification failed:', error.message);
    } else {
      console.log('[Email] SMTP server is ready to send emails');
    }
  });
  
  return smtpTransporter;
}

// ==================== Email Providers ====================

/**
 * Send email using Resend API
 */
async function sendWithResend(options: SendEmailOptions): Promise<SendEmailResult> {
  const apiKey = process.env.EMAIL_API_KEY || process.env.RESEND_API_KEY;
  const fromEmail = getFromEmail();
  const fromName = getFromName();
  
  if (!apiKey) {
    console.error('[Email] Resend API key not configured');
    return { success: false, error: 'Resend API key not configured' };
  }
  
  try {
    const response = await fetch('https://api.resend.com/emails', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        from: `${fromName} <${fromEmail}>`,
        to: Array.isArray(options.to) ? options.to : [options.to],
        subject: options.subject,
        html: options.html,
        text: options.text,
        reply_to: options.replyTo,
      }),
    });
    
    const data = await response.json();
    
    if (!response.ok) {
      console.error('[Email] Resend API error:', data);
      return { 
        success: false, 
        error: data.message || data.error || 'Failed to send email' 
      };
    }
    
    console.log('[Email] Email sent via Resend:', data.id);
    return { 
      success: true, 
      messageId: data.id 
    };
  } catch (error: any) {
    console.error('[Email] Resend error:', error);
    return { 
      success: false, 
      error: error.message || 'Failed to send email via Resend' 
    };
  }
}

/**
 * Send email using SMTP (Nodemailer)
 */
async function sendWithSMTP(options: SendEmailOptions): Promise<SendEmailResult> {
  const transporter = getSMTPTransporter();
  
  if (!transporter) {
    return { success: false, error: 'SMTP not configured' };
  }
  
  const fromEmail = getFromEmail();
  const fromName = getFromName();
  
  try {
    const info = await transporter.sendMail({
      from: `"${fromName}" <${fromEmail}>`,
      to: Array.isArray(options.to) ? options.to.join(', ') : options.to,
      replyTo: options.replyTo,
      subject: options.subject,
      html: options.html,
      text: options.text,
    });
    
    console.log('[Email] Email sent via SMTP:', info.messageId);
    return { 
      success: true, 
      messageId: info.messageId 
    };
  } catch (error: any) {
    console.error('[Email] SMTP error:', error);
    return { 
      success: false, 
      error: error.message || 'Failed to send email via SMTP' 
    };
  }
}

/**
 * Console logger for development/testing (no actual email sent)
 */
async function sendWithConsole(options: SendEmailOptions): Promise<SendEmailResult> {
  const to = Array.isArray(options.to) ? options.to.join(', ') : options.to;
  
  console.log('\n========================================');
  console.log('[Email] Console Mode - Email Preview');
  console.log('========================================');
  console.log(`To: ${to}`);
  console.log(`Subject: ${options.subject}`);
  console.log(`Reply-To: ${options.replyTo || 'N/A'}`);
  console.log('----------------------------------------');
  console.log('HTML Body (truncated):');
  console.log(options.html?.substring(0, 500) + '...');
  console.log('========================================\n');
  
  return { 
    success: true, 
    messageId: `console-${Date.now()}` 
  };
}

/**
 * Main send function that routes to the appropriate provider
 */
async function sendEmail(options: SendEmailOptions): Promise<SendEmailResult> {
  const provider = getEmailProvider();
  
  console.log(`[Email] Sending email via ${provider} to ${Array.isArray(options.to) ? options.to.join(', ') : options.to}`);
  
  switch (provider) {
    case 'resend':
      return sendWithResend(options);
    case 'smtp':
      return sendWithSMTP(options);
    case 'console':
    default:
      return sendWithConsole(options);
  }
}

// ==================== Email Service ====================

export const emailService = {
  /**
   * Get current email provider info
   */
  getProviderInfo(): { provider: EmailProvider; configured: boolean } {
    const provider = getEmailProvider();
    let configured = false;
    
    switch (provider) {
      case 'resend':
        configured = !!(process.env.EMAIL_API_KEY || process.env.RESEND_API_KEY);
        break;
      case 'smtp':
        configured = !!process.env.SMTP_HOST;
        break;
      case 'console':
        configured = true;
        break;
    }
    
    return { provider, configured };
  },
  
  /**
   * Test email configuration by sending a test email
   */
  async testConnection(to: string): Promise<SendEmailResult> {
    const { provider, configured } = this.getProviderInfo();
    
    if (!configured) {
      return { success: false, error: `Email provider "${provider}" is not configured` };
    }
    
    return sendEmail({
      to,
      subject: 'Spanglish - Email Test',
      html: `
        <h2>Email Configuration Test</h2>
        <p>This is a test email from your Spanglish platform.</p>
        <p><strong>Provider:</strong> ${provider}</p>
        <p><strong>Timestamp:</strong> ${new Date().toISOString()}</p>
        <p>If you received this email, your email configuration is working correctly!</p>
      `,
      text: `Email Configuration Test\n\nProvider: ${provider}\nTimestamp: ${new Date().toISOString()}\n\nIf you received this email, your email configuration is working correctly!`,
    });
  },
  
  /**
   * Get common variables for all emails
   */
  getCommonVariables(): Record<string, string> {
    return {
      siteName: 'Spanglish',
      siteUrl: process.env.FRONTEND_URL || 'https://spanglish.com',
      currentYear: new Date().getFullYear().toString(),
      supportEmail: process.env.EMAIL_FROM || 'hello@spanglish.com',
    };
  },
  
  /**
   * Get the site timezone from settings (cached for performance)
   */
  async getSiteTimezone(): Promise<string> {
    const settings = await dbGet<any>(
      (db as any).select().from(siteSettings).limit(1)
    );
    return settings?.timezone || 'America/Asuncion';
  },
  
  /**
   * Format date for emails using site timezone
   */
  formatDate(dateStr: string, locale: string = 'en', timezone: string = 'America/Asuncion'): string {
    const date = new Date(dateStr);
    return date.toLocaleDateString(locale === 'es' ? 'es-ES' : 'en-US', {
      weekday: 'long',
      year: 'numeric',
      month: 'long',
      day: 'numeric',
      timeZone: timezone,
    });
  },
  
  /**
   * Format time for emails using site timezone
   */
  formatTime(dateStr: string, locale: string = 'en', timezone: string = 'America/Asuncion'): string {
    const date = new Date(dateStr);
    return date.toLocaleTimeString(locale === 'es' ? 'es-ES' : 'en-US', {
      hour: '2-digit',
      minute: '2-digit',
      timeZone: timezone,
    });
  },
  
  /**
   * Format currency
   */
  formatCurrency(amount: number, currency: string = 'PYG'): string {
    if (currency === 'PYG') {
      return `${amount.toLocaleString('es-PY')} PYG`;
    }
    return `$${amount.toFixed(2)} ${currency}`;
  },
  
  /**
   * Get a template by slug
   */
  async getTemplate(slug: string): Promise<any | null> {
    const template = await dbGet(
      (db as any)
        .select()
        .from(emailTemplates)
        .where(eq((emailTemplates as any).slug, slug))
    );
    
    return template || null;
  },
  
  /**
   * Seed default templates if they don't exist, and update system templates with latest content
   */
  async seedDefaultTemplates(): Promise<void> {
    console.log('[Email] Checking for default templates...');
    
    for (const template of defaultTemplates) {
      const existing = await this.getTemplate(template.slug);
      const now = getNow();
      
      if (!existing) {
        console.log(`[Email] Creating template: ${template.name}`);
        
        await (db as any).insert(emailTemplates).values({
          id: generateId(),
          name: template.name,
          slug: template.slug,
          subject: template.subject,
          subjectEs: template.subjectEs,
          bodyHtml: template.bodyHtml,
          bodyHtmlEs: template.bodyHtmlEs,
          bodyText: template.bodyText,
          bodyTextEs: template.bodyTextEs,
          description: template.description,
          variables: JSON.stringify(template.variables),
          isSystem: template.isSystem ? 1 : 0,
          isActive: 1,
          createdAt: now,
          updatedAt: now,
        });
      } else if (existing.isSystem) {
        // Update system templates with latest content from defaults
        console.log(`[Email] Updating system template: ${template.name}`);
        
        await (db as any)
          .update(emailTemplates)
          .set({
            subject: template.subject,
            subjectEs: template.subjectEs,
            bodyHtml: template.bodyHtml,
            bodyHtmlEs: template.bodyHtmlEs,
            bodyText: template.bodyText,
            bodyTextEs: template.bodyTextEs,
            description: template.description,
            variables: JSON.stringify(template.variables),
            updatedAt: now,
          })
          .where(eq((emailTemplates as any).slug, template.slug));
      }
    }
    
    console.log('[Email] Default templates check complete');
  },
  
  /**
   * Send an email using a template
   */
  async sendTemplateEmail(params: {
    templateSlug: string;
    to: string;
    toName?: string;
    variables: Record<string, any>;
    locale?: string;
    eventId?: string;
    sentBy?: string;
  }): Promise<{ success: boolean; logId?: string; error?: string }> {
    const { templateSlug, to, toName, variables, locale = 'en', eventId, sentBy } = params;
    
    // Get template
    const template = await this.getTemplate(templateSlug);
    if (!template) {
      return { success: false, error: `Template "${templateSlug}" not found` };
    }
    
    // Build variables
    const allVariables = {
      ...this.getCommonVariables(),
      lang: locale,
      ...variables,
    };
    
    // Get localized content
    const subject = locale === 'es' && template.subjectEs 
      ? template.subjectEs 
      : template.subject;
    const bodyHtml = locale === 'es' && template.bodyHtmlEs 
      ? template.bodyHtmlEs 
      : template.bodyHtml;
    const bodyText = locale === 'es' && template.bodyTextEs 
      ? template.bodyTextEs 
      : template.bodyText;
    
    // Replace variables
    const finalSubject = replaceTemplateVariables(subject, allVariables);
    const finalBodyContent = replaceTemplateVariables(bodyHtml, allVariables);
    const finalBodyHtml = wrapInBaseTemplate(finalBodyContent, { ...allVariables, subject: finalSubject });
    const finalBodyText = bodyText ? replaceTemplateVariables(bodyText, allVariables) : undefined;
    
    // Create log entry
    const logId = generateId();
    const now = getNow();
    
    await (db as any).insert(emailLogs).values({
      id: logId,
      templateId: template.id,
      eventId: eventId || null,
      recipientEmail: to,
      recipientName: toName || null,
      subject: finalSubject,
      bodyHtml: finalBodyHtml,
      status: 'pending',
      sentBy: sentBy || null,
      createdAt: now,
    });
    
    // Send email
    const result = await sendEmail({
      to,
      subject: finalSubject,
      html: finalBodyHtml,
      text: finalBodyText,
    });
    
    // Update log with result
    if (result.success) {
      await (db as any)
        .update(emailLogs)
        .set({ 
          status: 'sent',
          sentAt: getNow(),
        })
        .where(eq((emailLogs as any).id, logId));
    } else {
      await (db as any)
        .update(emailLogs)
        .set({ 
          status: 'failed',
          errorMessage: result.error,
        })
        .where(eq((emailLogs as any).id, logId));
    }
    
    return { 
      success: result.success, 
      logId,
      error: result.error 
    };
  },
  
  /**
   * Send booking confirmation email
   * Supports multi-ticket bookings - includes all tickets in the booking
   */
  async sendBookingConfirmation(ticketId: string): Promise<{ success: boolean; error?: string }> {
    // Get ticket with event info
    const ticket = await dbGet<any>(
      (db as any)
        .select()
        .from(tickets)
        .where(eq((tickets as any).id, ticketId))
    );
    
    if (!ticket) {
      return { success: false, error: 'Ticket not found' };
    }
    
    const event = await dbGet<any>(
      (db as any)
        .select()
        .from(events)
        .where(eq((events as any).id, ticket.eventId))
    );
    
    if (!event) {
      return { success: false, error: 'Event not found' };
    }
    
    // Get all tickets in this booking (if multi-ticket)
    let allTickets: any[] = [ticket];
    if (ticket.bookingId) {
      allTickets = await dbAll(
        (db as any)
          .select()
          .from(tickets)
          .where(eq((tickets as any).bookingId, ticket.bookingId))
      );
    }
    
    const ticketCount = allTickets.length;
    const locale = ticket.preferredLanguage || 'en';
    const eventTitle = locale === 'es' && event.titleEs ? event.titleEs : event.title;
    
    // Generate ticket PDF URL (primary ticket, or use combined endpoint for multi)
    const apiUrl = process.env.API_URL || 'http://localhost:3001';
    const ticketPdfUrl = ticketCount > 1 && ticket.bookingId
      ? `${apiUrl}/api/tickets/booking/${ticket.bookingId}/pdf`
      : `${apiUrl}/api/tickets/${ticket.id}/pdf`;
    
    const attendeeFullName = `${ticket.attendeeFirstName} ${ticket.attendeeLastName || ''}`.trim();
    
    // Build attendee list for multi-ticket emails
    const attendeeNames = allTickets.map(t => 
      `${t.attendeeFirstName} ${t.attendeeLastName || ''}`.trim()
    ).join(', ');
    
    // Calculate total price for multi-ticket bookings
    const totalPrice = event.price * ticketCount;
    
    // Get site timezone for proper date/time formatting
    const timezone = await this.getSiteTimezone();
    
    return this.sendTemplateEmail({
      templateSlug: 'booking-confirmation',
      to: ticket.attendeeEmail,
      toName: attendeeFullName,
      locale,
      eventId: event.id,
      variables: {
        attendeeName: attendeeFullName,
        attendeeEmail: ticket.attendeeEmail,
        ticketId: ticket.id,
        bookingId: ticket.bookingId || ticket.id,
        qrCode: ticket.qrCode || '',
        ticketPdfUrl,
        eventTitle,
        eventDate: this.formatDate(event.startDatetime, locale, timezone),
        eventTime: this.formatTime(event.startDatetime, locale, timezone),
        eventLocation: event.location,
        eventLocationUrl: event.locationUrl || '',
        eventPrice: this.formatCurrency(event.price, event.currency),
        // Multi-ticket specific variables
        ticketCount: ticketCount.toString(),
        totalPrice: this.formatCurrency(totalPrice, event.currency),
        attendeeNames,
        isMultiTicket: ticketCount > 1 ? 'true' : 'false',
      },
    });
  },
  
  /**
   * Send payment receipt email
   */
  async sendPaymentReceipt(paymentId: string): Promise<{ success: boolean; error?: string }> {
    // Get payment with ticket and event info
    const payment = await dbGet<any>(
      (db as any)
        .select()
        .from(payments)
        .where(eq((payments as any).id, paymentId))
    );
    
    if (!payment) {
      return { success: false, error: 'Payment not found' };
    }
    
    const ticket = await dbGet<any>(
      (db as any)
        .select()
        .from(tickets)
        .where(eq((tickets as any).id, payment.ticketId))
    );
    
    if (!ticket) {
      return { success: false, error: 'Ticket not found' };
    }
    
    const event = await dbGet<any>(
      (db as any)
        .select()
        .from(events)
        .where(eq((events as any).id, ticket.eventId))
    );
    
    if (!event) {
      return { success: false, error: 'Event not found' };
    }
    
    // Calculate total amount for multi-ticket bookings
    let totalAmount = payment.amount;
    let ticketCount = 1;
    
    if (ticket.bookingId) {
      // Get all payments for this booking
      const bookingTickets = await dbAll<any>(
        (db as any)
          .select()
          .from(tickets)
          .where(eq((tickets as any).bookingId, ticket.bookingId))
      );
      
      ticketCount = bookingTickets.length;
      
      // Sum up all payment amounts for the booking
      const bookingPayments = await Promise.all(
        bookingTickets.map((t: any) => 
          dbGet<any>((db as any).select().from(payments).where(eq((payments as any).ticketId, t.id)))
        )
      );
      
      totalAmount = bookingPayments
        .filter((p: any) => p)
        .reduce((sum: number, p: any) => sum + Number(p.amount || 0), 0);
    }
    
    const locale = ticket.preferredLanguage || 'en';
    const eventTitle = locale === 'es' && event.titleEs ? event.titleEs : event.title;
    
    const paymentMethodNames: Record<string, Record<string, string>> = {
      en: { bancard: 'Card', lightning: 'Lightning (Bitcoin)', cash: 'Cash', bank_transfer: 'Bank Transfer', tpago: 'TPago' },
      es: { bancard: 'Tarjeta', lightning: 'Lightning (Bitcoin)', cash: 'Efectivo', bank_transfer: 'Transferencia Bancaria', tpago: 'TPago' },
    };
    
    const receiptFullName = `${ticket.attendeeFirstName} ${ticket.attendeeLastName || ''}`.trim();
    
    // Format amount with ticket count info for multi-ticket bookings
    const amountDisplay = ticketCount > 1 
      ? `${this.formatCurrency(totalAmount, payment.currency)} (${ticketCount} tickets)`
      : this.formatCurrency(totalAmount, payment.currency);
    
    // Get site timezone for proper date/time formatting
    const timezone = await this.getSiteTimezone();
    
    return this.sendTemplateEmail({
      templateSlug: 'payment-receipt',
      to: ticket.attendeeEmail,
      toName: receiptFullName,
      locale,
      eventId: event.id,
      variables: {
        attendeeName: receiptFullName,
        ticketId: ticket.bookingId || ticket.id,
        eventTitle,
        eventDate: this.formatDate(event.startDatetime, locale, timezone),
        paymentAmount: amountDisplay,
        paymentMethod: paymentMethodNames[locale]?.[payment.provider] || payment.provider,
        paymentReference: payment.reference || payment.id,
        paymentDate: this.formatDate(payment.paidAt || payment.createdAt, locale, timezone),
      },
    });
  },
  
  /**
   * Get merged payment configuration for an event (global + overrides)
   */
  async getPaymentConfig(eventId: string): Promise<Record<string, any>> {
    // Get global options
    const globalOptions = await dbGet<any>(
      (db as any)
        .select()
        .from(paymentOptions)
    );

    // Get event overrides
    const overrides = await dbGet<any>(
      (db as any)
        .select()
        .from(eventPaymentOverrides)
        .where(eq((eventPaymentOverrides as any).eventId, eventId))
    );

    // Defaults
    const defaults = {
      tpagoEnabled: false,
      tpagoLink: null,
      tpagoInstructions: null,
      tpagoInstructionsEs: null,
      bankTransferEnabled: false,
      bankName: null,
      bankAccountHolder: null,
      bankAccountNumber: null,
      bankAlias: null,
      bankPhone: null,
      bankNotes: null,
      bankNotesEs: null,
    };

    const global = globalOptions || defaults;

    // Merge: override values take precedence if they're not null/undefined
    return {
      tpagoEnabled: overrides?.tpagoEnabled ?? global.tpagoEnabled,
      tpagoLink: overrides?.tpagoLink ?? global.tpagoLink,
      tpagoInstructions: overrides?.tpagoInstructions ?? global.tpagoInstructions,
      tpagoInstructionsEs: overrides?.tpagoInstructionsEs ?? global.tpagoInstructionsEs,
      bankTransferEnabled: overrides?.bankTransferEnabled ?? global.bankTransferEnabled,
      bankName: overrides?.bankName ?? global.bankName,
      bankAccountHolder: overrides?.bankAccountHolder ?? global.bankAccountHolder,
      bankAccountNumber: overrides?.bankAccountNumber ?? global.bankAccountNumber,
      bankAlias: overrides?.bankAlias ?? global.bankAlias,
      bankPhone: overrides?.bankPhone ?? global.bankPhone,
      bankNotes: overrides?.bankNotes ?? global.bankNotes,
      bankNotesEs: overrides?.bankNotesEs ?? global.bankNotesEs,
    };
  },

  /**
   * Send payment instructions email (for TPago or Bank Transfer)
   * This email is sent immediately after user clicks "Continue to Payment"
   */
  async sendPaymentInstructions(ticketId: string): Promise<{ success: boolean; error?: string }> {
    // Get ticket
    const ticket = await dbGet<any>(
      (db as any)
        .select()
        .from(tickets)
        .where(eq((tickets as any).id, ticketId))
    );

    if (!ticket) {
      return { success: false, error: 'Ticket not found' };
    }

    // Get event
    const event = await dbGet<any>(
      (db as any)
        .select()
        .from(events)
        .where(eq((events as any).id, ticket.eventId))
    );

    if (!event) {
      return { success: false, error: 'Event not found' };
    }

    // Get payment
    const payment = await dbGet<any>(
      (db as any)
        .select()
        .from(payments)
        .where(eq((payments as any).ticketId, ticketId))
    );

    if (!payment) {
      return { success: false, error: 'Payment not found' };
    }

    // Only send for manual payment methods
    if (!['bank_transfer', 'tpago'].includes(payment.provider)) {
      return { success: false, error: 'Payment instructions email only for bank_transfer or tpago' };
    }

    // Get merged payment config for this event
    const paymentConfig = await this.getPaymentConfig(event.id);

    const locale = ticket.preferredLanguage || 'en';
    const eventTitle = locale === 'es' && event.titleEs ? event.titleEs : event.title;
    const attendeeFullName = `${ticket.attendeeFirstName} ${ticket.attendeeLastName || ''}`.trim();

    // Calculate total price for multi-ticket bookings
    let totalPrice = event.price;
    let ticketCount = 1;
    
    if (ticket.bookingId) {
      // Count all tickets in this booking
      const bookingTickets = await dbAll<any>(
        (db as any)
          .select()
          .from(tickets)
          .where(eq((tickets as any).bookingId, ticket.bookingId))
      );
      ticketCount = bookingTickets.length;
      totalPrice = event.price * ticketCount;
    }

    // Generate a payment reference using booking ID or ticket ID
    const paymentReference = `SPG-${(ticket.bookingId || ticket.id).substring(0, 8).toUpperCase()}`;

    // Generate the booking URL for returning to payment page
    const frontendUrl = process.env.FRONTEND_URL || 'https://spanglish.com';
    const bookingUrl = `${frontendUrl}/booking/${ticket.id}?step=payment`;

    // Determine which template to use
    const templateSlug = payment.provider === 'tpago' 
      ? 'payment-instructions-tpago' 
      : 'payment-instructions-bank-transfer';

    // Format amount with ticket count info for multi-ticket bookings
    const amountDisplay = ticketCount > 1 
      ? `${this.formatCurrency(totalPrice, event.currency)} (${ticketCount} tickets)`
      : this.formatCurrency(totalPrice, event.currency);

    // Get site timezone for proper date/time formatting
    const timezone = await this.getSiteTimezone();

    // Build variables based on payment method
    const variables: Record<string, any> = {
      attendeeName: attendeeFullName,
      attendeeEmail: ticket.attendeeEmail,
      ticketId: ticket.bookingId || ticket.id,
      eventTitle,
      eventDate: this.formatDate(event.startDatetime, locale, timezone),
      eventTime: this.formatTime(event.startDatetime, locale, timezone),
      eventLocation: event.location,
      eventLocationUrl: event.locationUrl || '',
      paymentAmount: amountDisplay,
      paymentReference,
      bookingUrl,
    };

    // Add payment-method specific variables
    if (payment.provider === 'tpago') {
      variables.tpagoLink = paymentConfig.tpagoLink || '';
    } else {
      // Bank transfer
      variables.bankName = paymentConfig.bankName || '';
      variables.bankAccountHolder = paymentConfig.bankAccountHolder || '';
      variables.bankAccountNumber = paymentConfig.bankAccountNumber || '';
      variables.bankAlias = paymentConfig.bankAlias || '';
      variables.bankPhone = paymentConfig.bankPhone || '';
    }

    console.log(`[Email] Sending payment instructions email (${payment.provider}) to ${ticket.attendeeEmail}`);

    return this.sendTemplateEmail({
      templateSlug,
      to: ticket.attendeeEmail,
      toName: attendeeFullName,
      locale,
      eventId: event.id,
      variables,
    });
  },

  /**
   * Send payment rejection email
   * This email is sent when admin rejects a TPago or Bank Transfer payment
   */
  async sendPaymentRejectionEmail(paymentId: string): Promise<{ success: boolean; error?: string }> {
    // Get payment
    const payment = await dbGet<any>(
      (db as any)
        .select()
        .from(payments)
        .where(eq((payments as any).id, paymentId))
    );

    if (!payment) {
      return { success: false, error: 'Payment not found' };
    }

    // Get ticket
    const ticket = await dbGet<any>(
      (db as any)
        .select()
        .from(tickets)
        .where(eq((tickets as any).id, payment.ticketId))
    );

    if (!ticket) {
      return { success: false, error: 'Ticket not found' };
    }

    // Get event
    const event = await dbGet<any>(
      (db as any)
        .select()
        .from(events)
        .where(eq((events as any).id, ticket.eventId))
    );

    if (!event) {
      return { success: false, error: 'Event not found' };
    }

    const locale = ticket.preferredLanguage || 'en';
    const eventTitle = locale === 'es' && event.titleEs ? event.titleEs : event.title;
    const attendeeFullName = `${ticket.attendeeFirstName} ${ticket.attendeeLastName || ''}`.trim();

    // Generate a new booking URL for the event
    const frontendUrl = process.env.FRONTEND_URL || 'https://spanglish.com';
    const newBookingUrl = `${frontendUrl}/book/${event.id}`;

    // Get site timezone for proper date/time formatting
    const timezone = await this.getSiteTimezone();

    console.log(`[Email] Sending payment rejection email to ${ticket.attendeeEmail}`);

    return this.sendTemplateEmail({
      templateSlug: 'payment-rejected',
      to: ticket.attendeeEmail,
      toName: attendeeFullName,
      locale,
      eventId: event.id,
      variables: {
        attendeeName: attendeeFullName,
        attendeeEmail: ticket.attendeeEmail,
        ticketId: ticket.id,
        eventTitle,
        eventDate: this.formatDate(event.startDatetime, locale, timezone),
        eventTime: this.formatTime(event.startDatetime, locale, timezone),
        eventLocation: event.location,
        eventLocationUrl: event.locationUrl || '',
        newBookingUrl,
      },
    });
  },

  /**
   * Send payment reminder email
   * This email is sent when admin wants to remind attendee about pending payment
   */
  async sendPaymentReminder(paymentId: string): Promise<{ success: boolean; error?: string }> {
    // Get payment
    const payment = await dbGet<any>(
      (db as any)
        .select()
        .from(payments)
        .where(eq((payments as any).id, paymentId))
    );

    if (!payment) {
      return { success: false, error: 'Payment not found' };
    }

    // Only send for pending/pending_approval payments
    if (!['pending', 'pending_approval'].includes(payment.status)) {
      return { success: false, error: 'Payment reminder can only be sent for pending payments' };
    }

    // Get ticket
    const ticket = await dbGet<any>(
      (db as any)
        .select()
        .from(tickets)
        .where(eq((tickets as any).id, payment.ticketId))
    );

    if (!ticket) {
      return { success: false, error: 'Ticket not found' };
    }

    // Get event
    const event = await dbGet<any>(
      (db as any)
        .select()
        .from(events)
        .where(eq((events as any).id, ticket.eventId))
    );

    if (!event) {
      return { success: false, error: 'Event not found' };
    }

    const locale = ticket.preferredLanguage || 'en';
    const eventTitle = locale === 'es' && event.titleEs ? event.titleEs : event.title;
    const attendeeFullName = `${ticket.attendeeFirstName} ${ticket.attendeeLastName || ''}`.trim();

    // Calculate total price for multi-ticket bookings
    let totalPrice = event.price;
    let ticketCount = 1;
    
    if (ticket.bookingId) {
      const bookingTickets = await dbAll<any>(
        (db as any)
          .select()
          .from(tickets)
          .where(eq((tickets as any).bookingId, ticket.bookingId))
      );
      ticketCount = bookingTickets.length;
      totalPrice = event.price * ticketCount;
    }

    // Generate the booking URL for returning to payment page
    const frontendUrl = process.env.FRONTEND_URL || 'https://spanglish.com';
    const bookingUrl = `${frontendUrl}/booking/${ticket.id}?step=payment`;

    // Format amount with ticket count info for multi-ticket bookings
    const amountDisplay = ticketCount > 1 
      ? `${this.formatCurrency(totalPrice, event.currency)} (${ticketCount} tickets)`
      : this.formatCurrency(totalPrice, event.currency);

    // Get site timezone for proper date/time formatting
    const timezone = await this.getSiteTimezone();

    console.log(`[Email] Sending payment reminder email to ${ticket.attendeeEmail}`);

    return this.sendTemplateEmail({
      templateSlug: 'payment-reminder',
      to: ticket.attendeeEmail,
      toName: attendeeFullName,
      locale,
      eventId: event.id,
      variables: {
        attendeeName: attendeeFullName,
        attendeeEmail: ticket.attendeeEmail,
        ticketId: ticket.bookingId || ticket.id,
        eventTitle,
        eventDate: this.formatDate(event.startDatetime, locale, timezone),
        eventTime: this.formatTime(event.startDatetime, locale, timezone),
        eventLocation: event.location,
        eventLocationUrl: event.locationUrl || '',
        paymentAmount: amountDisplay,
        bookingUrl,
      },
    });
  },

  /**
   * Send custom email to event attendees
   */
  async sendToEventAttendees(params: {
    eventId: string;
    templateSlug: string;
    customVariables?: Record<string, any>;
    recipientFilter?: 'all' | 'confirmed' | 'pending' | 'checked_in';
    sentBy: string;
  }): Promise<{ success: boolean; sentCount: number; failedCount: number; errors: string[] }> {
    const { eventId, templateSlug, customVariables = {}, recipientFilter = 'confirmed', sentBy } = params;
    
    // Get event
    const event = await dbGet<any>(
      (db as any)
        .select()
        .from(events)
        .where(eq((events as any).id, eventId))
    );
    
    if (!event) {
      return { success: false, sentCount: 0, failedCount: 0, errors: ['Event not found'] };
    }
    
    // Get tickets based on filter
    let ticketQuery = (db as any)
      .select()
      .from(tickets)
      .where(eq((tickets as any).eventId, eventId));
    
    if (recipientFilter !== 'all') {
      ticketQuery = ticketQuery.where(
        and(
          eq((tickets as any).eventId, eventId),
          eq((tickets as any).status, recipientFilter)
        )
      );
    }
    
    const eventTickets = await dbAll<any>(ticketQuery);
    
    if (eventTickets.length === 0) {
      return { success: true, sentCount: 0, failedCount: 0, errors: ['No recipients found'] };
    }
    
    // Get site timezone for proper date/time formatting
    const timezone = await this.getSiteTimezone();
    
    let sentCount = 0;
    let failedCount = 0;
    const errors: string[] = [];
    
    // Send to each attendee
    for (const ticket of eventTickets) {
      const locale = ticket.preferredLanguage || 'en';
      const eventTitle = locale === 'es' && event.titleEs ? event.titleEs : event.title;
      
      const bulkFullName = `${ticket.attendeeFirstName} ${ticket.attendeeLastName || ''}`.trim();
      const result = await this.sendTemplateEmail({
        templateSlug,
        to: ticket.attendeeEmail,
        toName: bulkFullName,
        locale,
        eventId: event.id,
        sentBy,
        variables: {
          attendeeName: bulkFullName,
          attendeeEmail: ticket.attendeeEmail,
          ticketId: ticket.id,
          eventTitle,
          eventDate: this.formatDate(event.startDatetime, locale, timezone),
          eventTime: this.formatTime(event.startDatetime, locale, timezone),
          eventLocation: event.location,
          eventLocationUrl: event.locationUrl || '',
          ...customVariables,
        },
      });
      
      if (result.success) {
        sentCount++;
      } else {
        failedCount++;
        errors.push(`Failed to send to ${ticket.attendeeEmail}: ${result.error}`);
      }
    }
    
    return {
      success: failedCount === 0,
      sentCount,
      failedCount,
      errors,
    };
  },

  /**
   * Queue emails for event attendees (non-blocking).
   * Adds all matching recipients to the background email queue and returns immediately.
   * Rate limiting and actual sending is handled by the email queue.
   */
  async queueEventEmails(params: {
    eventId: string;
    templateSlug: string;
    customVariables?: Record<string, any>;
    recipientFilter?: 'all' | 'confirmed' | 'pending' | 'checked_in';
    sentBy: string;
  }): Promise<{ success: boolean; queuedCount: number; error?: string }> {
    const { eventId, templateSlug, customVariables = {}, recipientFilter = 'confirmed', sentBy } = params;

    // Validate event exists
    const event = await dbGet<any>(
      (db as any)
        .select()
        .from(events)
        .where(eq((events as any).id, eventId))
    );

    if (!event) {
      return { success: false, queuedCount: 0, error: 'Event not found' };
    }

    // Validate template exists
    const template = await this.getTemplate(templateSlug);
    if (!template) {
      return { success: false, queuedCount: 0, error: `Template "${templateSlug}" not found` };
    }

    // Get tickets based on filter
    let ticketQuery = (db as any)
      .select()
      .from(tickets)
      .where(eq((tickets as any).eventId, eventId));

    if (recipientFilter !== 'all') {
      ticketQuery = ticketQuery.where(
        and(
          eq((tickets as any).eventId, eventId),
          eq((tickets as any).status, recipientFilter)
        )
      );
    }

    const eventTickets = await dbAll<any>(ticketQuery);

    if (eventTickets.length === 0) {
      return { success: true, queuedCount: 0, error: 'No recipients found' };
    }

    // Get site timezone for proper date/time formatting
    const timezone = await this.getSiteTimezone();

    // Build individual email jobs for the queue
    const jobs: TemplateEmailJobParams[] = eventTickets.map((ticket: any) => {
      const locale = ticket.preferredLanguage || 'en';
      const eventTitle = locale === 'es' && event.titleEs ? event.titleEs : event.title;
      const fullName = `${ticket.attendeeFirstName} ${ticket.attendeeLastName || ''}`.trim();

      return {
        templateSlug,
        to: ticket.attendeeEmail,
        toName: fullName,
        locale,
        eventId: event.id,
        sentBy,
        variables: {
          attendeeName: fullName,
          attendeeEmail: ticket.attendeeEmail,
          ticketId: ticket.id,
          eventTitle,
          eventDate: this.formatDate(event.startDatetime, locale, timezone),
          eventTime: this.formatTime(event.startDatetime, locale, timezone),
          eventLocation: event.location,
          eventLocationUrl: event.locationUrl || '',
          ...customVariables,
        },
      };
    });

    // Enqueue all emails for background processing
    enqueueBulkEmails(jobs);

    console.log(`[Email] Queued ${jobs.length} emails for event "${event.title}" (filter: ${recipientFilter})`);

    return {
      success: true,
      queuedCount: jobs.length,
    };
  },
  
  /**
   * Send a custom email (not from template)
   */
  async sendCustomEmail(params: {
    to: string;
    toName?: string;
    subject: string;
    bodyHtml: string;
    bodyText?: string;
    replyTo?: string;
    eventId?: string;
    sentBy?: string | null;
  }): Promise<{ success: boolean; logId?: string; error?: string }> {
    const { to, toName, subject, bodyHtml, bodyText, replyTo, eventId, sentBy = null } = params;
    
    const allVariables = {
      ...this.getCommonVariables(),
      subject,
    };
    
    const finalBodyHtml = wrapInBaseTemplate(bodyHtml, allVariables);
    
    // Create log entry
    const logId = generateId();
    const now = getNow();
    
    await (db as any).insert(emailLogs).values({
      id: logId,
      templateId: null,
      eventId: eventId || null,
      recipientEmail: to,
      recipientName: toName || null,
      subject,
      bodyHtml: finalBodyHtml,
      status: 'pending',
      sentBy: sentBy || null,
      createdAt: now,
    });
    
    // Send email
    const result = await sendEmail({
      to,
      subject,
      html: finalBodyHtml,
      text: bodyText,
      replyTo,
    });
    
    // Update log
    if (result.success) {
      await (db as any)
        .update(emailLogs)
        .set({ 
          status: 'sent',
          sentAt: getNow(),
        })
        .where(eq((emailLogs as any).id, logId));
    } else {
      await (db as any)
        .update(emailLogs)
        .set({ 
          status: 'failed',
          errorMessage: result.error,
        })
        .where(eq((emailLogs as any).id, logId));
    }
    
    return { 
      success: result.success, 
      logId,
      error: result.error 
    };
  },

  /**
   * Resend an email from an existing log entry
   */
  async resendFromLog(logId: string): Promise<{ success: boolean; error?: string }> {
    const log = await dbGet<any>(
      (db as any).select().from(emailLogs).where(eq((emailLogs as any).id, logId))
    );

    if (!log) {
      return { success: false, error: 'Email log not found' };
    }

    if (!log.bodyHtml || !log.subject || !log.recipientEmail) {
      return { success: false, error: 'Email log missing required data to resend' };
    }

    const result = await sendEmail({
      to: log.recipientEmail,
      subject: log.subject,
      html: log.bodyHtml,
      text: undefined,
    });

    const now = getNow();
    const currentResendAttempts = (log.resendAttempts ?? 0) + 1;

    if (result.success) {
      await (db as any)
        .update(emailLogs)
        .set({
          status: 'sent',
          sentAt: now,
          errorMessage: null,
          resendAttempts: currentResendAttempts,
          lastResentAt: now,
        })
        .where(eq((emailLogs as any).id, logId));
    } else {
      await (db as any)
        .update(emailLogs)
        .set({
          status: 'failed',
          errorMessage: result.error,
          resendAttempts: currentResendAttempts,
          lastResentAt: now,
        })
        .where(eq((emailLogs as any).id, logId));
    }

    return {
      success: result.success,
      error: result.error,
    };
  },
};

// Export the main sendEmail function for direct use
export { sendEmail };

export default emailService;
