/**
 * LLM-specific Circuit Breaker
 *
 * This module provides LLM-specific circuit breaker functionality by wrapping
 * the generic circuit breaker from @repo/core/resilience.
 *
 * Key design decisions:
 * - CircuitBreakerOpenError extends ExecutionError (not ServiceUnavailableError)
 *   to maintain compatibility with the execution engine's error handling
 * - Uses the generic circuit breaker state machine for consistency
 * - Converts generic ServiceUnavailableError to LLM-specific CircuitBreakerOpenError
 */

import { ExecutionError } from '../../base/errors'
import {
  createCircuitBreaker,
  CircuitState,
  ServiceUnavailableError,
  type CircuitBreaker,
  type CircuitBreakerConfig
} from '../../../../platform/resilience'

// Re-export CircuitState for backward compatibility
export { CircuitState as CircuitBreakerState }

// Module-level storage (shared across all executions, one breaker per model)
const llmCircuitBreakers = new Map<string, CircuitBreaker>()

// Injectable state change handler -- set by server layer via configureCircuitBreakerAlerts()
let stateChangeHandler: CircuitBreakerConfig['onStateChange'] | undefined

/**
 * Configure the alert handler for LLM circuit breaker state changes.
 * Call once at server startup to wire circuit breaker transitions to Better Stack.
 */
export function configureCircuitBreakerAlerts(
  handler: NonNullable<CircuitBreakerConfig['onStateChange']>
): void {
  stateChangeHandler = handler
}

/**
 * Get or create circuit breaker for a model
 */
export function getCircuitBreaker(modelName: string): CircuitBreaker {
  if (!llmCircuitBreakers.has(modelName)) {
    llmCircuitBreakers.set(
      modelName,
      createCircuitBreaker(`llm:${modelName}`, { onStateChange: stateChangeHandler })
    )
  }
  return llmCircuitBreakers.get(modelName)!
}

/**
 * Execute a function through the circuit breaker for a specific model.
 * This is the preferred API - it handles all state transitions automatically.
 *
 * @param modelName - The LLM model name (e.g., 'gpt-4', 'claude-3')
 * @param fn - The async function to execute
 * @returns The result of fn()
 * @throws CircuitBreakerOpenError if the circuit is open
 * @throws The original error from fn() if it fails
 *
 * @example
 * ```typescript
 * const response = await executeWithCircuitBreaker('gpt-4', async () => {
 *   return await openai.chat.completions.create({ ... })
 * })
 * ```
 */
export async function executeWithCircuitBreaker<T>(
  modelName: string,
  fn: () => Promise<T>
): Promise<T> {
  const breaker = getCircuitBreaker(modelName)

  try {
    return await breaker.execute(fn)
  } catch (error) {
    // Convert generic ServiceUnavailableError to LLM-specific error
    if (error instanceof ServiceUnavailableError) {
      throw new CircuitBreakerOpenError(modelName, {
        state: breaker.getState(),
        failureCount: breaker.getFailureCount(),
        reason: error.reason
      })
    }
    throw error
  }
}

/**
 * Error indicating circuit breaker is open for an LLM model.
 *
 * MUST extend ExecutionError for execution engine compatibility.
 * This is separate from ServiceUnavailableError by design.
 *
 * Severity: critical (execution cannot proceed while circuit is open)
 * Category: system (infrastructure/system protection mechanism)
 */
export class CircuitBreakerOpenError extends ExecutionError {
  readonly type = 'circuit_breaker_open' as const
  readonly severity = 'critical' as const
  readonly category = 'system' as const

  constructor(modelName: string, context?: Record<string, unknown>) {
    super(`Circuit breaker open for model '${modelName}' - too many recent failures`, {
      ...context,
      modelName
    })
  }

  override isRetryable(): boolean {
    return true // Circuit breaker handles retry delay internally
  }
}

// ============================================================================
// Legacy API (for backward compatibility)
// These functions are kept for API compatibility but delegate to the new implementation
// ============================================================================

/**
 * Check if circuit breaker allows requests for a model
 * @deprecated Use executeWithCircuitBreaker() instead
 */
export function canProceed(modelName: string): boolean {
  const breaker = getCircuitBreaker(modelName)
  return breaker.getState() !== CircuitState.OPEN
}

/**
 * Record successful LLM call
 * @deprecated Success is now recorded automatically in executeWithCircuitBreaker()
 */
export function recordSuccess(_modelName: string): void {
  // No-op: Success is recorded automatically in breaker.execute()
}

/**
 * Record failed LLM call
 * @deprecated Failure is now recorded automatically in executeWithCircuitBreaker()
 */
export function recordFailure(_modelName: string): void {
  // No-op: Failure is recorded automatically in breaker.execute()
}
