/**
 * Infrastructure error types for resilience utilities.
 * These errors represent external service failures (not business logic errors).
 * Map to HTTP 503 Service Unavailable.
 */

/**
 * Thrown when an external service operation times out.
 * Maps to HTTP 503 Service Unavailable.
 */
export class ServiceTimeoutError extends Error {
  readonly service: string
  readonly timeoutMs: number
  readonly operation?: string
  readonly context?: Record<string, unknown>

  constructor(
    service: string,
    timeoutMs: number,
    operation?: string,
    context?: Record<string, unknown>
  ) {
    const op = operation ? `:${operation}` : ''
    super(`${service}${op} timed out after ${timeoutMs}ms`)
    this.name = 'ServiceTimeoutError'
    this.service = service
    this.timeoutMs = timeoutMs
    this.operation = operation
    this.context = context
  }
}

/**
 * Thrown when an external service is unavailable (circuit open, repeated failures).
 * Maps to HTTP 503 Service Unavailable.
 */
export class ServiceUnavailableError extends Error {
  readonly service: string
  readonly reason: 'circuit_open' | 'timeout' | 'connection_failed'
  readonly context?: Record<string, unknown>

  constructor(
    service: string,
    reason: 'circuit_open' | 'timeout' | 'connection_failed',
    context?: Record<string, unknown>
  ) {
    super(`${service} temporarily unavailable (${reason})`)
    this.name = 'ServiceUnavailableError'
    this.service = service
    this.reason = reason
    this.context = context
  }
}

/**
 * Type guard to check if an error is an infrastructure error (ServiceTimeoutError or ServiceUnavailableError).
 * Infrastructure errors should map to HTTP 503, not 401 or 500.
 */
export function isInfrastructureError(
  error: unknown
): error is ServiceTimeoutError | ServiceUnavailableError {
  if (error instanceof ServiceTimeoutError) return true
  if (error instanceof ServiceUnavailableError) return true

  // Also check for common network error codes
  const code = (error as { code?: string })?.code
  return code === 'ETIMEDOUT' || code === 'ECONNREFUSED' || code === 'ENOTFOUND'
}
