//! Trading Gate - Atomic Order Processing Guards
//!
//! Provides atomic gates at all order processing entry points to ensure
//! immediate blocking when kill switch is activated. Designed for regulatory
//! compliance with sub-microsecond checking latency.

use std::sync::Arc;
use std::time::Instant;

use tracing::{debug, warn};

use crate::error::{RiskError, RiskResult};
use crate::risk_types::KillSwitchScope;
use crate::safety::kill_switch::AtomicKillSwitch;

/// Trading gate that guards all order processing operations
#[derive(Debug, Clone)]
pub struct TradingGate {
    kill_switch: Arc<AtomicKillSwitch>,
}

impl TradingGate {
    /// Create new trading gate with kill switch reference
    #[must_use]
    pub const fn new(kill_switch: Arc<AtomicKillSwitch>) -> Self {
        Self { kill_switch }
    }

    /// Check if trading is allowed for the given scope (THE CRITICAL GATE)
    /// This function MUST complete in under 1 microsecond for regulatory compliance
    #[inline(always)]
    pub fn check_trading_allowed(&self, scope: &KillSwitchScope) -> RiskResult<()> {
        if !self.kill_switch.is_trading_allowed(scope) {
            return Err(RiskError::KillSwitchActive {
                scope: scope.clone(),
                message: "Trading blocked by kill switch".to_owned(),
            });
        }
        Ok(())
    }

    /// Pre-order validation gate - checks kill switch before any order processing
    #[inline(always)]
    pub fn pre_order_gate(&self, symbol: &str, account: Option<&str>) -> RiskResult<()> {
        let start_time = Instant::now();

        // Check symbol-level kill switch
        let symbol_scope = KillSwitchScope::Symbol(symbol.to_owned());
        self.check_trading_allowed(&symbol_scope)?;

        // Check account-level kill switch if account is specified
        if let Some(account_id) = account {
            let account_scope = KillSwitchScope::Account(account_id.to_owned());
            self.check_trading_allowed(&account_scope)?;
        }

        // Check global kill switch (most critical)
        self.check_trading_allowed(&KillSwitchScope::Global)?;

        let elapsed = start_time.elapsed();
        if elapsed.as_nanos() > 1000 {
            // More than 1 microsecond
            warn!(
                "Trading gate check took {}ns (target: <1000ns) for symbol: {}",
                elapsed.as_nanos(),
                symbol
            );
        }

        debug!(
            "Trading gate passed for symbol: {} ({}ns)",
            symbol,
            elapsed.as_nanos()
        );

        Ok(())
    }

    /// Market data gate - checks if market data processing should continue
    #[inline(always)]
    pub fn market_data_gate(&self, symbol: &str) -> RiskResult<()> {
        let symbol_scope = KillSwitchScope::Symbol(symbol.to_owned());
        self.check_trading_allowed(&symbol_scope)?;
        self.check_trading_allowed(&KillSwitchScope::Global)?;
        Ok(())
    }

    /// Execution gate - final check before order execution
    #[inline(always)]
    pub fn execution_gate(&self, symbol: &str, account: &str) -> RiskResult<()> {
        let start_time = Instant::now();

        // Final checks before execution - must be ultra-fast
        self.check_trading_allowed(&KillSwitchScope::Symbol(symbol.to_owned()))?;
        self.check_trading_allowed(&KillSwitchScope::Account(account.to_owned()))?;
        self.check_trading_allowed(&KillSwitchScope::Global)?;

        let elapsed = start_time.elapsed();
        if elapsed.as_nanos() > 500 {
            // Even faster for execution gate
            warn!(
                "Execution gate check took {}ns (target: <500ns) for {}/{}",
                elapsed.as_nanos(),
                symbol,
                account
            );
        }

        Ok(())
    }

    /// Strategy gate - checks if strategy should continue operating
    #[inline(always)]
    pub fn strategy_gate(&self, strategy_id: &str) -> RiskResult<()> {
        let strategy_scope = KillSwitchScope::Strategy(strategy_id.to_owned());
        self.check_trading_allowed(&strategy_scope)?;
        self.check_trading_allowed(&KillSwitchScope::Global)?;
        Ok(())
    }

    /// Portfolio gate - checks if portfolio operations should continue
    #[inline(always)]
    pub fn portfolio_gate(&self, portfolio_id: &str) -> RiskResult<()> {
        let portfolio_scope = KillSwitchScope::Portfolio(portfolio_id.to_owned());
        self.check_trading_allowed(&portfolio_scope)?;
        self.check_trading_allowed(&KillSwitchScope::Global)?;
        Ok(())
    }

    /// Risk calculation gate - checks if risk calculations should proceed
    #[inline(always)]
    pub fn risk_calculation_gate(&self, account: &str) -> RiskResult<()> {
        let account_scope = KillSwitchScope::Account(account.to_owned());
        self.check_trading_allowed(&account_scope)?;
        self.check_trading_allowed(&KillSwitchScope::Global)?;
        Ok(())
    }

    /// Emergency check - bypasses all other gates for immediate shutdown validation
    #[inline(always)]
    #[must_use]
    pub fn emergency_check(&self) -> bool {
        self.kill_switch
            .is_trading_allowed(&KillSwitchScope::Global)
    }

    /// Get the underlying kill switch for direct access
    #[must_use]
    pub const fn kill_switch(&self) -> &Arc<AtomicKillSwitch> {
        &self.kill_switch
    }
}

/// Macro for automatic gate checking in trading functions
#[macro_export]
macro_rules! trading_gate_check {
    ($gate:expr, $symbol:expr) => {
        if let Err(e) = $gate.pre_order_gate($symbol, None) {
            return Err(e.into());
        }
    };
    ($gate:expr, $symbol:expr, $account:expr) => {
        if let Err(e) = $gate.pre_order_gate($symbol, Some($account)) {
            return Err(e.into());
        }
    };
}

/// Utility functions for common gate patterns
impl TradingGate {
    /// Comprehensive order validation gate - checks all relevant scopes
    pub fn comprehensive_order_gate(
        &self,
        symbol: &str,
        account: &str,
        strategy_id: Option<&str>,
    ) -> RiskResult<()> {
        let start_time = Instant::now();

        // Check global first (fastest rejection)
        self.check_trading_allowed(&KillSwitchScope::Global)?;

        // Check account
        self.check_trading_allowed(&KillSwitchScope::Account(account.to_owned()))?;

        // Check symbol
        self.check_trading_allowed(&KillSwitchScope::Symbol(symbol.to_owned()))?;

        // Check strategy if provided
        if let Some(strategy) = strategy_id {
            self.check_trading_allowed(&KillSwitchScope::Strategy(strategy.to_owned()))?;
        }

        let elapsed = start_time.elapsed();
        debug!(
            "Comprehensive order gate passed for {}/{} ({}ns)",
            symbol,
            account,
            elapsed.as_nanos()
        );

        Ok(())
    }

    /// Batch gate check for multiple symbols (optimized for market data processing)
    pub fn batch_symbol_gate(&self, symbols: &[String]) -> RiskResult<Vec<String>> {
        // First check global - if global is disabled, all symbols fail
        if !self
            .kill_switch
            .is_trading_allowed(&KillSwitchScope::Global)
        {
            return Err(RiskError::KillSwitchActive {
                scope: KillSwitchScope::Global,
                message: "Global kill switch active - all symbols blocked".to_owned(),
            });
        }

        // Check each symbol individually
        let mut allowed_symbols = Vec::with_capacity(symbols.len());
        for symbol in symbols {
            let symbol_scope = KillSwitchScope::Symbol(symbol.clone());
            if self.kill_switch.is_trading_allowed(&symbol_scope) {
                allowed_symbols.push(symbol.clone());
            } else {
                debug!("Symbol {} blocked by kill switch", symbol);
            }
        }

        Ok(allowed_symbols)
    }

    /// High-frequency gate check with performance monitoring
    pub fn hf_gate_check(&self, symbol: &str) -> (RiskResult<()>, u64) {
        let start_time = Instant::now();
        let result = self.pre_order_gate(symbol, None);
        let elapsed_ns = start_time.elapsed().as_nanos() as u64;
        (result, elapsed_ns)
    }
}

/// Performance metrics for gate operations
#[derive(Debug, Clone)]
pub struct GateMetrics {
    pub total_checks: u64,
    pub blocked_checks: u64,
    pub average_latency_ns: f64,
    pub max_latency_ns: u64,
    pub min_latency_ns: u64,
}

impl Default for GateMetrics {
    fn default() -> Self {
        Self {
            total_checks: 0,
            blocked_checks: 0,
            average_latency_ns: 0.0,
            max_latency_ns: 0,
            min_latency_ns: u64::MAX,
        }
    }
}

/// Trading gate with performance monitoring
pub struct MonitoredTradingGate {
    gate: TradingGate,
    metrics: Arc<std::sync::Mutex<GateMetrics>>,
}

impl MonitoredTradingGate {
    #[must_use]
    pub fn new(kill_switch: Arc<AtomicKillSwitch>) -> Self {
        Self {
            gate: TradingGate::new(kill_switch),
            metrics: Arc::new(std::sync::Mutex::new(GateMetrics::default())),
        }
    }

    /// Check with performance monitoring
    pub fn check_with_monitoring(&self, scope: &KillSwitchScope) -> RiskResult<()> {
        let start_time = Instant::now();
        let result = self.gate.check_trading_allowed(scope);
        let elapsed_ns = start_time.elapsed().as_nanos() as u64;

        // Update metrics
        if let Ok(mut metrics) = self.metrics.lock() {
            metrics.total_checks += 1;
            if result.is_err() {
                metrics.blocked_checks += 1;
            }

            metrics.max_latency_ns = metrics.max_latency_ns.max(elapsed_ns);
            metrics.min_latency_ns = metrics.min_latency_ns.min(elapsed_ns);

            let total_latency =
                metrics.average_latency_ns * (metrics.total_checks - 1) as f64 + elapsed_ns as f64;
            metrics.average_latency_ns = total_latency / metrics.total_checks as f64;
        }

        result
    }

    pub fn get_metrics(&self) -> RiskResult<GateMetrics> {
        self.metrics
            .lock()
            .map(|metrics| metrics.clone())
            .map_err(|_| RiskError::Internal("Failed to acquire metrics lock".to_owned()))
    }

    /// Get the underlying gate
    #[must_use]
    pub const fn gate(&self) -> &TradingGate {
        &self.gate
    }
}

#[cfg(test)]
#[allow(clippy::assertions_on_result_states, clippy::str_to_string)]
mod tests {
    use super::*;
    use crate::safety::kill_switch::AtomicKillSwitch;
    use crate::safety::KillSwitchConfig;

    fn create_test_gate() -> RiskResult<TradingGate> {
        let config = KillSwitchConfig::default();
        let kill_switch = Arc::new(AtomicKillSwitch::new_test(config));
        Ok(TradingGate::new(kill_switch))
    }

    #[tokio::test]
    async fn test_gate_creation() -> RiskResult<()> {
        let gate = create_test_gate()?;

        // Initially, all trading should be allowed
        assert!(gate.pre_order_gate("AAPL", None).is_ok());
        assert!(gate.emergency_check());

        Ok(())
    }

    #[tokio::test]
    async fn test_pre_order_gate() -> RiskResult<()> {
        let gate = create_test_gate()?;

        // Test symbol gate
        let result = gate.pre_order_gate("AAPL", None);
        assert!(result.is_ok());

        // Test with account
        let result = gate.pre_order_gate("AAPL", Some("account123"));
        assert!(result.is_ok());

        Ok(())
    }

    #[tokio::test]
    async fn test_comprehensive_order_gate() -> RiskResult<()> {
        let gate = create_test_gate()?;

        let result = gate.comprehensive_order_gate("AAPL", "account123", Some("strategy1"));
        assert!(result.is_ok());

        Ok(())
    }

    #[tokio::test]
    async fn test_batch_symbol_gate() -> RiskResult<()> {
        let gate = create_test_gate()?;

        let symbols = vec!["AAPL".to_string(), "GOOGL".to_string(), "MSFT".to_string()];
        let allowed = gate.batch_symbol_gate(&symbols)?;

        assert_eq!(allowed.len(), 3);
        assert_eq!(allowed, symbols);

        Ok(())
    }

    #[tokio::test]
    async fn test_gate_with_kill_switch_active() -> RiskResult<()> {
        let gate = create_test_gate()?;

        // Activate kill switch for symbol
        gate.kill_switch()
            .activate(
                KillSwitchScope::Symbol("AAPL".to_string()),
                "Test".to_string(),
                "test_user".to_string(),
                false,
            )
            .await?;

        // Gate should now block
        let result = gate.pre_order_gate("AAPL", None);
        assert!(result.is_err());

        Ok(())
    }

    #[tokio::test]
    async fn test_performance_monitoring() -> RiskResult<()> {
        let config = KillSwitchConfig::default();
        let kill_switch = Arc::new(AtomicKillSwitch::new_test(config));

        let monitored_gate = MonitoredTradingGate::new(kill_switch);
        let scope = KillSwitchScope::Symbol("AAPL".to_string());

        // Perform some checks
        for _ in 0..10 {
            let _ = monitored_gate.check_with_monitoring(&scope);
        }

        let metrics = monitored_gate.get_metrics()?;
        assert_eq!(metrics.total_checks, 10);
        assert!(metrics.average_latency_ns > 0.0);

        Ok(())
    }

    #[tokio::test]
    async fn test_hf_gate_check() -> RiskResult<()> {
        let gate = create_test_gate()?;

        let (result, latency_ns) = gate.hf_gate_check("AAPL");
        assert!(result.is_ok());
        assert!(latency_ns > 0);

        Ok(())
    }

    #[tokio::test]
    async fn test_different_gate_types() -> RiskResult<()> {
        let gate = create_test_gate()?;

        // Test all gate types
        assert!(gate.market_data_gate("AAPL").is_ok());
        assert!(gate.execution_gate("AAPL", "account123").is_ok());
        assert!(gate.strategy_gate("strategy1").is_ok());
        assert!(gate.portfolio_gate("portfolio1").is_ok());
        assert!(gate.risk_calculation_gate("account123").is_ok());

        Ok(())
    }

    #[tokio::test]
    async fn test_macro_usage() -> RiskResult<()> {
        let gate = create_test_gate()?;

        // This simulates usage of the trading_gate_check! macro
        // In real code, this would be used in trading functions
        let symbol = "AAPL";
        let account = "account123";

        // Macro equivalent checks
        assert!(gate.pre_order_gate(symbol, None).is_ok());
        assert!(gate.pre_order_gate(symbol, Some(account)).is_ok());

        Ok(())
    }
}
