import type { TypedError } from './errors'

// Circuit breaker for resilience
export class CircuitBreaker {
  private failures = 0
  private lastFailureTime = 0
  private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED'
  private threshold: number
  private timeout: number
  private endpointStates = new Map<string, { failures: number; lastFailure: number; state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' }>()
  
  constructor(threshold = 5, timeout = 30000) {
    this.threshold = threshold
    this.timeout = timeout
  }
  
  async execute<T>(fn: () => Promise<T>, endpoint?: string): Promise<T> {
    // Use per-endpoint circuit breaker if endpoint is provided
    if (endpoint) {
      const endpointState = this.endpointStates.get(endpoint) || { failures: 0, lastFailure: 0, state: 'CLOSED' }
      
      if (endpointState.state === 'OPEN') {
        if (Date.now() - endpointState.lastFailure > this.timeout) {
          endpointState.state = 'HALF_OPEN'
        } else {
          throw this.createCircuitError()
        }
      }
      
      try {
        const result = await fn()
        this.onEndpointSuccess(endpoint)
        return result
      } catch (error) {
        this.onEndpointFailure(endpoint)
        throw error
      }
    }
    
    // Global circuit breaker
    if (this.state === 'OPEN') {
      if (Date.now() - this.lastFailureTime > this.timeout) {
        this.state = 'HALF_OPEN'
      } else {
        throw this.createCircuitError()
      }
    }
    
    try {
      const result = await fn()
      this.onSuccess()
      return result
    } catch (error) {
      this.onFailure()
      throw error
    }
  }
  
  private onSuccess(): void {
    this.failures = 0
    this.state = 'CLOSED'
  }
  
  private onFailure(): void {
    this.failures++
    this.lastFailureTime = Date.now()
    
    if (this.failures >= this.threshold) {
      this.state = 'OPEN'
    }
  }
  
  private onEndpointSuccess(endpoint: string): void {
    this.endpointStates.delete(endpoint)
  }
  
  private onEndpointFailure(endpoint: string): void {
    const state = this.endpointStates.get(endpoint) || { failures: 0, lastFailure: 0, state: 'CLOSED' }
    state.failures++
    state.lastFailure = Date.now()
    
    if (state.failures >= this.threshold) {
      state.state = 'OPEN'
    }
    
    this.endpointStates.set(endpoint, state)
  }
  
  reset(): void {
    this.failures = 0
    this.state = 'CLOSED'
    this.endpointStates.clear()
  }
  
  private createCircuitError(): TypedError {
    const error = new Error('Circuit breaker is OPEN - too many failures') as TypedError
    error.type = 'circuit'
    error.retryable = true
    error.retryAfter = this.timeout
    error.suggestions = [
      'Wait for circuit breaker to reset',
      'Check if service is healthy',
      'Try again in 30 seconds',
      'Call circuitBreaker.reset() to manually reset'
    ]
    error.debug = () => console.log('Circuit breaker state:', this.state, 'Failures:', this.failures)
    return error
  }
}