//! ML-Enhanced Circuit Breakers for HFT Risk Management
//!
//! Implements intelligent circuit breakers that use machine learning models
//! to detect market stress, model degradation, and risk anomalies with
//! canonical types for production HFT systems.

use std::collections::{HashMap, VecDeque};

use chrono::{DateTime, Utc};
use common::types::{Price, Volume};
use serde::{Deserialize, Serialize};

use crate::{MLError, MLResult as Result};

/// Circuit breaker type enumeration
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum CircuitBreakerType {
    /// Volatility-based circuit breaker
    Volatility,
    /// Drawdown-based circuit breaker
    Drawdown,
    /// Volume-based circuit breaker
    Volume,
    /// Model performance circuit breaker
    ModelPerformance,
    /// Market stress circuit breaker
    MarketStress,
    /// Position concentration circuit breaker
    PositionConcentration,
}

/// Circuit breaker configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CircuitBreakerConfig {
    pub volatility_threshold: f64,
    pub drawdown_threshold: f64,
    pub volume_spike_threshold: f64,
    pub model_accuracy_threshold: f64,
    pub max_position_size: f64,
    pub lookback_period: usize,
    pub cooldown_seconds: u64,
    pub enable_auto_reset: bool,
}

impl Default for CircuitBreakerConfig {
    fn default() -> Self {
        Self {
            volatility_threshold: 0.05,    // 5% volatility threshold
            drawdown_threshold: 0.03,      // 3% drawdown threshold
            volume_spike_threshold: 3.0,   // 3x normal volume
            model_accuracy_threshold: 0.8, // 80% minimum accuracy
            max_position_size: 0.1,        // 10% max position size
            lookback_period: 100,          // 100 periods lookback
            cooldown_seconds: 300,         // 5 minute cooldown
            enable_auto_reset: true,
        }
    }
}

/// Circuit breaker state
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CircuitBreakerState {
    pub breaker_type: CircuitBreakerType,
    pub is_triggered: bool,
    pub trigger_time: Option<DateTime<Utc>>,
    pub trigger_value: f64,
    pub threshold: f64,
    pub reset_time: Option<DateTime<Utc>>,
    pub trigger_count: u64,
}

impl CircuitBreakerState {
    pub fn new(breaker_type: CircuitBreakerType, threshold: f64) -> Self {
        Self {
            breaker_type,
            is_triggered: false,
            trigger_time: None,
            trigger_value: 0.0,
            threshold,
            reset_time: None,
            trigger_count: 0,
        }
    }

    pub fn trigger(&mut self, value: f64) {
        self.is_triggered = true;
        self.trigger_time = Some(Utc::now());
        self.trigger_value = value;
        self.trigger_count += 1;
    }

    pub fn reset(&mut self) {
        self.is_triggered = false;
        self.reset_time = Some(Utc::now());
        self.trigger_value = 0.0;
    }

    pub fn can_reset(&self, cooldown_seconds: u64) -> bool {
        if let Some(trigger_time) = self.trigger_time {
            let elapsed = Utc::now().signed_duration_since(trigger_time);
            elapsed.num_seconds() >= cooldown_seconds as i64
        } else {
            true
        }
    }
}

/// ML Circuit Breaker system
#[derive(Debug)]
pub struct MLCircuitBreaker {
    config: CircuitBreakerConfig,
    states: HashMap<CircuitBreakerType, CircuitBreakerState>,
    historical_data: VecDeque<MarketDataPoint>,
    performance_history: VecDeque<f64>,
}

/// Market data point for circuit breaker analysis
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MarketDataPoint {
    pub timestamp: DateTime<Utc>,
    pub price: Price,
    pub volume: Volume,
    pub volatility: f64,
    pub returns: f64,
}

impl MLCircuitBreaker {
    pub fn new(config: CircuitBreakerConfig) -> Result<Self> {
        let mut states = HashMap::new();

        // Initialize all circuit breaker states
        states.insert(
            CircuitBreakerType::Volatility,
            CircuitBreakerState::new(CircuitBreakerType::Volatility, config.volatility_threshold),
        );
        states.insert(
            CircuitBreakerType::Drawdown,
            CircuitBreakerState::new(CircuitBreakerType::Drawdown, config.drawdown_threshold),
        );
        states.insert(
            CircuitBreakerType::Volume,
            CircuitBreakerState::new(CircuitBreakerType::Volume, config.volume_spike_threshold),
        );
        states.insert(
            CircuitBreakerType::ModelPerformance,
            CircuitBreakerState::new(
                CircuitBreakerType::ModelPerformance,
                config.model_accuracy_threshold,
            ),
        );
        states.insert(
            CircuitBreakerType::MarketStress,
            CircuitBreakerState::new(CircuitBreakerType::MarketStress, 0.95), // 95th percentile
        );
        states.insert(
            CircuitBreakerType::PositionConcentration,
            CircuitBreakerState::new(
                CircuitBreakerType::PositionConcentration,
                config.max_position_size,
            ),
        );

        let lookback_period = config.lookback_period;
        Ok(Self {
            config,
            states,
            historical_data: VecDeque::with_capacity(lookback_period),
            performance_history: VecDeque::with_capacity(100),
        })
    }

    /// Update circuit breaker with new market data
    pub fn update_market_data(&mut self, data: MarketDataPoint) -> Result<Vec<CircuitBreakerType>> {
        let mut triggered_breakers = Vec::new();

        // Add to historical data
        self.historical_data.push_back(data.clone());
        if self.historical_data.len() > self.config.lookback_period {
            self.historical_data.pop_front();
        }

        // Check volatility circuit breaker
        if data.volatility > self.config.volatility_threshold {
            if let Some(state) = self.states.get_mut(&CircuitBreakerType::Volatility) {
                if !state.is_triggered {
                    state.trigger(data.volatility);
                    triggered_breakers.push(CircuitBreakerType::Volatility);
                }
            }
        }

        // Check volume circuit breaker
        if self.historical_data.len() > 10 {
            let avg_volume = self
                .historical_data
                .iter()
                .take(self.historical_data.len() - 1)
                .map(|d| d.volume.to_f64())
                .sum::<f64>()
                / (self.historical_data.len() - 1) as f64;

            let volume_ratio = data.volume.to_f64() / avg_volume;
            if volume_ratio > self.config.volume_spike_threshold {
                if let Some(state) = self.states.get_mut(&CircuitBreakerType::Volume) {
                    if !state.is_triggered {
                        state.trigger(volume_ratio);
                        triggered_breakers.push(CircuitBreakerType::Volume);
                    }
                }
            }
        }

        // Check drawdown circuit breaker
        if let Some(max_price) =
            self.historical_data
                .iter()
                .map(|d| d.price.to_f64())
                .fold(None, |max, price| match max {
                    None => Some(price),
                    Some(m) => Some(m.max(price)),
                })
        {
            let current_drawdown = (max_price - data.price.to_f64()) / max_price;
            if current_drawdown > self.config.drawdown_threshold {
                if let Some(state) = self.states.get_mut(&CircuitBreakerType::Drawdown) {
                    if !state.is_triggered {
                        state.trigger(current_drawdown);
                        triggered_breakers.push(CircuitBreakerType::Drawdown);
                    }
                }
            }
        }

        Ok(triggered_breakers)
    }

    /// Update model performance and check performance circuit breaker
    pub fn update_model_performance(&mut self, accuracy: f64) -> Result<bool> {
        self.performance_history.push_back(accuracy);
        if self.performance_history.len() > 100 {
            self.performance_history.pop_front();
        }

        if accuracy < self.config.model_accuracy_threshold {
            if let Some(state) = self.states.get_mut(&CircuitBreakerType::ModelPerformance) {
                if !state.is_triggered {
                    state.trigger(accuracy);
                    return Ok(true);
                }
            }
        }

        Ok(false)
    }

    /// Check if any circuit breakers are triggered
    pub fn is_any_triggered(&self) -> bool {
        self.states.values().any(|state| state.is_triggered)
    }

    /// Check if specific circuit breaker is triggered
    pub fn is_triggered(&self, breaker_type: CircuitBreakerType) -> bool {
        self.states
            .get(&breaker_type)
            .map(|state| state.is_triggered)
            .unwrap_or(false)
    }

    /// Get all triggered circuit breakers
    pub fn get_triggered_breakers(&self) -> Vec<CircuitBreakerType> {
        self.states
            .iter()
            .filter_map(|(breaker_type, state)| state.is_triggered.then_some(*breaker_type))
            .collect()
    }

    /// Reset all circuit breakers that can be reset
    pub fn reset_eligible_breakers(&mut self) -> Vec<CircuitBreakerType> {
        let mut reset_breakers = Vec::new();

        for (breaker_type, state) in self.states.iter_mut() {
            if state.is_triggered && state.can_reset(self.config.cooldown_seconds) {
                state.reset();
                reset_breakers.push(*breaker_type);
            }
        }

        reset_breakers
    }

    /// Manually reset specific circuit breaker
    pub fn reset_breaker(&mut self, breaker_type: CircuitBreakerType) -> Result<()> {
        if let Some(state) = self.states.get_mut(&breaker_type) {
            state.reset();
            Ok(())
        } else {
            Err(MLError::InvalidInput(format!(
                "Unknown circuit breaker type: {:?}",
                breaker_type
            )))
        }
    }

    /// Get circuit breaker state
    pub fn get_state(&self, breaker_type: CircuitBreakerType) -> Option<&CircuitBreakerState> {
        self.states.get(&breaker_type)
    }

    /// Get all circuit breaker states
    pub fn get_all_states(&self) -> &HashMap<CircuitBreakerType, CircuitBreakerState> {
        &self.states
    }

    /// Calculate market stress score
    pub fn calculate_market_stress(&self) -> f64 {
        if self.historical_data.len() < 10 {
            return 0.0;
        }

        // Calculate volatility score
        let volatilities: Vec<f64> = self.historical_data.iter().map(|d| d.volatility).collect();
        let avg_volatility = volatilities.iter().sum::<f64>() / volatilities.len() as f64;
        let volatility_score = (avg_volatility / self.config.volatility_threshold).min(1.0);

        // Calculate volume score
        let volumes: Vec<f64> = self
            .historical_data
            .iter()
            .map(|d| d.volume.to_f64())
            .collect();
        let avg_volume = volumes.iter().sum::<f64>() / volumes.len() as f64;
        let recent_volume = volumes.iter().rev().take(5).sum::<f64>() / 5.0;
        let volume_score =
            (recent_volume / avg_volume / self.config.volume_spike_threshold).min(1.0);

        // Calculate returns volatility
        let returns: Vec<f64> = self.historical_data.iter().map(|d| d.returns).collect();
        let returns_std = {
            let mean = returns.iter().sum::<f64>() / returns.len() as f64;
            let variance =
                returns.iter().map(|r| (r - mean).powi(2)).sum::<f64>() / returns.len() as f64;
            variance.sqrt()
        };
        let returns_score = (returns_std / 0.02).min(1.0); // 2% daily volatility baseline

        // Combined stress score
        volatility_score * 0.4 + volume_score * 0.3 + returns_score * 0.3
    }
}

#[cfg(test)]
mod tests {
    use super::*;


    #[test]
    fn test_circuit_breaker_creation() -> Result<()> {
        let config = CircuitBreakerConfig::default();
        let breaker = MLCircuitBreaker::new(config);
        assert!(breaker.is_ok());

        let breaker = breaker?;
        assert!(!breaker.is_any_triggered());
        assert_eq!(breaker.get_triggered_breakers().len(), 0);
        Ok(())
    }

    #[test]
    fn test_circuit_breaker_state() {
        let mut state = CircuitBreakerState::new(CircuitBreakerType::Volatility, 0.05);

        assert!(!state.is_triggered);
        assert_eq!(state.trigger_count, 0);

        state.trigger(0.08);
        assert!(state.is_triggered);
        assert_eq!(state.trigger_count, 1);
        assert_eq!(state.trigger_value, 0.08);

        state.reset();
        assert!(!state.is_triggered);
        assert_eq!(state.trigger_value, 0.0);
    }

    #[test]
    fn test_volatility_circuit_breaker() -> Result<()> {
        let config = CircuitBreakerConfig::default();
        let mut breaker = MLCircuitBreaker::new(config)?;

        // Normal volatility - should not trigger
        let normal_data = MarketDataPoint {
            timestamp: Utc::now(),
            price: Price::from_f64(100.0).unwrap(),
            volume: Volume::from_f64(1000.0).unwrap(),
            volatility: 0.02, // 2% - below threshold
            returns: 0.01,
        };

        let triggered = breaker.update_market_data(normal_data)?;
        assert!(triggered.is_empty());
        assert!(!breaker.is_any_triggered());

        // High volatility - should trigger
        let high_vol_data = MarketDataPoint {
            timestamp: Utc::now(),
            price: Price::from_f64(105.0).unwrap(),
            volume: Volume::from_f64(1000.0).unwrap(),
            volatility: 0.08, // 8% - above 5% threshold
            returns: 0.05,
        };

        let triggered = breaker.update_market_data(high_vol_data)?;
        assert!(!triggered.is_empty());
        assert!(triggered.contains(&CircuitBreakerType::Volatility));
        assert!(breaker.is_triggered(CircuitBreakerType::Volatility));
        Ok(())
    }

    #[test]
    fn test_model_performance_circuit_breaker() -> Result<()> {
        let config = CircuitBreakerConfig::default();
        let mut breaker = MLCircuitBreaker::new(config)?;

        // Good performance - should not trigger
        let good_triggered = breaker.update_model_performance(0.95)?;
        assert!(!good_triggered);
        assert!(!breaker.is_triggered(CircuitBreakerType::ModelPerformance));

        // Poor performance - should trigger
        let poor_triggered = breaker.update_model_performance(0.60)?; // Below 80% threshold
        assert!(poor_triggered);
        assert!(breaker.is_triggered(CircuitBreakerType::ModelPerformance));
        Ok(())
    }

    #[test]
    fn test_circuit_breaker_reset() -> Result<()> {
        let config = CircuitBreakerConfig::default();
        let mut breaker = MLCircuitBreaker::new(config)?;

        // Trigger a circuit breaker
        breaker.update_model_performance(0.60)?;
        assert!(breaker.is_triggered(CircuitBreakerType::ModelPerformance));

        // Manual reset
        breaker.reset_breaker(CircuitBreakerType::ModelPerformance)?;
        assert!(!breaker.is_triggered(CircuitBreakerType::ModelPerformance));
        Ok(())
    }

    #[test]
    fn test_market_stress_calculation() -> Result<()> {
        let config = CircuitBreakerConfig::default();
        let mut breaker = MLCircuitBreaker::new(config)?;

        // Add some historical data
        for i in 0..20 {
            let data = MarketDataPoint {
                timestamp: Utc::now(),
                price: Price::from_f64(100.0 + i as f64).unwrap(),
                volume: Volume::from_f64(1000.0).unwrap(),
                volatility: 0.02,
                returns: 0.01,
            };
            breaker.update_market_data(data)?;
        }

        let stress_score = breaker.calculate_market_stress();
        assert!(stress_score >= 0.0 && stress_score <= 1.0);
        Ok(())
    }
}
