/**
 * Email Service
 * 
 * Core email sending service with nodemailer
 * Supports both test accounts (preview) and production SMTP
 */

import nodemailer from 'nodemailer';
import logger from '@utils/logger';

interface EmailOptions {
  to: string | string[];
  subject: string;
  html: string;
  cc?: string | string[];
  bcc?: string | string[];
  attachments?: any[];
}



export class EmailService {
  private transporter: nodemailer.Transporter | null = null;
  private useTestAccount: boolean = false;
  private testAccountInfo: any = null;

  /**
   * Initialize email service
   * If SMTP credentials are not configured, uses test account for preview
   */
  async initialize(): Promise<void> {
    const smtpHost = process.env.SMTP_HOST;
    const smtpUser = process.env.SMTP_USER;
    const smtpPassword = process.env.SMTP_PASSWORD;

    // Check if SMTP is configured
    if (!smtpHost || !smtpUser || !smtpPassword) {
      logger.warn('⚠️  SMTP not configured - using test account for preview');
      await this.initializeTestAccount();
      return;
    }

    // Prod SMTP configuration
    try {
      this.transporter = nodemailer.createTransport({
        host: smtpHost,
        port: parseInt(process.env.SMTP_PORT || '587'),
        secure: process.env.SMTP_SECURE === 'true',
        auth: {
          user: smtpUser,
          pass: smtpPassword
        },
        pool: true, // Use connection pooling
        maxConnections: 5,
        maxMessages: 100,
        rateDelta: 1000,
        rateLimit: 5
      });

      // Verify connection
      await this.transporter.verify();
      logger.info('✅ Email service initialized with production SMTP');
      this.useTestAccount = false;
    } catch (error) {
      logger.error('❌ Failed to initialize production SMTP:', error);
      logger.warn('⚠️  Falling back to test account');
      await this.initializeTestAccount();
    }
  }

  /**
   * Initialize test account for preview (no real SMTP needed)
   */
  private async initializeTestAccount(): Promise<void> {
    try {
      this.testAccountInfo = await nodemailer.createTestAccount();

      this.transporter = nodemailer.createTransport({
        host: this.testAccountInfo.smtp.host,
        port: this.testAccountInfo.smtp.port,
        secure: this.testAccountInfo.smtp.secure,
        auth: {
          user: this.testAccountInfo.user,
          pass: this.testAccountInfo.pass
        }
      });

      this.useTestAccount = true;
      logger.info('✅ Email service initialized with test account (preview mode)');
      logger.info(`📧 Test account: ${this.testAccountInfo.user}`);
    } catch (error) {
      logger.error('❌ Failed to initialize test account:', error);
      throw new Error('Email service initialization failed');
    }
  }

  /**
   * Send email with retry logic
   */
  async sendEmail(options: EmailOptions): Promise<{ messageId: string; previewUrl?: string }> {
    if (!this.transporter) {
      await this.initialize();
    }

    // If using test account, check if SMTP credentials are now available and re-initialize
    if (this.useTestAccount) {
      const smtpHost = process.env.SMTP_HOST;
      const smtpUser = process.env.SMTP_USER;
      const smtpPassword = process.env.SMTP_PASSWORD;

      if (smtpHost && smtpUser && smtpPassword) {
        logger.info('📧 SMTP credentials detected - re-initializing email service with production SMTP');
        await this.initialize();
      }
    }

    const recipients = Array.isArray(options.to) ? options.to.join(', ') : options.to;
    const fromAddress = process.env.EMAIL_FROM || `RE Flow <noreply@${process.env.APP_DOMAIN || 'royalenfield.com'}>`;

    const mailOptions = {
      from: fromAddress,
      to: recipients,
      cc: options.cc,
      bcc: options.bcc || [],
      subject: options.subject,
      html: options.html,
      attachments: options.attachments
    };

    // Retry logic
    const maxRetries = parseInt(process.env.EMAIL_RETRY_ATTEMPTS || '3');
    let lastError: any;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        const info = await this.transporter!.sendMail(mailOptions);

        if (!info || !info.messageId) {
          throw new Error('Email sent but no messageId returned');
        }

        const result: { messageId: string; previewUrl?: string } = {
          messageId: info.messageId
        };

        // If using test account, generate preview URL
        if (this.useTestAccount) {
          try {
            const previewUrl = nodemailer.getTestMessageUrl(info);

            if (previewUrl) {
              result.previewUrl = previewUrl;

              logger.info(`✅ Email sent (TEST MODE) to ${recipients}`);
              logger.info(`📧 Preview URL: ${previewUrl}`);
            } else {
              logger.warn(`⚠️  Email sent but preview URL not available. Message ID: ${info.messageId}`);
              logger.warn(`💡 This can happen if the email service is rate-limited or the message hasn't been processed yet.`);
            }
          } catch (previewError: any) {
            logger.error(`❌ Failed to generate preview URL:`, previewError);
            logger.warn(`⚠️  Email was sent successfully (Message ID: ${info.messageId}) but preview URL generation failed.`);
            logger.warn(`💡 You can try sending the email again to get a new preview URL.`);
            // Don't throw - email was sent successfully, just preview URL failed
          }
        } else {
          logger.info(`✅ Email sent to ${recipients}: ${options.subject}`);
        }

        return result;

      } catch (error) {
        lastError = error;
        logger.error(`❌ Email send attempt ${attempt}/${maxRetries} failed:`, error);

        if (attempt < maxRetries) {
          const delay = parseInt(process.env.EMAIL_RETRY_DELAY || '5000') * attempt;
          logger.info(`⏳ Retrying in ${delay}ms...`);
          await new Promise(resolve => setTimeout(resolve, delay));
        }
      }
    }

    // All retries failed
    logger.error(`❌ Failed to send email after ${maxRetries} attempts:`, lastError);
    throw new Error(`Email delivery failed: ${lastError?.message || 'Unknown error'}`);
  }

  /**
   * Send email to multiple recipients (batch)
   */
  async sendBatch(emails: EmailOptions[]): Promise<void> {
    logger.info(`📧 Sending batch of ${emails.length} emails`);

    const batchSize = parseInt(process.env.EMAIL_BATCH_SIZE || '10');

    for (let i = 0; i < emails.length; i += batchSize) {
      const batch = emails.slice(i, i + batchSize);

      await Promise.allSettled(
        batch.map(email => this.sendEmail(email))
      );

      // Small delay between batches to avoid rate limiting
      if (i + batchSize < emails.length) {
        await new Promise(resolve => setTimeout(resolve, 1000));
      }
    }

    logger.info(`✅ Batch email sending complete`);
  }

  /**
   * Check if email service is in test mode
   */
  isTestMode(): boolean {
    return this.useTestAccount;
  }

  /**
   * Get test account info (for preview URLs)
   */
  getTestAccountInfo(): any {
    return this.testAccountInfo;
  }

  /**
   * Close transporter (cleanup)
   */
  async close(): Promise<void> {
    if (this.transporter) {
      this.transporter.close();
      logger.info('📧 Email service closed');
    }
  }
}

// Singleton instance
export const emailService = new EmailService();

// Note: initialize() is now called explicitly in server.ts
// to ensure it happens after secrets are loaded.

