import nodemailer, { Transporter } from 'nodemailer';
import sgMail from '@sendgrid/mail';
import { SESClient } from '@aws-sdk/client-ses';
import { fromEnv } from '@aws-sdk/credential-providers';
import {
  EmailProviderType,
  EmailProviderConfig,
  SmtpConfig,
  SendGridConfig,
  SesConfig,
} from '../interfaces/email-options.interface';
import { ConfigurationError } from '../errors';

/**
 * Factory for creating email provider configurations
 */
export class ProviderFactory {
  /**
   * Create SMTP transporter
   */
  static createSmtpTransporter(config: SmtpConfig): Transporter {
    return nodemailer.createTransport(config);
  }

  /**
   * Configure SendGrid
   */
  static configureSendGrid(config: SendGridConfig): void {
    sgMail.setApiKey(config.apiKey);
  }

  /**
   * Create AWS SES client
   */
  static createSesClient(config: SesConfig): SESClient {
    const clientConfig: any = {
      region: config.region,
      apiVersion: config.apiVersion || '2010-12-01',
      maxAttempts: config.maxRetries || 3,
    };

    // Configure AWS credentials
    if (config.credentials) {
      clientConfig.credentials = config.credentials;
    } else if (config.accessKeyId && config.secretAccessKey) {
      clientConfig.credentials = {
        accessKeyId: config.accessKeyId,
        secretAccessKey: config.secretAccessKey,
        sessionToken: config.sessionToken,
      };
    } else {
      // Use AWS credential providers chain (environment variables, IAM roles, etc.)
      clientConfig.credentials = fromEnv();
    }

    // Configure AWS SES endpoint if provided
    if (config.endpoint) {
      clientConfig.endpoint = config.endpoint;
    }

    // Configure AWS SES HTTP options
    if (config.httpOptions) {
      clientConfig.requestHandler = {
        httpOptions: config.httpOptions,
      };
    }

    return new SESClient(clientConfig);
  }

  /**
   * Create provider configuration from legacy SMTP config
   */
  static createProviderConfig(config: SmtpConfig | EmailProviderConfig): EmailProviderConfig {
    if (!('type' in config)) {
      return {
        type: EmailProviderType.SMTP,
        config: config as SmtpConfig,
      };
    }
    return config as EmailProviderConfig;
  }

  /**
   * Validate provider configuration
   */
  static validateProviderConfig(config: EmailProviderConfig): void {
    if (!config.type) {
      throw new ConfigurationError('Provider type is required');
    }

    if (!config.config) {
      throw new ConfigurationError('Provider configuration is required');
    }

    switch (config.type) {
      case EmailProviderType.SMTP:
        const smtpConfig = config.config as SmtpConfig;
        if (!smtpConfig.host || !smtpConfig.port) {
          throw new ConfigurationError('SMTP host and port are required');
        }
        break;
        
      case EmailProviderType.SENDGRID:
        const sendGridConfig = config.config as SendGridConfig;
        if (!sendGridConfig.apiKey) {
          throw new ConfigurationError('SendGrid API key is required');
        }
        
        // Check for basic API key format (SendGrid keys typically start with "SG.")
        if (typeof sendGridConfig.apiKey === 'string' && !sendGridConfig.apiKey.startsWith('SG.')) {
          throw new ConfigurationError('SendGrid API key appears to be invalid. Valid SendGrid API keys typically start with "SG."');
        }
        
        // Check for minimum length (SendGrid keys are typically 69 characters)
        if (typeof sendGridConfig.apiKey === 'string' && sendGridConfig.apiKey.length < 20) {
          throw new ConfigurationError('SendGrid API key appears to be too short. Please verify your API key.');
        }
        break;
        
      case EmailProviderType.SES:
        // AWS SES Configuration Validation
        const sesConfig = config.config as SesConfig;
        if (!sesConfig.region) {
          throw new ConfigurationError('AWS SES region is required');
        }
        
        // Validate that at least one credential method is provided
        const hasCredentials = sesConfig.credentials || 
                             (sesConfig.accessKeyId && sesConfig.secretAccessKey) ||
                             process.env.AWS_ACCESS_KEY_ID;
        
        if (!hasCredentials) {
          throw new ConfigurationError('AWS SES credentials are required (accessKeyId/secretAccessKey, credentials object, or AWS environment variables)');
        }
        break;
        
      default:
        throw new ConfigurationError(`Unsupported provider type: ${config.type}`);
    }
  }
} 