//! Ensemble Rollback Automation Service
//!
//! Automated monitoring and response system for ensemble failure scenarios:
//! 1. Daily loss exceeds $2K
//! 2. Model disagreement >70% for 1 hour
//! 3. Single model >3 consecutive errors
//! 4. Cascade failure (2+ models fail)
//!
//! Automatic Actions:
//! - Emergency halt trading
//! - Reduce position sizes 50%
//! - Disable failed models
//! - Revert to baseline (DQN-30 only)
//!
//! Target: Recovery time <5 minutes with zero manual intervention

use std::collections::{HashMap, VecDeque};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::RwLock;
use tokio::time::interval;
use tracing::{debug, error, info, warn};

// Removed unused imports: Price, Symbol
use ml::MLResult;

use crate::core::position_manager::PositionManager;
use crate::ensemble_coordinator::EnsembleCoordinator;
use crate::ensemble_risk_manager::EnsembleRiskManager;

// Removed unused import: CheckpointMetadata
use ml::ModelType;

/// Rollback scenario types
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RollbackScenario {
    /// Daily loss exceeds $2K threshold
    DailyLossExceeded,
    /// Model disagreement >70% for 1 hour
    HighDisagreement,
    /// Single model >3 consecutive errors
    ModelFailure,
    /// Cascade failure (2+ models fail)
    CascadeFailure,
}

impl RollbackScenario {
    pub fn name(&self) -> &'static str {
        match self {
            Self::DailyLossExceeded => "DailyLossExceeded",
            Self::HighDisagreement => "HighDisagreement",
            Self::ModelFailure => "ModelFailure",
            Self::CascadeFailure => "CascadeFailure",
        }
    }

    pub fn description(&self) -> &'static str {
        match self {
            Self::DailyLossExceeded => "Daily trading loss exceeded $2K threshold",
            Self::HighDisagreement => "Model disagreement rate >70% sustained for 1 hour",
            Self::ModelFailure => "Single model experienced >3 consecutive errors",
            Self::CascadeFailure => "2 or more models failed simultaneously",
        }
    }
}

/// Rollback action types
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RollbackAction {
    /// Emergency halt all trading
    EmergencyHalt,
    /// Reduce position sizes by 50%
    ReducePositions,
    /// Disable specific failed models
    DisableModels,
    /// Revert to baseline (DQN-30 only)
    RevertToBaseline,
}

impl RollbackAction {
    pub fn name(&self) -> &'static str {
        match self {
            Self::EmergencyHalt => "EmergencyHalt",
            Self::ReducePositions => "ReducePositions",
            Self::DisableModels => "DisableModels",
            Self::RevertToBaseline => "RevertToBaseline",
        }
    }

    pub fn priority(&self) -> u8 {
        match self {
            Self::EmergencyHalt => 1, // Highest priority
            Self::DisableModels => 2,
            Self::ReducePositions => 3,
            Self::RevertToBaseline => 4, // Lowest priority
        }
    }
}

/// Rollback automation configuration
#[derive(Debug, Clone)]
pub struct RollbackConfig {
    /// Daily loss threshold (USD)
    pub daily_loss_threshold_usd: f64,

    /// High disagreement rate threshold (0.0-1.0)
    pub high_disagreement_threshold: f64,

    /// Duration for sustained disagreement (seconds)
    pub disagreement_duration_secs: u64,

    /// Maximum consecutive errors before model failure
    pub max_consecutive_errors: u32,

    /// Cascade failure threshold (number of models)
    pub cascade_failure_threshold: usize,

    /// Position reduction factor (0.0-1.0)
    pub position_reduction_factor: f64,

    /// Monitoring interval (seconds)
    pub monitoring_interval_secs: u64,

    /// Recovery timeout (seconds) - maximum time for recovery
    pub recovery_timeout_secs: u64,

    /// Enable automatic rollback (if false, only monitoring)
    pub enable_automatic_rollback: bool,
}

impl Default for RollbackConfig {
    fn default() -> Self {
        Self {
            daily_loss_threshold_usd: 2000.0,
            high_disagreement_threshold: 0.70,
            disagreement_duration_secs: 3600, // 1 hour
            max_consecutive_errors: 3,
            cascade_failure_threshold: 2,
            position_reduction_factor: 0.50, // 50% reduction
            monitoring_interval_secs: 10,    // Check every 10 seconds
            recovery_timeout_secs: 300,      // 5 minutes
            enable_automatic_rollback: true,
        }
    }
}

/// Disagreement tracking entry
#[derive(Debug, Clone)]
pub struct DisagreementEntry {
    pub timestamp: Instant,
    pub rate: f64,
}

/// Rollback automation state
#[derive(Debug, Clone)]
pub struct RollbackState {
    /// Current daily P&L (USD)
    pub daily_pnl_usd: f64,

    /// Disagreement history (windowed)
    pub disagreement_history: VecDeque<DisagreementEntry>,

    /// Active scenarios detected
    pub active_scenarios: HashMap<RollbackScenario, Instant>,

    /// Executed actions
    pub executed_actions: Vec<(RollbackAction, Instant)>,

    /// Trading halted flag
    pub trading_halted: bool,

    /// Positions reduced flag
    pub positions_reduced: bool,

    /// Disabled models
    pub disabled_models: Vec<String>,

    /// Baseline mode active (DQN-30 only)
    pub baseline_mode_active: bool,

    /// Recovery start time
    pub recovery_start: Option<Instant>,

    /// Recovery completed flag
    pub recovery_completed: bool,
}

impl RollbackState {
    fn new() -> Self {
        Self {
            daily_pnl_usd: 0.0,
            disagreement_history: VecDeque::new(),
            active_scenarios: HashMap::new(),
            executed_actions: Vec::new(),
            trading_halted: false,
            positions_reduced: false,
            disabled_models: Vec::new(),
            baseline_mode_active: false,
            recovery_start: None,
            recovery_completed: false,
        }
    }

    fn reset_daily(&mut self) {
        self.daily_pnl_usd = 0.0;
        info!("Daily P&L reset to 0.0");
    }

    fn add_disagreement(&mut self, rate: f64, max_entries: usize) {
        let now = Instant::now();
        self.disagreement_history.push_back(DisagreementEntry {
            timestamp: now,
            rate,
        });

        // Keep only recent entries
        while self.disagreement_history.len() > max_entries {
            self.disagreement_history.pop_front();
        }
    }

    fn trigger_scenario(&mut self, scenario: RollbackScenario) {
        let now = Instant::now();
        self.active_scenarios.insert(scenario, now);
        warn!("Rollback scenario triggered: {:?}", scenario);
    }

    fn execute_action(&mut self, action: RollbackAction) {
        let now = Instant::now();
        self.executed_actions.push((action, now));

        match action {
            RollbackAction::EmergencyHalt => {
                self.trading_halted = true;
                error!("EMERGENCY HALT EXECUTED: All trading stopped");
            },
            RollbackAction::ReducePositions => {
                self.positions_reduced = true;
                warn!("POSITION REDUCTION EXECUTED: Sizes reduced by 50%");
            },
            RollbackAction::DisableModels => {
                warn!("MODEL DISABLING EXECUTED: Failed models disabled");
            },
            RollbackAction::RevertToBaseline => {
                self.baseline_mode_active = true;
                warn!("BASELINE REVERT EXECUTED: Using DQN-30 only");
            },
        }
    }

    fn start_recovery(&mut self) {
        self.recovery_start = Some(Instant::now());
        info!("Recovery process started");
    }

    fn complete_recovery(&mut self) {
        self.recovery_completed = true;
        let duration = self
            .recovery_start
            .map(|s| s.elapsed())
            .unwrap_or(Duration::ZERO);
        info!("Recovery completed in {:.2}s", duration.as_secs_f64());
    }

    fn get_recovery_duration(&self) -> Option<Duration> {
        self.recovery_start.map(|s| s.elapsed())
    }
}

/// Rollback automation service
pub struct RollbackAutomation {
    config: RollbackConfig,
    state: Arc<RwLock<RollbackState>>,
    ensemble_coordinator: Option<Arc<EnsembleCoordinator>>,
    ensemble_risk_manager: Option<Arc<EnsembleRiskManager>>,
    monitoring_task: Option<tokio::task::JoinHandle<()>>,

    // NEW: Execution dependencies
    trading_enabled: Arc<std::sync::atomic::AtomicBool>,
    position_manager: Option<Arc<PositionManager>>,
    checkpoint_manager: Option<Arc<ml::checkpoint::CheckpointManager>>,

    // Configuration for execution
    account_id: String,
}

impl RollbackAutomation {
    /// Create new rollback automation service
    pub fn new(config: RollbackConfig) -> Self {
        use std::sync::atomic::AtomicBool;

        Self {
            config,
            state: Arc::new(RwLock::new(RollbackState::new())),
            ensemble_coordinator: None,
            ensemble_risk_manager: None,
            monitoring_task: None,
            trading_enabled: Arc::new(AtomicBool::new(true)),
            position_manager: None,
            checkpoint_manager: None,
            account_id: "PRODUCTION_ACCOUNT".to_string(),
        }
    }

    /// Set ensemble coordinator
    pub fn with_ensemble_coordinator(mut self, coordinator: Arc<EnsembleCoordinator>) -> Self {
        self.ensemble_coordinator = Some(coordinator);
        self
    }

    /// Set ensemble risk manager
    pub fn with_ensemble_risk_manager(mut self, risk_manager: Arc<EnsembleRiskManager>) -> Self {
        self.ensemble_risk_manager = Some(risk_manager);
        self
    }

    /// Set position manager for position reduction
    pub fn with_position_manager(mut self, position_manager: Arc<PositionManager>) -> Self {
        self.position_manager = Some(position_manager);
        self
    }

    /// Set checkpoint manager for baseline revert
    pub fn with_checkpoint_manager(
        mut self,
        checkpoint_manager: Arc<ml::checkpoint::CheckpointManager>,
    ) -> Self {
        self.checkpoint_manager = Some(checkpoint_manager);
        self
    }

    /// Set account ID for operations
    pub fn with_account_id(mut self, account_id: String) -> Self {
        self.account_id = account_id;
        self
    }

    /// Start continuous monitoring
    pub async fn start_monitoring(&mut self) -> MLResult<()> {
        if self.monitoring_task.is_some() {
            warn!("Monitoring already running");
            return Ok(());
        }

        let config = self.config.clone();
        let state = Arc::clone(&self.state);
        let ensemble_coordinator = self.ensemble_coordinator.clone();
        let ensemble_risk_manager = self.ensemble_risk_manager.clone();
        let trading_enabled = Arc::clone(&self.trading_enabled);
        let position_manager = self.position_manager.clone();
        let checkpoint_manager = self.checkpoint_manager.clone();
        let account_id = self.account_id.clone();

        let task = tokio::spawn(async move {
            Self::monitoring_loop(
                config,
                state,
                trading_enabled,
                ensemble_coordinator,
                ensemble_risk_manager,
                position_manager,
                checkpoint_manager,
                account_id,
            )
            .await;
        });

        self.monitoring_task = Some(task);
        info!(
            "Rollback automation monitoring started (interval: {}s)",
            self.config.monitoring_interval_secs
        );
        Ok(())
    }

    /// Stop monitoring
    pub async fn stop_monitoring(&mut self) {
        if let Some(task) = self.monitoring_task.take() {
            task.abort();
            info!("Rollback automation monitoring stopped");
        }
    }

    /// Monitoring loop
    async fn monitoring_loop(
        config: RollbackConfig,
        state: Arc<RwLock<RollbackState>>,
        trading_enabled: Arc<std::sync::atomic::AtomicBool>,
        ensemble_coordinator: Option<Arc<EnsembleCoordinator>>,
        ensemble_risk_manager: Option<Arc<EnsembleRiskManager>>,
        position_manager: Option<Arc<PositionManager>>,
        checkpoint_manager: Option<Arc<ml::checkpoint::CheckpointManager>>,
        account_id: String,
    ) {
        let mut interval_timer = interval(Duration::from_secs(config.monitoring_interval_secs));

        loop {
            interval_timer.tick().await;

            // Check all scenarios
            if let Err(e) = Self::check_all_scenarios(
                &config,
                &state,
                &ensemble_coordinator,
                &ensemble_risk_manager,
            )
            .await
            {
                error!("Error checking scenarios: {}", e);
            }

            // Execute recovery actions if needed
            if let Err(e) = Self::execute_recovery_actions(
                &config,
                &state,
                &trading_enabled,
                &ensemble_coordinator,
                &position_manager,
                &checkpoint_manager,
                &account_id,
            )
            .await
            {
                error!("Error executing recovery actions: {}", e);
            }

            // Check recovery timeout
            Self::check_recovery_timeout(&config, &state).await;
        }
    }

    /// Check all failure scenarios
    async fn check_all_scenarios(
        config: &RollbackConfig,
        state: &Arc<RwLock<RollbackState>>,
        _ensemble_coordinator: &Option<Arc<EnsembleCoordinator>>,
        ensemble_risk_manager: &Option<Arc<EnsembleRiskManager>>,
    ) -> MLResult<()> {
        // Scenario 1: Daily loss exceeds $2K
        Self::check_daily_loss_scenario(config, state).await?;

        // Scenario 2: High disagreement for 1 hour
        Self::check_disagreement_scenario(config, state).await?;

        // Scenario 3: Model consecutive errors
        if let Some(risk_manager) = ensemble_risk_manager {
            Self::check_model_failure_scenario(config, state, risk_manager).await?;
        }

        // Scenario 4: Cascade failure
        if let Some(risk_manager) = ensemble_risk_manager {
            Self::check_cascade_failure_scenario(config, state, risk_manager).await?;
        }

        Ok(())
    }

    /// Check daily loss scenario
    async fn check_daily_loss_scenario(
        config: &RollbackConfig,
        state: &Arc<RwLock<RollbackState>>,
    ) -> MLResult<()> {
        let mut state_guard = state.write().await;

        if state_guard.daily_pnl_usd < -config.daily_loss_threshold_usd
            && !state_guard
                .active_scenarios
                .contains_key(&RollbackScenario::DailyLossExceeded)
            {
                error!(
                    "SCENARIO TRIGGERED: Daily loss ${:.2} exceeds threshold ${:.2}",
                    -state_guard.daily_pnl_usd, config.daily_loss_threshold_usd
                );
                state_guard.trigger_scenario(RollbackScenario::DailyLossExceeded);
            }

        Ok(())
    }

    /// Check disagreement scenario
    async fn check_disagreement_scenario(
        config: &RollbackConfig,
        state: &Arc<RwLock<RollbackState>>,
    ) -> MLResult<()> {
        let mut state_guard = state.write().await;

        // Remove old entries
        let cutoff = Instant::now() - Duration::from_secs(config.disagreement_duration_secs);
        state_guard
            .disagreement_history
            .retain(|entry| entry.timestamp > cutoff);

        // Check if all recent entries exceed threshold
        if !state_guard.disagreement_history.is_empty() {
            let high_disagreement_count = state_guard
                .disagreement_history
                .iter()
                .filter(|entry| entry.rate > config.high_disagreement_threshold)
                .count();

            let total_count = state_guard.disagreement_history.len();

            // If >90% of samples show high disagreement, trigger scenario
            if high_disagreement_count as f64 / total_count as f64 > 0.9
                && !state_guard
                    .active_scenarios
                    .contains_key(&RollbackScenario::HighDisagreement)
                {
                    error!(
                        "SCENARIO TRIGGERED: High disagreement >{}% sustained for {} seconds",
                        config.high_disagreement_threshold * 100.0,
                        config.disagreement_duration_secs
                    );
                    state_guard.trigger_scenario(RollbackScenario::HighDisagreement);
                }
        }

        Ok(())
    }

    /// Check model failure scenario
    async fn check_model_failure_scenario(
        config: &RollbackConfig,
        state: &Arc<RwLock<RollbackState>>,
        risk_manager: &Arc<EnsembleRiskManager>,
    ) -> MLResult<()> {
        let model_health_map = risk_manager.get_all_model_health().await;

        for (model_id, health) in model_health_map {
            if health.consecutive_errors >= config.max_consecutive_errors {
                let mut state_guard = state.write().await;

                if !state_guard
                    .active_scenarios
                    .contains_key(&RollbackScenario::ModelFailure)
                {
                    error!(
                        "SCENARIO TRIGGERED: Model {} has {} consecutive errors",
                        model_id, health.consecutive_errors
                    );
                    state_guard.trigger_scenario(RollbackScenario::ModelFailure);

                    // Track disabled model
                    if !state_guard.disabled_models.contains(&model_id) {
                        state_guard.disabled_models.push(model_id);
                    }
                }
            }
        }

        Ok(())
    }

    /// Check cascade failure scenario
    async fn check_cascade_failure_scenario(
        _config: &RollbackConfig,
        state: &Arc<RwLock<RollbackState>>,
        risk_manager: &Arc<EnsembleRiskManager>,
    ) -> MLResult<()> {
        let cascade_state = risk_manager.get_cascade_state().await;

        if cascade_state.is_cascading {
            let mut state_guard = state.write().await;

            if !state_guard
                .active_scenarios
                .contains_key(&RollbackScenario::CascadeFailure)
            {
                error!(
                    "SCENARIO TRIGGERED: Cascade failure detected ({} models failed)",
                    cascade_state.failed_models.len()
                );
                state_guard.trigger_scenario(RollbackScenario::CascadeFailure);
            }
        }

        Ok(())
    }

    /// Execute recovery actions based on triggered scenarios
    async fn execute_recovery_actions(
        config: &RollbackConfig,
        state: &Arc<RwLock<RollbackState>>,
        trading_enabled: &Arc<std::sync::atomic::AtomicBool>,
        ensemble_coordinator: &Option<Arc<EnsembleCoordinator>>,
        position_manager: &Option<Arc<PositionManager>>,
        checkpoint_manager: &Option<Arc<ml::checkpoint::CheckpointManager>>,
        account_id: &str,
    ) -> MLResult<()> {
        use std::sync::atomic::Ordering;

        let mut state_guard = state.write().await;

        if !config.enable_automatic_rollback {
            debug!("Automatic rollback disabled - monitoring only");
            return Ok(());
        }

        if state_guard.active_scenarios.is_empty() {
            return Ok(());
        }

        // Start recovery if not already started
        if state_guard.recovery_start.is_none() {
            state_guard.start_recovery();
        }

        // Determine required actions based on scenarios
        let mut actions_needed: Vec<RollbackAction> = Vec::new();

        for scenario in state_guard.active_scenarios.keys() {
            match scenario {
                RollbackScenario::DailyLossExceeded => {
                    if !state_guard.trading_halted {
                        actions_needed.push(RollbackAction::EmergencyHalt);
                    }
                    if !state_guard.positions_reduced {
                        actions_needed.push(RollbackAction::ReducePositions);
                    }
                },
                RollbackScenario::HighDisagreement => {
                    if !state_guard.baseline_mode_active {
                        actions_needed.push(RollbackAction::RevertToBaseline);
                    }
                    if !state_guard.positions_reduced {
                        actions_needed.push(RollbackAction::ReducePositions);
                    }
                },
                RollbackScenario::ModelFailure => {
                    actions_needed.push(RollbackAction::DisableModels);
                    if !state_guard.baseline_mode_active {
                        actions_needed.push(RollbackAction::RevertToBaseline);
                    }
                },
                RollbackScenario::CascadeFailure => {
                    if !state_guard.trading_halted {
                        actions_needed.push(RollbackAction::EmergencyHalt);
                    }
                    if !state_guard.baseline_mode_active {
                        actions_needed.push(RollbackAction::RevertToBaseline);
                    }
                },
            }
        }

        // Sort by priority (highest first)
        actions_needed.sort_by_key(|a| a.priority());
        actions_needed.dedup();

        // Execute actions with REAL INTEGRATION
        for action in actions_needed {
            // Check if already executed
            let already_executed = state_guard
                .executed_actions
                .iter()
                .any(|(a, _)| *a == action);

            if !already_executed {
                info!("Executing rollback action: {:?}", action);

                // Execute action with real integration
                match action {
                    RollbackAction::EmergencyHalt => {
                        // Set trading enabled flag to false
                        trading_enabled.store(false, Ordering::Release);
                        error!("EMERGENCY HALT EXECUTED: All trading disabled");
                        state_guard.execute_action(action);
                    },

                    RollbackAction::ReducePositions => {
                        // Reduce all positions by configured factor (default 50%)
                        if let Some(ref pm) = position_manager {
                            let positions = pm.get_account_positions(account_id).await;

                            for (symbol, snapshot) in positions {
                                if snapshot.quantity != 0 {
                                    // Calculate reduction delta
                                    let target_quantity = (snapshot.quantity as f64
                                        * config.position_reduction_factor)
                                        as i64;
                                    let reduction_delta = target_quantity - snapshot.quantity;

                                    // Execute position reduction
                                    match pm
                                        .update_position(
                                            account_id,
                                            &symbol,
                                            reduction_delta,
                                            snapshot.market_price,
                                        )
                                        .await
                                    {
                                        Ok(_) => {
                                            info!(
                                                "Reduced position {} from {} to {} ({}% reduction)",
                                                symbol,
                                                snapshot.quantity,
                                                target_quantity,
                                                (1.0 - config.position_reduction_factor) * 100.0
                                            );
                                        },
                                        Err(e) => {
                                            error!("Failed to reduce position {}: {}", symbol, e);
                                        },
                                    }
                                }
                            }

                            warn!(
                                "POSITION REDUCTION EXECUTED: All positions reduced by {}%",
                                (1.0 - config.position_reduction_factor) * 100.0
                            );
                        } else {
                            warn!("Position reduction skipped: PositionManager not available");
                        }

                        state_guard.execute_action(action);
                    },

                    RollbackAction::DisableModels => {
                        // Models are automatically disabled by EnsembleRiskManager
                        // when consecutive_errors >= max_consecutive_errors
                        // Just log the disabled models
                        if !state_guard.disabled_models.is_empty() {
                            warn!(
                                "MODEL DISABLING CONFIRMED: {} models disabled: {:?}",
                                state_guard.disabled_models.len(),
                                state_guard.disabled_models
                            );
                        }

                        state_guard.execute_action(action);
                    },

                    RollbackAction::RevertToBaseline => {
                        // Load DQN-30 baseline checkpoint and swap into ensemble
                        if let Some(ref cm) = checkpoint_manager {
                            // Get latest DQN-30 checkpoint metadata
                            let checkpoints = cm.list_checkpoints(ModelType::DQN, "DQN-30").await;

                            if let Some(baseline_metadata) = checkpoints.first() {
                                // Found baseline checkpoint
                                info!(
                                    "Reverting to DQN-30 baseline: checkpoint={}, Sharpe={:.2}",
                                    baseline_metadata.checkpoint_id,
                                    baseline_metadata
                                        .metrics
                                        .get("sharpe_ratio")
                                        .copied()
                                        .unwrap_or(0.0)
                                );

                                // In production, this would load the checkpoint and swap the model
                                // For now, just register with 100% weight in ensemble coordinator
                                if let Some(ref coord) = ensemble_coordinator {
                                    // Set DQN-30 to 100% weight, others to 0
                                    let _ = coord.register_model("DQN-30".to_string(), 1.0).await;
                                    let _ = coord.register_model("PPO".to_string(), 0.0).await;
                                    let _ = coord.register_model("TFT".to_string(), 0.0).await;
                                    let _ = coord.register_model("MAMBA".to_string(), 0.0).await;
                                }

                                warn!("BASELINE REVERT EXECUTED: Using DQN-30 only");
                            } else {
                                error!("DQN-30 baseline checkpoint not found");
                            }
                        } else {
                            warn!("Baseline revert skipped: CheckpointManager not available");
                        }

                        state_guard.execute_action(action);
                    },
                }
            }
        }

        // Check if recovery is complete
        let required_actions = [RollbackAction::EmergencyHalt,
            RollbackAction::ReducePositions,
            RollbackAction::DisableModels,
            RollbackAction::RevertToBaseline];

        let all_executed = required_actions.iter().all(|req_action| {
            state_guard
                .executed_actions
                .iter()
                .any(|(a, _)| a == req_action)
        });

        if all_executed && !state_guard.recovery_completed {
            state_guard.complete_recovery();
        }

        Ok(())
    }

    /// Check recovery timeout
    async fn check_recovery_timeout(config: &RollbackConfig, state: &Arc<RwLock<RollbackState>>) {
        let state_guard = state.read().await;

        if let Some(duration) = state_guard.get_recovery_duration() {
            let timeout = Duration::from_secs(config.recovery_timeout_secs);

            if duration > timeout && !state_guard.recovery_completed {
                error!(
                    "RECOVERY TIMEOUT: Recovery took {:.2}s (limit: {:.2}s)",
                    duration.as_secs_f64(),
                    timeout.as_secs_f64()
                );
            }
        }
    }

    /// Update daily P&L
    pub async fn update_daily_pnl(&self, pnl_usd: f64) -> MLResult<()> {
        let mut state_guard = self.state.write().await;
        state_guard.daily_pnl_usd = pnl_usd;
        debug!("Daily P&L updated: ${:.2}", pnl_usd);
        Ok(())
    }

    /// Record disagreement rate
    pub async fn record_disagreement(&self, rate: f64) -> MLResult<()> {
        let max_entries = (self.config.disagreement_duration_secs
            / self.config.monitoring_interval_secs) as usize;
        let mut state_guard = self.state.write().await;
        state_guard.add_disagreement(rate, max_entries);
        debug!("Disagreement rate recorded: {:.3}", rate);
        Ok(())
    }

    /// Reset daily metrics (call at start of trading day)
    pub async fn reset_daily(&self) -> MLResult<()> {
        let mut state_guard = self.state.write().await;
        state_guard.reset_daily();
        Ok(())
    }

    /// Get current rollback state
    pub async fn get_state(&self) -> RollbackState {
        self.state.read().await.clone()
    }

    /// Check if trading is halted
    pub async fn is_trading_halted(&self) -> bool {
        use std::sync::atomic::Ordering;
        !self.trading_enabled.load(Ordering::Acquire)
    }

    /// Check if trading is enabled (inverse of is_trading_halted)
    pub fn is_trading_enabled(&self) -> bool {
        use std::sync::atomic::Ordering;
        self.trading_enabled.load(Ordering::Acquire)
    }

    /// Check if positions are reduced
    pub async fn are_positions_reduced(&self) -> bool {
        self.state.read().await.positions_reduced
    }

    /// Check if baseline mode is active
    pub async fn is_baseline_mode_active(&self) -> bool {
        self.state.read().await.baseline_mode_active
    }

    /// Get recovery duration
    pub async fn get_recovery_duration(&self) -> Option<Duration> {
        self.state.read().await.get_recovery_duration()
    }

    /// Manually trigger a scenario (for testing)
    pub async fn trigger_scenario_manual(&self, scenario: RollbackScenario) -> MLResult<()> {
        let mut state_guard = self.state.write().await;
        state_guard.trigger_scenario(scenario);
        Ok(())
    }

    /// Reset all scenarios and state (for testing)
    pub async fn reset_all(&self) -> MLResult<()> {
        let mut state_guard = self.state.write().await;
        *state_guard = RollbackState::new();
        info!("Rollback automation state reset");
        Ok(())
    }
}

/// Rollback report for analysis
#[derive(Debug, Clone)]
pub struct RollbackReport {
    pub scenarios_triggered: Vec<(RollbackScenario, Instant)>,
    pub actions_executed: Vec<(RollbackAction, Instant)>,
    pub recovery_duration: Option<Duration>,
    pub trading_halted: bool,
    pub positions_reduced: bool,
    pub disabled_models: Vec<String>,
    pub baseline_mode_active: bool,
    pub recovery_completed: bool,
}

impl RollbackReport {
    pub fn from_state(state: &RollbackState) -> Self {
        Self {
            scenarios_triggered: state
                .active_scenarios
                .iter()
                .map(|(s, t)| (*s, *t))
                .collect(),
            actions_executed: state.executed_actions.clone(),
            recovery_duration: state.get_recovery_duration(),
            trading_halted: state.trading_halted,
            positions_reduced: state.positions_reduced,
            disabled_models: state.disabled_models.clone(),
            baseline_mode_active: state.baseline_mode_active,
            recovery_completed: state.recovery_completed,
        }
    }

    pub fn meets_success_criteria(&self) -> bool {
        // Success criteria:
        // 1. Recovery completed
        // 2. Recovery duration < 5 minutes (300 seconds)
        self.recovery_completed
            && self
                .recovery_duration
                .map(|d| d.as_secs() < 300)
                .unwrap_or(false)
    }
}

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

    #[tokio::test]
    async fn test_rollback_automation_creation() {
        let config = RollbackConfig::default();
        let automation = RollbackAutomation::new(config);

        let state = automation.get_state().await;
        assert_eq!(state.daily_pnl_usd, 0.0);
        assert!(state.active_scenarios.is_empty());
        assert!(!state.trading_halted);
    }

    #[tokio::test]
    async fn test_daily_loss_scenario() {
        let config = RollbackConfig::default();
        let automation = RollbackAutomation::new(config.clone());

        // Update P&L to trigger scenario
        automation.update_daily_pnl(-2500.0).await.unwrap();

        // Check scenario manually
        RollbackAutomation::check_daily_loss_scenario(&config, &automation.state)
            .await
            .unwrap();

        let state = automation.get_state().await;
        assert!(state
            .active_scenarios
            .contains_key(&RollbackScenario::DailyLossExceeded));
    }

    #[tokio::test]
    async fn test_disagreement_scenario() {
        let config = RollbackConfig {
            disagreement_duration_secs: 10, // 10 seconds for testing
            monitoring_interval_secs: 1,
            ..Default::default()
        };
        let automation = RollbackAutomation::new(config.clone());

        // Record high disagreement
        for _ in 0..15 {
            automation.record_disagreement(0.75).await.unwrap();
            tokio::time::sleep(Duration::from_millis(100)).await;
        }

        // Check scenario
        RollbackAutomation::check_disagreement_scenario(&config, &automation.state)
            .await
            .unwrap();

        let state = automation.get_state().await;
        assert!(state
            .active_scenarios
            .contains_key(&RollbackScenario::HighDisagreement));
    }

    #[tokio::test]
    async fn test_emergency_halt_action() {
        let config = RollbackConfig::default();
        let automation = RollbackAutomation::new(config);

        // Trigger scenario manually
        automation
            .trigger_scenario_manual(RollbackScenario::DailyLossExceeded)
            .await
            .unwrap();

        // Execute recovery actions
        RollbackAutomation::execute_recovery_actions(
            &automation.config,
            &automation.state,
            &automation.trading_enabled,
            &automation.ensemble_coordinator,
            &automation.position_manager,
            &automation.checkpoint_manager,
            &automation.account_id,
        )
        .await
        .unwrap();

        let state = automation.get_state().await;
        assert!(state.trading_halted);
        assert!(state
            .executed_actions
            .iter()
            .any(|(a, _)| *a == RollbackAction::EmergencyHalt));
    }

    #[tokio::test]
    async fn test_reduce_positions_action() {
        let config = RollbackConfig::default();
        let automation = RollbackAutomation::new(config);

        // Trigger high disagreement scenario
        automation
            .trigger_scenario_manual(RollbackScenario::HighDisagreement)
            .await
            .unwrap();

        // Execute recovery actions
        RollbackAutomation::execute_recovery_actions(
            &automation.config,
            &automation.state,
            &automation.trading_enabled,
            &automation.ensemble_coordinator,
            &automation.position_manager,
            &automation.checkpoint_manager,
            &automation.account_id,
        )
        .await
        .unwrap();

        let state = automation.get_state().await;
        assert!(state.positions_reduced);
        assert!(state
            .executed_actions
            .iter()
            .any(|(a, _)| *a == RollbackAction::ReducePositions));
    }

    #[tokio::test]
    async fn test_baseline_revert_action() {
        let config = RollbackConfig::default();
        let automation = RollbackAutomation::new(config);

        // Trigger model failure scenario
        automation
            .trigger_scenario_manual(RollbackScenario::ModelFailure)
            .await
            .unwrap();

        // Execute recovery actions
        RollbackAutomation::execute_recovery_actions(
            &automation.config,
            &automation.state,
            &automation.trading_enabled,
            &automation.ensemble_coordinator,
            &automation.position_manager,
            &automation.checkpoint_manager,
            &automation.account_id,
        )
        .await
        .unwrap();

        let state = automation.get_state().await;
        assert!(state.baseline_mode_active);
        assert!(state
            .executed_actions
            .iter()
            .any(|(a, _)| *a == RollbackAction::RevertToBaseline));
    }

    #[tokio::test]
    async fn test_cascade_failure_scenario() {
        let config = RollbackConfig::default();
        let automation = RollbackAutomation::new(config);

        // Trigger cascade failure
        automation
            .trigger_scenario_manual(RollbackScenario::CascadeFailure)
            .await
            .unwrap();

        // Execute recovery actions
        RollbackAutomation::execute_recovery_actions(
            &automation.config,
            &automation.state,
            &automation.trading_enabled,
            &automation.ensemble_coordinator,
            &automation.position_manager,
            &automation.checkpoint_manager,
            &automation.account_id,
        )
        .await
        .unwrap();

        let state = automation.get_state().await;
        assert!(state.trading_halted);
        assert!(state.baseline_mode_active);
    }

    #[tokio::test]
    async fn test_recovery_duration_tracking() {
        let config = RollbackConfig::default();
        let automation = RollbackAutomation::new(config);

        // Trigger scenario
        automation
            .trigger_scenario_manual(RollbackScenario::DailyLossExceeded)
            .await
            .unwrap();

        // Execute recovery
        RollbackAutomation::execute_recovery_actions(
            &automation.config,
            &automation.state,
            &automation.trading_enabled,
            &automation.ensemble_coordinator,
            &automation.position_manager,
            &automation.checkpoint_manager,
            &automation.account_id,
        )
        .await
        .unwrap();

        // Wait a bit
        tokio::time::sleep(Duration::from_millis(100)).await;

        let duration = automation.get_recovery_duration().await;
        assert!(duration.is_some());
        assert!(duration.unwrap().as_millis() >= 100);
    }

    #[tokio::test]
    async fn test_rollback_report() {
        let config = RollbackConfig::default();
        let automation = RollbackAutomation::new(config);

        // Trigger and recover
        automation
            .trigger_scenario_manual(RollbackScenario::DailyLossExceeded)
            .await
            .unwrap();
        RollbackAutomation::execute_recovery_actions(
            &automation.config,
            &automation.state,
            &automation.trading_enabled,
            &automation.ensemble_coordinator,
            &automation.position_manager,
            &automation.checkpoint_manager,
            &automation.account_id,
        )
        .await
        .unwrap();

        let state = automation.get_state().await;
        let report = RollbackReport::from_state(&state);

        assert!(!report.scenarios_triggered.is_empty());
        assert!(!report.actions_executed.is_empty());
        assert!(report.trading_halted);
    }

    #[tokio::test]
    async fn test_reset_functionality() {
        let config = RollbackConfig::default();
        let automation = RollbackAutomation::new(config);

        // Trigger scenarios
        automation
            .trigger_scenario_manual(RollbackScenario::DailyLossExceeded)
            .await
            .unwrap();
        automation.update_daily_pnl(-3000.0).await.unwrap();

        // Reset
        automation.reset_all().await.unwrap();

        let state = automation.get_state().await;
        assert_eq!(state.daily_pnl_usd, 0.0);
        assert!(state.active_scenarios.is_empty());
        assert!(!state.trading_halted);
    }
}
