/**
 * SMTP Client Error Handler
 * Error classification and recovery strategies
 */

import { SmtpErrorType } from './constants.js';
import type { ISmtpResponse, ISmtpErrorContext, ISmtpClientOptions } from './interfaces.js';
import { logDebug } from './utils/logging.js';

export class SmtpErrorHandler {
  private options: ISmtpClientOptions;
  
  constructor(options: ISmtpClientOptions) {
    this.options = options;
  }
  
  /**
   * Classify error type based on response or error
   */
  public classifyError(error: Error | ISmtpResponse, context?: ISmtpErrorContext): SmtpErrorType {
    logDebug('Classifying error', this.options, { errorMessage: error instanceof Error ? error.message : String(error), context });
    
    // Handle Error objects
    if (error instanceof Error) {
      return this.classifyErrorByMessage(error);
    }
    
    // Handle SMTP response codes
    if (typeof error === 'object' && 'code' in error) {
      return this.classifyErrorByCode(error.code);
    }
    
    return SmtpErrorType.UNKNOWN_ERROR;
  }
  
  /**
   * Determine if error is retryable
   */
  public isRetryable(errorType: SmtpErrorType, response?: ISmtpResponse): boolean {
    switch (errorType) {
      case SmtpErrorType.CONNECTION_ERROR:
      case SmtpErrorType.TIMEOUT_ERROR:
        return true;
      
      case SmtpErrorType.PROTOCOL_ERROR:
        // Only retry on temporary failures (4xx codes)
        return response ? response.code >= 400 && response.code < 500 : false;
      
      case SmtpErrorType.AUTHENTICATION_ERROR:
      case SmtpErrorType.TLS_ERROR:
      case SmtpErrorType.SYNTAX_ERROR:
      case SmtpErrorType.MAILBOX_ERROR:
      case SmtpErrorType.QUOTA_ERROR:
        return false;
      
      default:
        return false;
    }
  }
  
  /**
   * Get retry delay for error type
   */
  public getRetryDelay(attempt: number, errorType: SmtpErrorType): number {
    const baseDelay = 1000; // 1 second
    const maxDelay = 30000;  // 30 seconds
    
    // Exponential backoff with jitter
    const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
    const jitter = Math.random() * 0.1 * delay; // 10% jitter
    
    return Math.floor(delay + jitter);
  }
  
  /**
   * Create enhanced error with context
   */
  public createError(
    message: string,
    errorType: SmtpErrorType,
    context?: ISmtpErrorContext,
    originalError?: Error
  ): Error {
    const error = new Error(message);
    (error as any).type = errorType;
    (error as any).context = context;
    (error as any).originalError = originalError;
    
    return error;
  }
  
  private classifyErrorByMessage(error: Error): SmtpErrorType {
    const message = error.message.toLowerCase();
    
    if (message.includes('timeout') || message.includes('etimedout')) {
      return SmtpErrorType.TIMEOUT_ERROR;
    }
    
    if (message.includes('connect') || message.includes('econnrefused') || 
        message.includes('enotfound') || message.includes('enetunreach')) {
      return SmtpErrorType.CONNECTION_ERROR;
    }
    
    if (message.includes('tls') || message.includes('ssl') || 
        message.includes('certificate') || message.includes('handshake')) {
      return SmtpErrorType.TLS_ERROR;
    }
    
    if (message.includes('auth')) {
      return SmtpErrorType.AUTHENTICATION_ERROR;
    }
    
    return SmtpErrorType.UNKNOWN_ERROR;
  }
  
  private classifyErrorByCode(code: number): SmtpErrorType {
    if (code >= 500) {
      // Permanent failures
      if (code === 550 || code === 551 || code === 553) {
        return SmtpErrorType.MAILBOX_ERROR;
      }
      if (code === 552) {
        return SmtpErrorType.QUOTA_ERROR;
      }
      if (code === 500 || code === 501 || code === 502 || code === 504) {
        return SmtpErrorType.SYNTAX_ERROR;
      }
      return SmtpErrorType.PROTOCOL_ERROR;
    }
    
    if (code >= 400) {
      // Temporary failures
      if (code === 450 || code === 451 || code === 452) {
        return SmtpErrorType.QUOTA_ERROR;
      }
      return SmtpErrorType.PROTOCOL_ERROR;
    }
    
    return SmtpErrorType.UNKNOWN_ERROR;
  }
}