import type { IncomingMessage } from 'http'
import { SerializableError } from '../../../models/SerializableError.js'

/**
 * Base class for all HTTP engine errors
 */
export class HttpEngineError extends SerializableError {
  readonly category: string

  constructor(message: string, code?: number | string, category = 'general') {
    super(message, code)
    this.category = category
  }
}

/**
 * Connection-related errors
 */
export class ConnectionError extends HttpEngineError {
  readonly connectionType: 'direct' | 'proxy' | 'tunnel'

  constructor(message: string, code?: number | string, connectionType: 'direct' | 'proxy' | 'tunnel' = 'direct') {
    super(message, code, 'connection')
    this.connectionType = connectionType
  }
}

/**
 * Authentication-related errors
 */
export class AuthenticationError extends HttpEngineError {
  readonly authType: 'request' | 'proxy' | 'digest' | 'basic' | 'ntlm'

  constructor(
    message: string,
    code?: number | string,
    authType: 'request' | 'proxy' | 'digest' | 'basic' | 'ntlm' = 'request'
  ) {
    super(message, code, 'authentication')
    this.authType = authType
  }
}

/**
 * Message building errors
 */
export class MessageBuilderError extends HttpEngineError {
  readonly component: 'status' | 'headers' | 'body' | 'host'

  constructor(message: string, code?: number | string, component: 'status' | 'headers' | 'body' | 'host' = 'headers') {
    super(message, code, 'message')
    this.component = component
  }
}

/**
 * Response parsing errors
 */
export class ResponseParsingError extends HttpEngineError {
  readonly parsingStage: 'status' | 'headers' | 'body' | 'chunked'

  constructor(
    message: string,
    code?: number | string,
    parsingStage: 'status' | 'headers' | 'body' | 'chunked' = 'headers'
  ) {
    super(message, code, 'parsing')
    this.parsingStage = parsingStage
  }
}

/**
 * Timeout errors
 */
export class TimeoutError extends HttpEngineError {
  readonly timeoutType: 'connection' | 'request' | 'response'

  constructor(message: string, code?: number | string, timeoutType: 'connection' | 'request' | 'response' = 'request') {
    super(message, code, 'timeout')
    this.timeoutType = timeoutType
  }
}

/**
 * Proxy-related errors
 */
export class ProxyError extends HttpEngineError {
  readonly proxyStage: 'connection' | 'authentication' | 'tunneling' | 'configuration'
  override name = 'ProxyError'

  constructor(
    message: string,
    code?: number | string,
    proxyStage: 'connection' | 'authentication' | 'tunneling' | 'configuration' = 'connection'
  ) {
    super(message, code, 'proxy')
    this.proxyStage = proxyStage
  }
}

/**
 * Proxy authentication error
 */
export class ProxyAuthenticationError extends HttpEngineError {
  override name = 'ProxyAuthenticationError'

  constructor(
    message: string,
    code: number | string,
    readonly response: IncomingMessage,
    readonly head: Buffer
  ) {
    super(message, code, 'proxy')
  }
}

/**
 * Payload-related errors
 */
export class PayloadError extends HttpEngineError {
  readonly payloadType: 'serialization' | 'validation' | 'unsupported'

  constructor(
    message: string,
    code?: number | string,
    payloadType: 'serialization' | 'validation' | 'unsupported' = 'validation'
  ) {
    super(message, code, 'payload')
    this.payloadType = payloadType
  }
}

/**
 * URL parsing errors
 */
export class UrlParsingError extends HttpEngineError {
  readonly url: string

  constructor(message: string, url: string, code?: number | string) {
    super(message, code, 'url')
    this.url = url
  }
}

/**
 * Configuration errors
 */
export class ConfigurationError extends HttpEngineError {
  readonly configType: 'request' | 'proxy' | 'auth' | 'timeout'

  constructor(
    message: string,
    code?: number | string,
    configType: 'request' | 'proxy' | 'auth' | 'timeout' = 'request'
  ) {
    super(message, code, 'configuration')
    this.configType = configType
  }
}

/**
 * Error factory for creating specific error types
 */
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
export class HttpEngineErrorFactory {
  /**
   * Create a connection error
   */
  static connectionError(
    message: string,
    code?: number | string,
    connectionType?: 'direct' | 'proxy' | 'tunnel'
  ): ConnectionError {
    return new ConnectionError(message, code, connectionType)
  }

  /**
   * Create an authentication error
   */
  static authenticationError(
    message: string,
    code?: number | string,
    authType?: 'request' | 'proxy' | 'digest' | 'basic' | 'ntlm'
  ): AuthenticationError {
    return new AuthenticationError(message, code, authType)
  }

  /**
   * Create a message builder error
   */
  static messageBuilderError(
    message: string,
    code?: number | string,
    component?: 'status' | 'headers' | 'body' | 'host'
  ): MessageBuilderError {
    return new MessageBuilderError(message, code, component)
  }

  /**
   * Create a response parsing error
   */
  static responseParsingError(
    message: string,
    code?: number | string,
    parsingStage?: 'status' | 'headers' | 'body' | 'chunked'
  ): ResponseParsingError {
    return new ResponseParsingError(message, code, parsingStage)
  }

  /**
   * Create a timeout error
   */
  static timeoutError(
    message: string,
    code?: number | string,
    timeoutType?: 'connection' | 'request' | 'response'
  ): TimeoutError {
    return new TimeoutError(message, code, timeoutType)
  }

  /**
   * Create a proxy error
   */
  static proxyError(
    message: string,
    code?: number | string,
    proxyStage?: 'connection' | 'authentication' | 'tunneling' | 'configuration'
  ): ProxyError {
    return new ProxyError(message, code, proxyStage)
  }

  /**
   * Create a payload error
   */
  static payloadError(
    message: string,
    code?: number | string,
    payloadType?: 'serialization' | 'validation' | 'unsupported'
  ): PayloadError {
    return new PayloadError(message, code, payloadType)
  }

  /**
   * Create a URL parsing error
   */
  static urlParsingError(message: string, url: string, code?: number | string): UrlParsingError {
    return new UrlParsingError(message, url, code)
  }

  /**
   * Create a configuration error
   */
  static configurationError(
    message: string,
    code?: number | string,
    configType?: 'request' | 'proxy' | 'auth' | 'timeout'
  ): ConfigurationError {
    return new ConfigurationError(message, code, configType)
  }

  /**
   * Convert a generic error to an HTTP engine error
   */
  static fromError(error: Error, category = 'general'): HttpEngineError {
    if (error instanceof HttpEngineError) {
      return error
    }
    return new HttpEngineError(error.message, undefined, category)
  }

  /**
   * Create a connection timeout error
   */
  static connectionTimeout(timeoutMs: number): TimeoutError {
    return new TimeoutError(`Connection timeout after ${timeoutMs}ms`, 408, 'connection')
  }

  /**
   * Create a request timeout error
   */
  static requestTimeout(timeoutMs: number): TimeoutError {
    return new TimeoutError(`Request timeout after ${timeoutMs}ms`, 408, 'request')
  }

  /**
   * Create a proxy authentication error
   */
  static proxyAuthRequired(res: IncomingMessage, head: Buffer): ProxyAuthenticationError {
    return new ProxyAuthenticationError('Proxy authentication required', 407, res, head)
  }

  /**
   * Create a proxy authentication failed error
   */
  static proxyAuthFailed(): AuthenticationError {
    return new AuthenticationError('Proxy authentication failed', 407, 'proxy')
  }

  /**
   * Create a proxy configuration error
   */
  static proxyConfigError(message: string): ProxyError {
    return new ProxyError(message, 400, 'configuration')
  }

  /**
   * Create a payload serialization error
   */
  static payloadSerializationError(message: string): PayloadError {
    return new PayloadError(message, 400, 'serialization')
  }

  /**
   * Create an unsupported payload error
   */
  static unsupportedPayload(payloadType: string): PayloadError {
    return new PayloadError(`Unsupported payload type: ${payloadType}`, 400, 'unsupported')
  }
}
