import * as plugins from '../../plugins.js';
import { logger } from '../../logger.js';
import { LRUCache } from 'lru-cache';

export interface IEmailValidationResult {
  isValid: boolean;
  hasMx: boolean;
  hasSpamMarkings: boolean;
  score: number;
  details?: {
    formatValid?: boolean;
    mxRecords?: string[];
    disposable?: boolean;
    role?: boolean;
    spamIndicators?: string[];
    errorMessage?: string;
  };
}

/**
 * Advanced email validator class using smartmail's capabilities
 */
export class EmailValidator {
  private validator: plugins.smartmail.EmailAddressValidator;
  private dnsCache: LRUCache<string, string[]>;
  
  constructor(options?: {
    maxCacheSize?: number;
    cacheTTL?: number;
  }) {
    this.validator = new plugins.smartmail.EmailAddressValidator();
    
    // Initialize LRU cache for DNS records
    this.dnsCache = new LRUCache<string, string[]>({
      // Default to 1000 entries (reasonable for most applications)
      max: options?.maxCacheSize || 1000,
      // Default TTL of 1 hour (DNS records don't change frequently)
      ttl: options?.cacheTTL || 60 * 60 * 1000,
      // Optional cache monitoring
      allowStale: false,
      updateAgeOnGet: true,
      // Add logging for cache events in production environments
      disposeAfter: (value, key) => {
        logger.log('debug', `DNS cache entry expired for domain: ${key}`);
      },
    });
  }
  
  /**
   * Validates an email address using comprehensive checks
   * @param email The email to validate
   * @param options Validation options
   * @returns Validation result with details
   */
  public async validate(
    email: string,
    options: {
      checkMx?: boolean;
      checkDisposable?: boolean;
      checkRole?: boolean;
      checkSyntaxOnly?: boolean;
    } = {}
  ): Promise<IEmailValidationResult> {
    try {
      const result: IEmailValidationResult = {
        isValid: false,
        hasMx: false,
        hasSpamMarkings: false,
        score: 0,
        details: {
          formatValid: false,
          spamIndicators: []
        }
      };
      
      // Always check basic format
      result.details.formatValid = this.validator.isValidEmailFormat(email);
      if (!result.details.formatValid) {
        result.details.errorMessage = 'Invalid email format';
        return result;
      }
      
      // If syntax-only check is requested, return early
      if (options.checkSyntaxOnly) {
        result.isValid = true;
        result.score = 0.5;
        return result;
      }
      
      // Get domain for additional checks
      const domain = email.split('@')[1];
      
      // Check MX records
      if (options.checkMx !== false) {
        try {
          const mxRecords = await this.getMxRecords(domain);
          result.details.mxRecords = mxRecords;
          result.hasMx = mxRecords && mxRecords.length > 0;
          
          if (!result.hasMx) {
            result.details.spamIndicators.push('No MX records');
            result.details.errorMessage = 'Domain has no MX records';
          }
        } catch (error) {
          logger.log('error', `Error checking MX records: ${error.message}`);
          result.details.errorMessage = 'Unable to check MX records';
        }
      }
      
      // Check if domain is disposable
      if (options.checkDisposable !== false) {
        result.details.disposable = await this.validator.isDisposableEmail(email);
        if (result.details.disposable) {
          result.details.spamIndicators.push('Disposable email');
        }
      }
      
      // Check if email is a role account
      if (options.checkRole !== false) {
        result.details.role = this.validator.isRoleAccount(email);
        if (result.details.role) {
          result.details.spamIndicators.push('Role account');
        }
      }
      
      // Calculate spam score and final validity
      result.hasSpamMarkings = result.details.spamIndicators.length > 0;
      
      // Calculate a score between 0-1 based on checks
      let scoreFactors = 0;
      let scoreTotal = 0;
      
      // Format check (highest weight)
      scoreFactors += 0.4;
      if (result.details.formatValid) scoreTotal += 0.4;
      
      // MX check (high weight)
      if (options.checkMx !== false) {
        scoreFactors += 0.3;
        if (result.hasMx) scoreTotal += 0.3;
      }
      
      // Disposable check (medium weight)
      if (options.checkDisposable !== false) {
        scoreFactors += 0.2;
        if (!result.details.disposable) scoreTotal += 0.2;
      }
      
      // Role account check (low weight)
      if (options.checkRole !== false) {
        scoreFactors += 0.1;
        if (!result.details.role) scoreTotal += 0.1;
      }
      
      // Normalize score based on factors actually checked
      result.score = scoreFactors > 0 ? scoreTotal / scoreFactors : 0;
      
      // Email is valid if score is above 0.7 (configurable threshold)
      result.isValid = result.score >= 0.7;
      
      return result;
    } catch (error) {
      logger.log('error', `Email validation error: ${error.message}`);
      return {
        isValid: false,
        hasMx: false,
        hasSpamMarkings: true,
        score: 0,
        details: {
          formatValid: false,
          errorMessage: `Validation error: ${error.message}`,
          spamIndicators: ['Validation error']
        }
      };
    }
  }
  
  /**
   * Gets MX records for a domain with caching
   * @param domain Domain to check
   * @returns Array of MX records
   */
  private async getMxRecords(domain: string): Promise<string[]> {
    // Check cache first
    const cachedRecords = this.dnsCache.get(domain);
    if (cachedRecords) {
      logger.log('debug', `Using cached MX records for domain: ${domain}`);
      return cachedRecords;
    }
    
    try {
      // Use smartmail's getMxRecords method
      const records = await this.validator.getMxRecords(domain);
      
      // Store in cache (TTL is handled by the LRU cache configuration)
      this.dnsCache.set(domain, records);
      logger.log('debug', `Cached MX records for domain: ${domain}`);
      
      return records;
    } catch (error) {
      logger.log('error', `Error fetching MX records for ${domain}: ${error.message}`);
      return [];
    }
  }
  
  /**
   * Validates multiple email addresses in batch
   * @param emails Array of emails to validate
   * @param options Validation options
   * @returns Object with email addresses as keys and validation results as values
   */
  public async validateBatch(
    emails: string[],
    options: {
      checkMx?: boolean;
      checkDisposable?: boolean;
      checkRole?: boolean;
      checkSyntaxOnly?: boolean;
    } = {}
  ): Promise<Record<string, IEmailValidationResult>> {
    const results: Record<string, IEmailValidationResult> = {};
    
    for (const email of emails) {
      results[email] = await this.validate(email, options);
    }
    
    return results;
  }
  
  /**
   * Quick check if an email format is valid (synchronous, no DNS checks)
   * @param email Email to check
   * @returns Boolean indicating if format is valid
   */
  public isValidFormat(email: string): boolean {
    return this.validator.isValidEmailFormat(email);
  }
  
}