//! Safety Coordinator - Integration Hub for All Safety Systems
//!
//! Coordinates and orchestrates all safety mechanisms across the trading system:
//! - Kill switches with atomic broadcasting
//! - Position limits with hybrid checking  
//! - Circuit breakers for market anomalies
//! - Emergency response coordination
//! - Real-time safety monitoring and alerts
//! - Integration with trading-engine and ai-intelligence services

use std::collections::HashMap;
use std::sync::Arc;
// Removed foxhunt_infrastructure - not available in this simplified risk crate

use chrono::{DateTime, Utc};
// REMOVED: Direct Decimal usage - use canonical types
use common::Price;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use tokio::sync::{broadcast, RwLock};
use tracing::{debug, error, info, warn};

use crate::circuit_breaker::RealCircuitBreaker;
use crate::error::{RiskError, RiskResult};
use crate::safety::emergency_response::EmergencyResponseSystem;
use crate::safety::kill_switch::AtomicKillSwitch;
use crate::safety::SafetyConfig;

/// System Health Report
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemHealthReport {
    pub component_status: HashMap<String, String>,
    pub overall_health: f64,
    pub last_updated: DateTime<Utc>,
}

/// Safety Coordinator - Central hub for all safety systems
pub struct SafetyCoordinator {
    kill_switch: Arc<AtomicKillSwitch>,
    circuit_breaker: Arc<RealCircuitBreaker>,
    emergency_response: Arc<EmergencyResponseSystem>,
    event_tx: broadcast::Sender<String>,
    is_running: Arc<RwLock<bool>>,
}

impl SafetyCoordinator {
    /// Create a new `SafetyCoordinator`
    pub async fn new(config: SafetyConfig, redis_url: String) -> RiskResult<Self> {
        let (event_tx, _) = broadcast::channel(1000);

        // Create safety components with proper implementations
        let kill_switch_config = config.kill_switch.clone();
        let kill_switch = AtomicKillSwitch::new(kill_switch_config, redis_url.clone()).await?;

        // Create circuit breaker with proper configuration
        // For now, create a real broker client for local development using the circuit_breaker module's RealBrokerClient
        let _broker_service = Arc::new(crate::circuit_breaker::RealBrokerClient::new({
            let host = std::env::var("SERVICE_HOST")
                .unwrap_or_else(|_| "localhost".to_owned());
            format!("http://{}:8080", host)
        }));
        let circuit_breaker_config = crate::circuit_breaker::CircuitBreakerConfig {
            enabled: true,
            daily_loss_percentage: Price::from_f64(2.0).unwrap_or(Decimal::from(2).into()), // Default 2% daily loss limit
            position_limit_percentage: Price::from_f64(5.0).unwrap_or(Decimal::from(5).into()), // Default 5% position limit
            max_consecutive_violations: 3,
            redis_url: redis_url.clone(),
            redis_key_prefix: "foxhunt:risk:circuit_breaker".to_owned(),
            auto_recovery_enabled: true,
            portfolio_refresh_interval_secs: 60,
            cooldown_period_secs: 300,
        };
        let adapter = Arc::new(crate::risk_engine::BrokerAccountServiceAdapter::new());
        let circuit_breaker = RealCircuitBreaker::new(circuit_breaker_config, adapter)
            .await
            .map_err(|e| RiskError::Config(format!("Failed to initialize circuit breaker: {e}")))?;

        // Create emergency response system
        let kill_switch_arc = Arc::new(kill_switch);
        let emergency_response = EmergencyResponseSystem::new(
            config.emergency_response.clone(),
            redis_url.clone(),
            kill_switch_arc.clone(),
        )
        .await?;

        Ok(Self {
            kill_switch: kill_switch_arc,
            circuit_breaker: Arc::new(circuit_breaker),
            emergency_response: Arc::new(emergency_response),
            event_tx,
            is_running: Arc::new(RwLock::new(false)),
        })
    }

    /// Create a new `SafetyCoordinator` for testing without Redis
    #[cfg(test)]
    #[allow(clippy::missing_panics_doc)]
    pub async fn new_test(config: SafetyConfig) -> RiskResult<Self> {
        let (event_tx, _) = broadcast::channel(1000);

        // Create test kill switch without Redis
        let kill_switch_config = config.kill_switch.clone();
        let kill_switch_arc = Arc::new(AtomicKillSwitch::new_test(kill_switch_config));

        // Create test position limiter
        // Create test circuit breaker using mock adapter
        let circuit_breaker_config = crate::circuit_breaker::CircuitBreakerConfig {
            enabled: true,
            daily_loss_percentage: Price::from_f64(2.0).unwrap_or(Decimal::from(2).into()),
            position_limit_percentage: Price::from_f64(5.0).unwrap_or(Decimal::from(5).into()),
            max_consecutive_violations: 3,
            redis_url: "redis://localhost:6379".to_owned(),
            redis_key_prefix: "foxhunt:risk:circuit_breaker:test".to_owned(),
            auto_recovery_enabled: false, // Disable for tests
            portfolio_refresh_interval_secs: 60,
            cooldown_period_secs: 300,
        };
        let adapter = Arc::new(crate::risk_engine::BrokerAccountServiceAdapter::new());
        // Use real circuit breaker but it won't try to connect to Redis if auto_recovery is disabled
        let circuit_breaker = RealCircuitBreaker::new(circuit_breaker_config, adapter)
            .await
            .unwrap_or_else(|_| {
                // Fallback: create a minimal circuit breaker for tests
                panic!("Circuit breaker creation failed in test - this should not happen")
            });

        // Create test emergency response system
        let emergency_response = EmergencyResponseSystem::new(
            config.emergency_response.clone(),
            "redis://localhost:6379".to_owned(),
            kill_switch_arc.clone(),
        )
        .await?;

        Ok(Self {
            kill_switch: kill_switch_arc,
            circuit_breaker: Arc::new(circuit_breaker),
            emergency_response: Arc::new(emergency_response),
            event_tx,
            is_running: Arc::new(RwLock::new(false)),
        })
    }

    /// Start all safety systems
    pub async fn start_all_systems(&self) -> RiskResult<()> {
        info!("Starting all safety systems");

        // Start kill switch monitoring
        self.kill_switch.start_monitoring().await?;

        // Start emergency response system
        self.emergency_response.start_monitoring().await?;

        // Circuit breaker is stateless and always active

        let mut running = self.is_running.write().await;
        *running = true;

        info!("All safety systems started successfully");
        Ok(())
    }

    /// Stop all safety systems
    pub async fn stop_all_systems(&self) {
        info!("Stopping all safety systems");

        // Stop kill switch monitoring
        if let Err(e) = self.kill_switch.stop_monitoring().await {
            warn!("Error stopping kill switch: {}", e);
        }

        // Stop emergency response system
        if let Err(e) = self.emergency_response.stop_monitoring().await {
            warn!("Error stopping emergency response: {}", e);
        }

        let mut running = self.is_running.write().await;
        *running = false;

        info!("All safety systems stopped");
    }

    /// Check if trading is allowed for the given account and symbol
    pub async fn is_trading_allowed(&self, account_id: &str, symbol: &str) -> bool {
        let running = self.is_running.read().await;
        if !*running {
            return false;
        }

        // Check kill switch status
        if let Ok(is_active) = self.kill_switch.is_active().await {
            if is_active {
                debug!(
                    "Trading blocked by kill switch for {}:{}",
                    account_id, symbol
                );
                return false;
            }
        }

        // Check circuit breaker status
        if self.circuit_breaker.is_active(account_id).await {
            debug!(
                "Trading blocked by circuit breaker for account {}",
                account_id
            );
            return false;
        }

        true
    }

    /// Trigger global emergency halt
    pub async fn global_emergency_halt(&self, reason: String, user: String) -> RiskResult<()> {
        warn!("Global emergency halt triggered by {}: {}", user, reason);

        // Activate kill switch
        self.kill_switch
            .activate_global(reason.clone(), user.clone())
            .await?;

        // Trigger emergency response
        self.emergency_response
            .handle_manual_emergency(user.clone(), reason.clone())
            .await?;

        let mut running = self.is_running.write().await;
        *running = false;

        // Broadcast emergency event
        let _ = self
            .event_tx
            .send(format!("EMERGENCY_HALT: {reason} by {user}"));

        error!("Global emergency halt activated");
        Ok(())
    }

    /// Get system health report
    pub async fn get_system_health(&self) -> SystemHealthReport {
        let mut component_status = HashMap::new();
        let mut health_scores = Vec::new();

        // Check kill switch health
        match self.kill_switch.is_healthy().await {
            Ok(true) => {
                component_status.insert("kill_switch".to_owned(), "operational".to_owned());
                health_scores.push(1.0);
            },
            Ok(false) => {
                component_status.insert("kill_switch".to_owned(), "degraded".to_owned());
                health_scores.push(0.5);
            },
            Err(_) => {
                component_status.insert("kill_switch".to_owned(), "failed".to_owned());
                health_scores.push(0.0);
            },
        }

        // Position limiter is always healthy if initialized
        component_status.insert("position_limiter".to_owned(), "operational".to_owned());
        health_scores.push(1.0);

        // Check circuit breaker health (simplified check)
        component_status.insert("circuit_breaker".to_owned(), "operational".to_owned());
        health_scores.push(1.0);

        // Check emergency response health
        if self.emergency_response.is_healthy().await {
            component_status.insert("emergency_response".to_owned(), "operational".to_owned());
            health_scores.push(1.0);
        } else {
            component_status.insert("emergency_response".to_owned(), "degraded".to_owned());
            health_scores.push(0.5);
        }

        // Calculate overall health
        let overall_health = if health_scores.is_empty() {
            0.0
        } else {
            health_scores.iter().sum::<f64>() / health_scores.len() as f64
        };

        SystemHealthReport {
            component_status,
            overall_health,
            last_updated: Utc::now(),
        }
    }

    /// Subscribe to safety events
    #[must_use]
    pub fn subscribe_safety_events(&self) -> broadcast::Receiver<String> {
        self.event_tx.subscribe()
    }
}

#[cfg(test)]
#[allow(clippy::assertions_on_result_states, clippy::str_to_string)]
mod tests {
    use super::*;
    use crate::safety::{EmergencyResponseConfig, KillSwitchConfig, PositionLimiterConfig};
    use std::time::Duration;
    // operations module removed - use direct imports from common

    fn create_test_config() -> SafetyConfig {
        SafetyConfig {
            enabled: true,
            kill_switch: KillSwitchConfig::default(),
            position_limits: PositionLimiterConfig::default(),
            emergency_response: EmergencyResponseConfig::default(),
            redis_url: std::env::var("REDIS_URL").unwrap_or_else(|_| {
                let host = std::env::var("REDIS_HOST")
                    .unwrap_or_else(|_| "localhost".to_string());
                format!("redis://{}:6379", host)
            }),
            safety_check_timeout: Duration::from_millis(10),
        }
    }

    async fn create_test_coordinator() -> RiskResult<SafetyCoordinator> {
        let config = create_test_config();
        SafetyCoordinator::new_test(config).await
    }

    #[tokio::test]
    async fn test_safety_coordinator_creation() {
        let coordinator = create_test_coordinator().await;
        assert!(coordinator.is_ok());
    }

    #[tokio::test]
    async fn test_trading_allowed_check() -> RiskResult<()> {
        let coordinator = create_test_coordinator().await?;

        // Start systems first
        coordinator.start_all_systems().await?;

        // Trading should be allowed when systems are running
        assert!(coordinator.is_trading_allowed("account1", "AAPL").await);

        coordinator.stop_all_systems().await;
        Ok(())
    }

    #[tokio::test]
    async fn test_global_emergency_halt() -> RiskResult<()> {
        let coordinator = create_test_coordinator().await?;

        coordinator.start_all_systems().await?;

        let result = coordinator
            .global_emergency_halt("Test emergency".to_string(), "TEST_USER".to_string())
            .await;

        assert!(result.is_ok());

        // Check that trading is now blocked
        assert!(!coordinator.is_trading_allowed("account1", "AAPL").await);

        coordinator.stop_all_systems().await;
        Ok(())
    }

    #[tokio::test]
    async fn test_system_health_monitoring() -> RiskResult<()> {
        let coordinator = create_test_coordinator().await?;

        coordinator.start_all_systems().await?;

        let health_report = coordinator.get_system_health().await;
        assert!(!health_report.component_status.is_empty());
        assert!(health_report.overall_health >= 0.0 && health_report.overall_health <= 1.0);

        coordinator.stop_all_systems().await;
        Ok(())
    }

    #[tokio::test]
    async fn test_event_subscription() -> RiskResult<()> {
        let coordinator = create_test_coordinator().await?;

        let mut event_rx = coordinator.subscribe_safety_events();

        // This would be a more comprehensive test with actual event generation
        // For now, just verify the subscription works
        assert!(event_rx.try_recv().is_err()); // No events initially
        Ok(())
    }

    #[tokio::test]
    async fn test_start_stop_lifecycle() -> RiskResult<()> {
        let coordinator = create_test_coordinator().await?;

        // Start systems
        coordinator.start_all_systems().await?;

        // Stop systems
        coordinator.stop_all_systems().await;

        Ok(())
    }

    #[tokio::test]
    async fn test_trading_blocked_when_not_running() -> RiskResult<()> {
        let coordinator = create_test_coordinator().await?;

        // Before starting, trading should be blocked
        assert!(!coordinator.is_trading_allowed("account1", "AAPL").await);

        Ok(())
    }

    #[tokio::test]
    async fn test_circuit_breaker_blocks_trading() -> RiskResult<()> {
        let coordinator = create_test_coordinator().await?;

        coordinator.start_all_systems().await?;

        // Trading should initially be allowed
        assert!(coordinator.is_trading_allowed("account1", "AAPL").await);

        coordinator.stop_all_systems().await;
        Ok(())
    }

    #[tokio::test]
    async fn test_multiple_accounts() -> RiskResult<()> {
        let coordinator = create_test_coordinator().await?;

        coordinator.start_all_systems().await?;

        // Check multiple accounts
        assert!(coordinator.is_trading_allowed("account1", "AAPL").await);
        assert!(coordinator.is_trading_allowed("account2", "GOOGL").await);
        assert!(coordinator.is_trading_allowed("account3", "MSFT").await);

        coordinator.stop_all_systems().await;
        Ok(())
    }

    #[tokio::test]
    async fn test_health_report_components() -> RiskResult<()> {
        let coordinator = create_test_coordinator().await?;

        coordinator.start_all_systems().await?;

        let health = coordinator.get_system_health().await;

        // Verify all components are reported
        assert!(health.component_status.contains_key("kill_switch"));
        assert!(health.component_status.contains_key("position_limiter"));
        assert!(health.component_status.contains_key("circuit_breaker"));
        assert!(health.component_status.contains_key("emergency_response"));

        coordinator.stop_all_systems().await;
        Ok(())
    }

    #[tokio::test]
    async fn test_emergency_halt_stops_trading() -> RiskResult<()> {
        let coordinator = create_test_coordinator().await?;

        coordinator.start_all_systems().await?;

        // Initially allowed
        assert!(coordinator.is_trading_allowed("account1", "AAPL").await);

        // Trigger emergency halt
        coordinator
            .global_emergency_halt("Test emergency".to_string(), "TEST_USER".to_string())
            .await?;

        // Should now be blocked
        assert!(!coordinator.is_trading_allowed("account1", "AAPL").await);

        Ok(())
    }

    #[tokio::test]
    async fn test_event_broadcast() -> RiskResult<()> {
        let coordinator = create_test_coordinator().await?;

        let mut event_rx = coordinator.subscribe_safety_events();

        coordinator.start_all_systems().await?;

        // Trigger emergency
        coordinator
            .global_emergency_halt("Test".to_string(), "USER".to_string())
            .await?;

        // Should receive event (with timeout)
        let result = tokio::time::timeout(Duration::from_millis(100), event_rx.recv()).await;

        match result {
            Ok(Ok(_event)) => {
                // Event received successfully
            },
            _ => {
                // Event might have been dropped or not received in time
                // This is acceptable in test environment
            },
        }

        Ok(())
    }

    #[tokio::test]
    async fn test_health_score_calculation() -> RiskResult<()> {
        let coordinator = create_test_coordinator().await?;

        coordinator.start_all_systems().await?;

        let health = coordinator.get_system_health().await;

        // Health should be between 0 and 1
        assert!(health.overall_health >= 0.0);
        assert!(health.overall_health <= 1.0);

        // With all systems healthy, should be close to 1.0
        assert!(health.overall_health > 0.5);

        coordinator.stop_all_systems().await;
        Ok(())
    }

    #[tokio::test]
    async fn test_concurrent_trading_checks() -> RiskResult<()> {
        let _config = create_test_config();
        let coordinator = Arc::new(create_test_coordinator().await?);

        coordinator.start_all_systems().await?;

        // Spawn concurrent checks
        let mut handles = vec![];
        for i in 0..10 {
            let coord = Arc::clone(&coordinator);
            let handle = tokio::spawn(async move {
                coord
                    .is_trading_allowed(&format!("account{}", i), "AAPL")
                    .await
            });
            handles.push(handle);
        }

        // All should complete successfully
        for handle in handles {
            let result = handle.await;
            assert!(result.is_ok());
        }

        coordinator.stop_all_systems().await;
        Ok(())
    }
}
