import { 
  PlatformError, 
  OperationError,
  ResourceError
} from './base.errors.js';
import type { IErrorContext } from './base.errors.js';

import {
  REPUTATION_CHECK_ERROR,
  REPUTATION_DATA_ERROR,
  REPUTATION_BLOCKLIST_ERROR,
  REPUTATION_UPDATE_ERROR,
  WARMUP_ALLOCATION_ERROR,
  WARMUP_LIMIT_EXCEEDED,
  WARMUP_SCHEDULE_ERROR
} from './error.codes.js';

/**
 * Base class for reputation-related errors
 */
export class ReputationError extends OperationError {
  /**
   * Creates a new reputation error
   * 
   * @param message Error message
   * @param code Error code
   * @param context Additional context
   */
  constructor(
    message: string,
    code: string,
    context: IErrorContext = {}
  ) {
    super(message, code, context);
  }
}

/**
 * Error class for reputation check errors
 */
export class ReputationCheckError extends ReputationError {
  /**
   * Creates a new reputation check error
   * 
   * @param message Error message
   * @param context Additional context
   */
  constructor(
    message: string,
    context: IErrorContext = {}
  ) {
    super(message, REPUTATION_CHECK_ERROR, context);
  }
  
  /**
   * Creates a new instance with updated context
   */
  protected createWithContext(context: IErrorContext): PlatformError {
    return new (this.constructor as typeof ReputationCheckError)(
      this.message,
      context
    );
  }

  /**
   * Creates an instance for an IP reputation check error
   * 
   * @param ip IP address
   * @param provider Reputation provider
   * @param originalError Original error
   * @param context Additional context
   */
  public static ipCheckFailed(
    ip: string,
    provider: string,
    originalError?: Error,
    context: IErrorContext = {}
  ): ReputationCheckError {
    const errorMsg = originalError ? `: ${originalError.message}` : '';
    return new ReputationCheckError(
      `Failed to check reputation for IP ${ip} with provider ${provider}${errorMsg}`,
      {
        ...context,
        data: {
          ...context.data,
          ip,
          provider,
          originalError: originalError ? {
            message: originalError.message,
            stack: originalError.stack
          } : undefined
        }
      }
    );
  }

  /**
   * Creates an instance for a domain reputation check error
   * 
   * @param domain Domain
   * @param provider Reputation provider
   * @param originalError Original error
   * @param context Additional context
   */
  public static domainCheckFailed(
    domain: string,
    provider: string,
    originalError?: Error,
    context: IErrorContext = {}
  ): ReputationCheckError {
    const errorMsg = originalError ? `: ${originalError.message}` : '';
    return new ReputationCheckError(
      `Failed to check reputation for domain ${domain} with provider ${provider}${errorMsg}`,
      {
        ...context,
        data: {
          ...context.data,
          domain,
          provider,
          originalError: originalError ? {
            message: originalError.message,
            stack: originalError.stack
          } : undefined
        }
      }
    );
  }
}

/**
 * Error class for reputation data errors
 */
export class ReputationDataError extends ReputationError {
  /**
   * Creates a new reputation data error
   * 
   * @param message Error message
   * @param context Additional context
   */
  constructor(
    message: string,
    context: IErrorContext = {}
  ) {
    super(message, REPUTATION_DATA_ERROR, context);
  }
  
  /**
   * Creates a new instance with updated context
   */
  protected createWithContext(context: IErrorContext): PlatformError {
    return new (this.constructor as typeof ReputationDataError)(
      this.message,
      context
    );
  }

  /**
   * Creates an instance for a data access error
   * 
   * @param entity Entity type (domain, ip)
   * @param entityId Entity identifier
   * @param operation Operation that failed (read, write, update)
   * @param originalError Original error
   * @param context Additional context
   */
  public static dataAccessFailed(
    entity: string,
    entityId: string,
    operation: string,
    originalError?: Error,
    context: IErrorContext = {}
  ): ReputationDataError {
    const errorMsg = originalError ? `: ${originalError.message}` : '';
    return new ReputationDataError(
      `Failed to ${operation} reputation data for ${entity} ${entityId}${errorMsg}`,
      {
        ...context,
        data: {
          ...context.data,
          entity,
          entityId,
          operation,
          originalError: originalError ? {
            message: originalError.message,
            stack: originalError.stack
          } : undefined
        }
      }
    );
  }
}

/**
 * Error class for blocklist-related errors
 */
export class BlocklistError extends ReputationError {
  /**
   * Creates a new blocklist error
   * 
   * @param message Error message
   * @param context Additional context
   */
  constructor(
    message: string,
    context: IErrorContext = {}
  ) {
    super(message, REPUTATION_BLOCKLIST_ERROR, context);
  }
  
  /**
   * Creates a new instance with updated context
   */
  protected createWithContext(context: IErrorContext): PlatformError {
    return new (this.constructor as typeof BlocklistError)(
      this.message,
      context
    );
  }

  /**
   * Creates an instance for an entity found on a blocklist
   * 
   * @param entity Entity type (domain, ip)
   * @param entityId Entity identifier
   * @param blocklist Blocklist name
   * @param reason Reason for listing (if available)
   * @param context Additional context
   */
  public static entityBlocked(
    entity: string,
    entityId: string,
    blocklist: string,
    reason?: string,
    context: IErrorContext = {}
  ): BlocklistError {
    const reasonText = reason ? ` (${reason})` : '';
    return new BlocklistError(
      `${entity.charAt(0).toUpperCase() + entity.slice(1)} ${entityId} is listed on blocklist ${blocklist}${reasonText}`,
      {
        ...context,
        data: {
          ...context.data,
          entity,
          entityId,
          blocklist,
          reason
        },
        userMessage: `The ${entity} ${entityId} is on a blocklist. This may affect email deliverability.`
      }
    );
  }
}

/**
 * Error class for reputation update errors
 */
export class ReputationUpdateError extends ReputationError {
  /**
   * Creates a new reputation update error
   * 
   * @param message Error message
   * @param context Additional context
   */
  constructor(
    message: string,
    context: IErrorContext = {}
  ) {
    super(message, REPUTATION_UPDATE_ERROR, context);
  }
  
  /**
   * Creates a new instance with updated context
   */
  protected createWithContext(context: IErrorContext): PlatformError {
    return new (this.constructor as typeof ReputationUpdateError)(
      this.message,
      context
    );
  }
}

/**
 * Error class for IP warmup allocation errors
 */
export class WarmupAllocationError extends ReputationError {
  /**
   * Creates a new warmup allocation error
   * 
   * @param message Error message
   * @param context Additional context
   */
  constructor(
    message: string,
    context: IErrorContext = {}
  ) {
    super(message, WARMUP_ALLOCATION_ERROR, context);
  }
  
  /**
   * Creates a new instance with updated context
   */
  protected createWithContext(context: IErrorContext): PlatformError {
    return new (this.constructor as typeof WarmupAllocationError)(
      this.message,
      context
    );
  }

  /**
   * Creates an instance for no available IPs
   * 
   * @param domain Domain requesting an IP
   * @param policy Allocation policy that was used
   * @param context Additional context
   */
  public static noAvailableIps(
    domain: string,
    policy: string,
    context: IErrorContext = {}
  ): WarmupAllocationError {
    return new WarmupAllocationError(
      `No available IPs for domain ${domain} using ${policy} allocation policy`,
      {
        ...context,
        data: {
          ...context.data,
          domain,
          policy
        },
        userMessage: `No available sending IPs for ${domain}.`
      }
    );
  }
}

/**
 * Error class for IP warmup limit exceeded errors
 */
export class WarmupLimitError extends ResourceError {
  /**
   * Creates a new warmup limit error
   * 
   * @param message Error message
   * @param context Additional context
   */
  constructor(
    message: string,
    context: IErrorContext = {}
  ) {
    super(message, WARMUP_LIMIT_EXCEEDED, context);
  }
  
  /**
   * Creates a new instance with updated context
   */
  protected createWithContext(context: IErrorContext): PlatformError {
    return new (this.constructor as typeof WarmupLimitError)(
      this.message,
      context
    );
  }

  /**
   * Creates an instance for daily sending limit exceeded
   * 
   * @param ip IP address
   * @param domain Domain
   * @param limit Daily limit
   * @param sent Number of emails sent
   * @param context Additional context
   */
  public static dailyLimitExceeded(
    ip: string,
    domain: string,
    limit: number,
    sent: number,
    context: IErrorContext = {}
  ): WarmupLimitError {
    return new WarmupLimitError(
      `Daily sending limit exceeded for IP ${ip} and domain ${domain}: ${sent}/${limit}`,
      {
        ...context,
        data: {
          ...context.data,
          ip,
          domain,
          limit,
          sent
        },
        userMessage: `Daily sending limit reached for ${domain}.`
      }
    );
  }
}

/**
 * Error class for IP warmup schedule errors
 */
export class WarmupScheduleError extends ReputationError {
  /**
   * Creates a new warmup schedule error
   * 
   * @param message Error message
   * @param context Additional context
   */
  constructor(
    message: string,
    context: IErrorContext = {}
  ) {
    super(message, WARMUP_SCHEDULE_ERROR, context);
  }
  
  /**
   * Creates a new instance with updated context
   */
  protected createWithContext(context: IErrorContext): PlatformError {
    return new (this.constructor as typeof WarmupScheduleError)(
      this.message,
      context
    );
  }
}