//! Enhanced Circuit Breaker Infrastructure
//!
//! Provides comprehensive circuit breaker patterns with integration to the unified
//! error hierarchy, sophisticated failure detection, and recovery strategies.

#![deny(clippy::unwrap_used, clippy::expect_used)]
#![warn(missing_docs)]

use crate::types::errors::{FoxhuntError, FoxhuntResult};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use tokio::sync::RwLock;

/// Circuit Breaker State
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
/// CircuitState
///
/// Auto-generated documentation placeholder - enhance with specifics
pub enum CircuitState {
    /// Normal operation - all calls allowed
    Closed,
    /// Service failing - most calls blocked
    Open,
    /// Testing recovery - limited calls allowed
    HalfOpen,
}

impl std::fmt::Display for CircuitState {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Closed => write!(f, "CLOSED"),
            Self::Open => write!(f, "OPEN"),
            Self::HalfOpen => write!(f, "HALF_OPEN"),
        }
    }
}

/// Circuit Breaker Configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
/// CircuitBreakerConfig
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct CircuitBreakerConfig {
    /// Number of consecutive failures to trigger open state
    pub failure_threshold: usize,
    /// Success rate threshold to trigger open state (0.0 - 1.0)
    pub success_rate_threshold: f64,
    /// Minimum number of requests to evaluate success rate
    pub minimum_requests: usize,
    /// `Duration` to wait before transitioning from Open to `HalfOpen`
    pub open_timeout: Duration,
    /// `Duration` for evaluating success rate in closed state
    pub rolling_window: Duration,
    /// Number of successful calls needed to close circuit in `HalfOpen` state
    pub half_open_success_threshold: usize,
    /// Maximum concurrent requests in `HalfOpen` state
    pub half_open_max_calls: usize,
    /// Timeout for individual operations
    pub operation_timeout: Duration,
    /// Enable latency-based circuit breaking
    pub enable_latency_detection: bool,
    /// Latency threshold for circuit breaking (95th percentile)
    pub latency_threshold: Duration,
    /// Enable error-type specific thresholds
    pub enable_error_classification: bool,
}

impl Default for CircuitBreakerConfig {
    fn default() -> Self {
        Self {
            failure_threshold: 5,
            success_rate_threshold: 0.5, // 50% success rate
            minimum_requests: 10,
            open_timeout: Duration::from_secs(30),
            rolling_window: Duration::from_secs(60),
            half_open_success_threshold: 3,
            half_open_max_calls: 2,
            operation_timeout: Duration::from_secs(30),
            enable_latency_detection: true,
            latency_threshold: Duration::from_millis(1000), // 1 second
            enable_error_classification: true,
        }
    }
}

impl CircuitBreakerConfig {
    /// Create configuration optimized for `HFT` services
    #[must_use]
    pub const fn hft_optimized() -> Self {
        Self {
            failure_threshold: 3,         // Lower threshold for HFT
            success_rate_threshold: 0.95, // Higher success rate requirement
            minimum_requests: 5,
            open_timeout: Duration::from_secs(10), // Faster recovery
            rolling_window: Duration::from_secs(30),
            half_open_success_threshold: 2,
            half_open_max_calls: 1, // Single probe for HFT
            operation_timeout: Duration::from_millis(50), // 50ms timeout
            enable_latency_detection: true,
            latency_threshold: Duration::from_millis(10), // 10ms threshold
            enable_error_classification: true,
        }
    }

    /// Create configuration for market data feeds
    #[must_use]
    pub const fn market_data_optimized() -> Self {
        Self {
            failure_threshold: 10, // Higher tolerance for market data
            success_rate_threshold: 0.8,
            minimum_requests: 20,
            open_timeout: Duration::from_secs(5), // Quick recovery for market data
            rolling_window: Duration::from_secs(60),
            half_open_success_threshold: 5,
            half_open_max_calls: 3,
            operation_timeout: Duration::from_secs(5),
            enable_latency_detection: true,
            latency_threshold: Duration::from_millis(100),
            enable_error_classification: true,
        }
    }

    /// Create configuration for external broker connections
    #[must_use]
    pub const fn broker_optimized() -> Self {
        Self {
            failure_threshold: 5,
            success_rate_threshold: 0.9,
            minimum_requests: 10,
            open_timeout: Duration::from_secs(60), // Longer recovery for brokers
            rolling_window: Duration::from_secs(120),
            half_open_success_threshold: 3,
            half_open_max_calls: 2,
            operation_timeout: Duration::from_secs(30),
            enable_latency_detection: true,
            latency_threshold: Duration::from_millis(500),
            enable_error_classification: true,
        }
    }
}

/// Request statistics for circuit breaker decision making
#[derive(Debug)]
struct RequestStats {
    /// Total number of requests
    total_requests: AtomicUsize,
    /// Number of successful requests
    successful_requests: AtomicUsize,
    /// Number of failed requests
    failed_requests: AtomicUsize,
    /// Consecutive failure count
    consecutive_failures: AtomicUsize,
    /// Last request timestamp
    last_request_time: AtomicU64,
    /// Last failure timestamp
    last_failure_time: AtomicU64,
    /// Average latency (in nanoseconds)
    average_latency_ns: AtomicU64,
    /// 95th percentile latency (in nanoseconds)
    p95_latency_ns: AtomicU64,
}

impl RequestStats {
    const fn new() -> Self {
        Self {
            total_requests: AtomicUsize::new(0),
            successful_requests: AtomicUsize::new(0),
            failed_requests: AtomicUsize::new(0),
            consecutive_failures: AtomicUsize::new(0),
            last_request_time: AtomicU64::new(0),
            last_failure_time: AtomicU64::new(0),
            average_latency_ns: AtomicU64::new(0),
            p95_latency_ns: AtomicU64::new(0),
        }
    }

    fn record_success(&self) {
        self.total_requests.fetch_add(1, Ordering::Relaxed);
        self.successful_requests.fetch_add(1, Ordering::Relaxed);
        self.consecutive_failures.store(0, Ordering::Relaxed);

        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_secs())
            .unwrap_or(0);
        self.last_request_time.store(now, Ordering::Relaxed);
    }

    fn record_failure(&self, _error: &FoxhuntError) {
        self.total_requests.fetch_add(1, Ordering::Relaxed);
        self.failed_requests.fetch_add(1, Ordering::Relaxed);
        self.consecutive_failures.fetch_add(1, Ordering::Relaxed);

        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_secs())
            .unwrap_or(0);
        self.last_request_time.store(now, Ordering::Relaxed);
        self.last_failure_time.store(now, Ordering::Relaxed);
    }

    fn get_success_rate(&self) -> f64 {
        let total = self.total_requests.load(Ordering::Relaxed);
        if total == 0 {
            return 1.0; // No requests yet, assume healthy
        }
        let successful = self.successful_requests.load(Ordering::Relaxed);
        successful as f64 / total as f64
    }

    fn reset_rolling_window(&self) {
        self.total_requests.store(0, Ordering::Relaxed);
        self.successful_requests.store(0, Ordering::Relaxed);
        self.failed_requests.store(0, Ordering::Relaxed);
        // Don't reset consecutive failures - they persist across windows
    }
}

/// Enhanced Circuit Breaker
#[derive(Debug)]
pub struct CircuitBreaker {
    /// Service identifier
    service_name: String,
    /// Circuit breaker configuration
    config: CircuitBreakerConfig,
    /// Current circuit state
    state: Arc<RwLock<CircuitState>>,
    /// Request statistics
    stats: RequestStats,
    /// Half-open state tracking
    half_open_calls: AtomicUsize,
    half_open_successes: AtomicUsize,
    /// State transition timestamp
    state_change_time: AtomicU64,
    /// Rolling window start time
    window_start_time: AtomicU64,
}

impl CircuitBreaker {
    /// Create a new circuit breaker
    #[must_use]
    pub fn new(service_name: String, config: CircuitBreakerConfig) -> Self {
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_secs())
            .unwrap_or(0);

        Self {
            service_name,
            config,
            state: Arc::new(RwLock::new(CircuitState::Closed)),
            stats: RequestStats::new(),
            half_open_calls: AtomicUsize::new(0),
            half_open_successes: AtomicUsize::new(0),
            state_change_time: AtomicU64::new(now),
            window_start_time: AtomicU64::new(now),
        }
    }

    /// Create with default configuration
    #[must_use]
    pub fn new_default(service_name: String) -> Self {
        Self::new(service_name, CircuitBreakerConfig::default())
    }

    /// Create with `HFT`-optimized configuration
    #[must_use]
    pub fn new_hft(service_name: String) -> Self {
        Self::new(service_name, CircuitBreakerConfig::hft_optimized())
    }

    /// Execute an operation with circuit breaker protection
    pub async fn execute<F, Fut, T>(&self, operation: F) -> FoxhuntResult<T>
    where
        F: FnOnce() -> Fut + Send,
        Fut: std::future::Future<Output = FoxhuntResult<T>> + Send,
        T: Send,
    {
        // Check if call is allowed
        self.check_call_allowed().await?;

        // Execute operation with timing
        let start_time = Instant::now();
        let result = tokio::time::timeout(self.config.operation_timeout, operation()).await;

        let _latency = start_time.elapsed();

        // Handle timeout
        let result = if let Ok(result) = result {
            result
        } else {
            let timeout_error = FoxhuntError::ServiceTimeout {
                service: self.service_name.clone(),
                timeout_ms: self.config.operation_timeout.as_millis() as u64,
                operation: Some("circuit_breaker_operation".to_owned()),
            };
            self.record_failure(&timeout_error).await;
            return Err(timeout_error);
        };

        // Record result and update circuit state
        match &result {
            Ok(_) => {
                self.record_success().await;
            },
            Err(error) => {
                self.record_failure(error).await;
            },
        }

        result
    }

    /// Execute with automatic retry based on recovery strategy
    pub async fn execute_with_retry<F, Fut, T>(
        &self,
        mut operation: F,
        max_retries: u32,
    ) -> FoxhuntResult<T>
    where
        F: FnMut() -> Fut + Send,
        Fut: std::future::Future<Output = FoxhuntResult<T>> + Send,
        T: Send,
    {
        let mut attempts = 0;
        let mut last_error = None;

        while attempts <= max_retries {
            match self.execute(&mut operation).await {
                Ok(result) => return Ok(result),
                Err(error) => {
                    attempts += 1;
                    last_error = Some(error.clone());

                    // Check if error is retryable
                    if !error.is_retryable() {
                        return Err(error);
                    }

                    // Apply exponential backoff if not last attempt
                    if attempts <= max_retries {
                        let delay = Duration::from_millis(100 * (2_u64.pow(attempts - 1)));
                        tokio::time::sleep(delay).await;
                    }
                },
            }
        }

        Err(last_error.unwrap_or_else(|| FoxhuntError::Internal {
            reason: "Retry loop failed without error".to_owned(),
            component: Some("circuit_breaker".to_owned()),
            context: Some(self.service_name.clone()),
            source_description: None,
        }))
    }

    /// Check if calls are currently allowed
    async fn check_call_allowed(&self) -> FoxhuntResult<()> {
        // Check rolling window reset
        self.check_rolling_window_reset().await;

        let state = *self.state.read().await;

        match state {
            CircuitState::Closed => {
                // Check if we need to open based on failure criteria
                if self.should_open_circuit().await {
                    self.transition_to_open().await;
                    return Err(FoxhuntError::CircuitBreaker {
                        state: "OPEN".to_owned(),
                        reason: "Failure threshold exceeded".to_owned(),
                        component: self.service_name.clone(),
                        threshold: Some(self.config.failure_threshold as f64),
                    });
                }
                Ok(())
            },
            CircuitState::Open => {
                // Check if we can transition to half-open
                if self.should_transition_to_half_open().await {
                    self.transition_to_half_open().await;
                    // Increment half_open_calls since we're allowing this call through
                    self.half_open_calls.fetch_add(1, Ordering::Relaxed);
                    Ok(())
                } else {
                    Err(FoxhuntError::CircuitBreaker {
                        state: "OPEN".to_owned(),
                        reason: "Circuit breaker is open".to_owned(),
                        component: self.service_name.clone(),
                        threshold: None,
                    })
                }
            },
            CircuitState::HalfOpen => {
                // Check if we can allow more calls
                let current_calls = self.half_open_calls.load(Ordering::Relaxed);
                if current_calls < self.config.half_open_max_calls {
                    self.half_open_calls.fetch_add(1, Ordering::Relaxed);
                    Ok(())
                } else {
                    Err(FoxhuntError::CircuitBreaker {
                        state: "HALF_OPEN".to_owned(),
                        reason: "Half-open call limit reached".to_owned(),
                        component: self.service_name.clone(),
                        threshold: Some(self.config.half_open_max_calls as f64),
                    })
                }
            },
        }
    }

    /// Record successful operation
    pub async fn record_success(&self) {
        self.stats.record_success();

        let state = *self.state.read().await;

        // Check latency-based circuit breaking
        if self.config.enable_latency_detection {
            let p95_latency =
                Duration::from_nanos(self.stats.p95_latency_ns.load(Ordering::Relaxed));
            if p95_latency > self.config.latency_threshold {
                tracing::warn!(
                    service = %self.service_name,
                    latency_ms = p95_latency.as_millis(),
                    threshold_ms = self.config.latency_threshold.as_millis(),
                    "High latency detected, monitoring for circuit breaking"
                );
            }
        }

        if state == CircuitState::HalfOpen {
            let successes = self.half_open_successes.fetch_add(1, Ordering::Relaxed) + 1;
            self.half_open_calls.fetch_sub(1, Ordering::Relaxed);

            if successes >= self.config.half_open_success_threshold {
                self.transition_to_closed().await;
            }
        } else {
            // Success in closed state resets consecutive failures
        }

        tracing::debug!(
            service = %self.service_name,
            state = %state,
            "Circuit breaker: Operation succeeded"
        );
    }

    /// Record failed operation
    pub async fn record_failure(&self, error: &FoxhuntError) {
        self.stats.record_failure(error);

        // Check current state to determine transition behavior
        let current_state = *self.state.read().await;

        match current_state {
            CircuitState::HalfOpen => {
                // Any failure in half-open immediately transitions to open
                self.half_open_calls.store(0, Ordering::Relaxed);
                self.half_open_successes.store(0, Ordering::Relaxed);
                self.transition_to_open().await;
            },
            CircuitState::Closed => {
                // Check if we should transition to open based on failure criteria
                if self.should_open_circuit().await {
                    self.transition_to_open().await;
                }
            },
            CircuitState::Open => {
                // Already open, nothing to do
            },
        }

        // Re-read state for accurate logging after potential transitions
        let final_state = *self.state.read().await;

        tracing::error!(
            service = %self.service_name,
            state = %final_state,
            error = %error,
            consecutive_failures = self.stats.consecutive_failures.load(Ordering::Relaxed),
            "Circuit breaker: Operation failed"
        );
    }

    /// Check if circuit should open based on failure criteria
    async fn should_open_circuit(&self) -> bool {
        let consecutive_failures = self.stats.consecutive_failures.load(Ordering::Relaxed);
        let total_requests = self.stats.total_requests.load(Ordering::Relaxed);

        // Check consecutive failure threshold
        if consecutive_failures >= self.config.failure_threshold {
            return true;
        }

        // Check success rate threshold (only if minimum requests met)
        if total_requests >= self.config.minimum_requests {
            let success_rate = self.stats.get_success_rate();
            if success_rate < self.config.success_rate_threshold {
                return true;
            }
        }

        // Check latency threshold
        if self.config.enable_latency_detection {
            let p95_latency =
                Duration::from_nanos(self.stats.p95_latency_ns.load(Ordering::Relaxed));
            if p95_latency > self.config.latency_threshold
                && total_requests >= self.config.minimum_requests
            {
                return true;
            }
        }

        false
    }

    /// Check if circuit should transition from open to half-open
    async fn should_transition_to_half_open(&self) -> bool {
        let state_change_time = self.state_change_time.load(Ordering::Relaxed);
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_secs())
            .unwrap_or(0);

        now.saturating_sub(state_change_time) >= self.config.open_timeout.as_secs()
    }

    /// Check if rolling window should be reset
    async fn check_rolling_window_reset(&self) {
        let window_start = self.window_start_time.load(Ordering::Relaxed);
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_secs())
            .unwrap_or(0);

        if now.saturating_sub(window_start) >= self.config.rolling_window.as_secs() {
            self.stats.reset_rolling_window();
            self.window_start_time.store(now, Ordering::Relaxed);
        }
    }

    /// Transition to closed state
    async fn transition_to_closed(&self) {
        let mut state = self.state.write().await;
        *state = CircuitState::Closed;

        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_secs())
            .unwrap_or(0);
        self.state_change_time.store(now, Ordering::Relaxed);

        // Reset half-open counters
        self.half_open_calls.store(0, Ordering::Relaxed);
        self.half_open_successes.store(0, Ordering::Relaxed);

        tracing::info!(
            service = %self.service_name,
            "Circuit breaker transitioned to CLOSED"
        );
    }

    /// Transition to open state
    async fn transition_to_open(&self) {
        let mut state = self.state.write().await;
        *state = CircuitState::Open;

        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_secs())
            .unwrap_or(0);
        self.state_change_time.store(now, Ordering::Relaxed);

        tracing::error!(
            service = %self.service_name,
            consecutive_failures = self.stats.consecutive_failures.load(Ordering::Relaxed),
            success_rate = self.stats.get_success_rate(),
            "Circuit breaker transitioned to OPEN"
        );
    }

    /// Transition to half-open state
    async fn transition_to_half_open(&self) {
        let mut state = self.state.write().await;
        *state = CircuitState::HalfOpen;

        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .map(|d| d.as_secs())
            .unwrap_or(0);
        self.state_change_time.store(now, Ordering::Relaxed);

        // Reset half-open counters
        self.half_open_calls.store(0, Ordering::Relaxed);
        self.half_open_successes.store(0, Ordering::Relaxed);

        tracing::info!(
            service = %self.service_name,
            "Circuit breaker transitioned to HALF_OPEN"
        );
    }

    /// Get current circuit breaker state
    pub async fn state(&self) -> CircuitState {
        *self.state.read().await
    }

    /// Check if the circuit breaker is open
    pub async fn is_open(&self) -> bool {
        matches!(self.state().await, CircuitState::Open)
    }

    /// Get circuit breaker metrics
    pub async fn metrics(&self) -> CircuitBreakerMetrics {
        CircuitBreakerMetrics {
            service_name: self.service_name.clone(),
            state: self.state().await,
            total_requests: self.stats.total_requests.load(Ordering::Relaxed),
            successful_requests: self.stats.successful_requests.load(Ordering::Relaxed),
            failed_requests: self.stats.failed_requests.load(Ordering::Relaxed),
            consecutive_failures: self.stats.consecutive_failures.load(Ordering::Relaxed),
            success_rate: self.stats.get_success_rate(),
            average_latency: Duration::from_nanos(
                self.stats.average_latency_ns.load(Ordering::Relaxed),
            ),
            p95_latency: Duration::from_nanos(self.stats.p95_latency_ns.load(Ordering::Relaxed)),
            last_failure_time: self.stats.last_failure_time.load(Ordering::Relaxed),
            state_change_time: self.state_change_time.load(Ordering::Relaxed),
        }
    }

    /// Get service name
    pub fn service_name(&self) -> &str {
        &self.service_name
    }

    /// Force circuit breaker to open (for testing/emergency)
    pub async fn force_open(&self) {
        self.transition_to_open().await;
    }

    /// Force circuit breaker to close (for recovery)
    pub async fn force_close(&self) {
        self.transition_to_closed().await;
    }
}

/// Circuit Breaker Metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
/// CircuitBreakerMetrics
///
/// Auto-generated documentation placeholder - enhance with specifics
pub struct CircuitBreakerMetrics {
    /// Service name
    pub service_name: String,
    /// Current state
    pub state: CircuitState,
    /// Total number of requests
    pub total_requests: usize,
    /// Number of successful requests
    pub successful_requests: usize,
    /// Number of failed requests
    pub failed_requests: usize,
    /// Consecutive failure count
    pub consecutive_failures: usize,
    /// Success rate (0.0 - 1.0)
    pub success_rate: f64,
    /// Average latency
    pub average_latency: Duration,
    /// 95th percentile latency
    pub p95_latency: Duration,
    /// Last failure timestamp
    pub last_failure_time: u64,
    /// Last state change timestamp
    pub state_change_time: u64,
}

/// Circuit Breaker Registry for managing multiple circuit breakers
#[derive(Debug)]
pub struct CircuitBreakerRegistry {
    breakers: Arc<RwLock<HashMap<String, Arc<CircuitBreaker>>>>,
    default_config: CircuitBreakerConfig,
}

impl CircuitBreakerRegistry {
    /// Create new registry with default configuration
    #[must_use]
    pub fn new() -> Self {
        Self {
            breakers: Arc::new(RwLock::new(HashMap::new())),
            default_config: CircuitBreakerConfig::default(),
        }
    }

    /// Create new registry with custom default configuration
    #[must_use]
    pub fn with_config(config: CircuitBreakerConfig) -> Self {
        Self {
            breakers: Arc::new(RwLock::new(HashMap::new())),
            default_config: config,
        }
    }

    /// Get or create circuit breaker for service
    pub async fn get_or_create(&self, service_name: &str) -> Arc<CircuitBreaker> {
        let breakers = self.breakers.read().await;
        if let Some(breaker) = breakers.get(service_name) {
            return breaker.clone();
        }
        drop(breakers);

        // Create new circuit breaker
        let breaker = Arc::new(CircuitBreaker::new(
            service_name.to_owned(),
            self.default_config.clone(),
        ));

        let mut breakers = self.breakers.write().await;
        breakers.insert(service_name.to_owned(), breaker.clone());
        breaker
    }

    /// Get or create circuit breaker with custom configuration
    pub async fn get_or_create_with_config(
        &self,
        service_name: &str,
        config: CircuitBreakerConfig,
    ) -> Arc<CircuitBreaker> {
        let breakers = self.breakers.read().await;
        if let Some(breaker) = breakers.get(service_name) {
            return breaker.clone();
        }
        drop(breakers);

        // Create new circuit breaker with custom config
        let breaker = Arc::new(CircuitBreaker::new(service_name.to_owned(), config));

        let mut breakers = self.breakers.write().await;
        breakers.insert(service_name.to_owned(), breaker.clone());
        breaker
    }

    /// Get all circuit breaker metrics
    pub async fn get_all_metrics(&self) -> Vec<CircuitBreakerMetrics> {
        let breakers = self.breakers.read().await;
        let mut metrics = Vec::new();

        for breaker in breakers.values() {
            metrics.push(breaker.metrics().await);
        }

        metrics
    }

    /// Get circuit breaker for service if it exists
    pub async fn get(&self, service_name: &str) -> Option<Arc<CircuitBreaker>> {
        let breakers = self.breakers.read().await;
        breakers.get(service_name).cloned()
    }

    /// Remove circuit breaker
    pub async fn remove(&self, service_name: &str) -> Option<Arc<CircuitBreaker>> {
        let mut breakers = self.breakers.write().await;
        breakers.remove(service_name)
    }

    /// Get all service names
    pub async fn service_names(&self) -> Vec<String> {
        let breakers = self.breakers.read().await;
        breakers.keys().cloned().collect()
    }

    /// Force open all circuit breakers (emergency)
    pub async fn emergency_open_all(&self) {
        let breakers = self.breakers.read().await;
        for breaker in breakers.values() {
            breaker.force_open().await;
        }
        tracing::error!("EMERGENCY: All circuit breakers forced open");
    }

    /// Get unhealthy services (open or half-open circuits)
    pub async fn get_unhealthy_services(&self) -> Vec<String> {
        let breakers = self.breakers.read().await;
        let mut unhealthy = Vec::new();

        for breaker in breakers.values() {
            let state = breaker.state().await;
            if state != CircuitState::Closed {
                unhealthy.push(breaker.service_name().to_owned());
            }
        }

        unhealthy
    }
}

impl Default for CircuitBreakerRegistry {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
#[allow(clippy::assertions_on_result_states, clippy::let_underscore_must_use)]
mod tests {
    use super::*;
    use tokio::time::{sleep, Duration};

    #[tokio::test]
    async fn test_circuit_breaker_closed_to_open() {
        let breaker = CircuitBreaker::new(
            "test_service".to_string(),
            CircuitBreakerConfig {
                failure_threshold: 3,
                ..Default::default()
            },
        );

        // Initial state should be closed
        assert_eq!(breaker.state().await, CircuitState::Closed);

        // Simulate failures
        for i in 1..=3 {
            let result = breaker
                .execute(|| async {
                    Err::<(), _>(FoxhuntError::Network {
                        reason: "Connection failed".to_string(),
                        endpoint: Some("test_endpoint".to_string()),
                        operation: Some("test".to_string()),
                        source_description: None,
                    })
                })
                .await;
            assert!(result.is_err());

            if i < 3 {
                assert_eq!(breaker.state().await, CircuitState::Closed);
            }
        }

        // Circuit should now be open
        assert_eq!(breaker.state().await, CircuitState::Open);
    }

    #[tokio::test]
    async fn test_circuit_breaker_success_rate() {
        let breaker = CircuitBreaker::new(
            "test_service".to_string(),
            CircuitBreakerConfig {
                success_rate_threshold: 0.5,
                minimum_requests: 4,
                ..Default::default()
            },
        );

        // 2 successes, 2 failures = 50% success rate (should remain closed)
        for _ in 0..2 {
            let _ = breaker
                .execute(|| async { Ok::<(), FoxhuntError>(()) })
                .await;
        }
        for _ in 0..2 {
            let _ = breaker
                .execute(|| async {
                    Err::<(), _>(FoxhuntError::Internal {
                        reason: "Test failure".to_string(),
                        component: None,
                        context: None,
                        source_description: None,
                    })
                })
                .await;
        }

        assert_eq!(breaker.state().await, CircuitState::Closed);

        // One more failure should trigger open (success rate < 50%)
        let _ = breaker
            .execute(|| async {
                Err::<(), _>(FoxhuntError::Internal {
                    reason: "Test failure".to_string(),
                    component: None,
                    context: None,
                    source_description: None,
                })
            })
            .await;

        assert_eq!(breaker.state().await, CircuitState::Open);
    }

    #[tokio::test]
    async fn test_circuit_breaker_registry() {
        let registry = CircuitBreakerRegistry::new();

        // Get or create circuit breaker
        let breaker1 = registry.get_or_create("service1").await;
        let breaker2 = registry.get_or_create("service1").await;

        // Should return the same instance
        assert!(Arc::ptr_eq(&breaker1, &breaker2));

        // Different service should get different instance
        let breaker3 = registry.get_or_create("service2").await;
        assert!(!Arc::ptr_eq(&breaker1, &breaker3));

        // Check service names
        let service_names = registry.service_names().await;
        assert_eq!(service_names.len(), 2);
        assert!(service_names.contains(&"service1".to_string()));
        assert!(service_names.contains(&"service2".to_string()));
    }

    #[tokio::test]
    async fn test_circuit_breaker_timeout() {
        let breaker = CircuitBreaker::new(
            "test_service".to_string(),
            CircuitBreakerConfig {
                operation_timeout: Duration::from_millis(100),
                ..Default::default()
            },
        );

        // Operation that takes longer than timeout
        let result = breaker
            .execute(|| async {
                sleep(Duration::from_millis(200)).await;
                Ok::<(), FoxhuntError>(())
            })
            .await;

        assert!(result.is_err());
        if let Err(FoxhuntError::ServiceTimeout { .. }) = result {
            // Expected timeout error
        } else {
            panic!("Expected timeout error");
        }
    }

    #[tokio::test]
    async fn test_circuit_breaker_half_open_recovery() {
        let breaker = CircuitBreaker::new(
            "test_service".to_string(),
            CircuitBreakerConfig {
                failure_threshold: 2,
                open_timeout: Duration::from_millis(100),
                half_open_success_threshold: 2,
                ..Default::default()
            },
        );

        // Trigger failures to open circuit
        for _ in 0..2 {
            let _ = breaker
                .execute(|| async {
                    Err::<(), _>(FoxhuntError::Internal {
                        reason: "Test failure".to_string(),
                        component: None,
                        context: None,
                        source_description: None,
                    })
                })
                .await;
        }
        assert_eq!(breaker.state().await, CircuitState::Open);

        // Wait for open timeout
        sleep(Duration::from_millis(150)).await;

        // Next call should transition to half-open
        let result = breaker
            .execute(|| async { Ok::<(), FoxhuntError>(()) })
            .await;
        assert!(result.is_ok());
        assert_eq!(breaker.state().await, CircuitState::HalfOpen);

        // One more success should close the circuit
        let result = breaker
            .execute(|| async { Ok::<(), FoxhuntError>(()) })
            .await;
        assert!(result.is_ok());
        assert_eq!(breaker.state().await, CircuitState::Closed);
    }
}
