//! Integration test: Risk limit violation -> Kill switch activation
//!
//! Verifies that risk safety mechanisms properly trigger under stress,
//! correctly scope kill switch activations, and block trading when active.
//! This is a critical path test for the Risk -> Kill Switch pipeline.

use risk::safety::kill_switch::{AtomicKillSwitch, TradingGate};
use risk::safety::KillSwitchConfig;
use risk::risk_types::KillSwitchScope;

fn create_test_kill_switch() -> AtomicKillSwitch {
    let config = KillSwitchConfig::default();
    AtomicKillSwitch::new_test(config)
}

// ---------------------------------------------------------------------------
// Test 1: Kill switch activation and trading blocking
// ---------------------------------------------------------------------------

#[tokio::test]
async fn test_kill_switch_activation_and_blocking() {
    let kill_switch = create_test_kill_switch();

    // Initially not triggered
    assert!(
        !kill_switch.is_triggered(),
        "Kill switch should start inactive"
    );
    assert!(
        kill_switch.is_trading_allowed(&KillSwitchScope::Global),
        "Trading should be allowed initially"
    );

    // Trigger the kill switch via global activation
    let result = kill_switch
        .activate_global(
            "Max drawdown exceeded: -5.2%".to_string(),
            "risk_monitor".to_string(),
        )
        .await;
    assert!(result.is_ok(), "Triggering kill switch should succeed");

    // Now it should be triggered
    assert!(
        kill_switch.is_triggered(),
        "Kill switch should be active after trigger"
    );

    // Trading should be blocked
    assert!(
        !kill_switch.is_trading_allowed(&KillSwitchScope::Global),
        "Trading must be blocked after global kill switch trigger"
    );

    // Also blocked for any scoped query (global takes precedence)
    assert!(
        !kill_switch.is_trading_allowed(&KillSwitchScope::Symbol("ES.FUT".to_string())),
        "Symbol-scoped trading must also be blocked by global kill switch"
    );
}

// ---------------------------------------------------------------------------
// Test 2: Scoped kill switch (symbol-level)
// ---------------------------------------------------------------------------

#[tokio::test]
async fn test_scoped_kill_switch_symbol() {
    let kill_switch = create_test_kill_switch();

    // Trigger for a specific symbol scope only
    let result = kill_switch
        .engage(
            KillSwitchScope::Symbol("ES.FUT".to_string()),
            "Symbol-level risk limit breached".to_string(),
            "symbol_monitor".to_string(),
            false, // no cascade
        )
        .await;
    assert!(result.is_ok(), "Scoped trigger should succeed");

    // Global should NOT be triggered
    assert!(
        !kill_switch.is_triggered(),
        "Global kill switch should NOT be triggered by symbol-scoped engagement"
    );
    assert!(
        kill_switch.is_trading_allowed(&KillSwitchScope::Global),
        "Global trading should still be allowed"
    );

    // The specific symbol should be blocked
    assert!(
        !kill_switch.is_trading_allowed(&KillSwitchScope::Symbol("ES.FUT".to_string())),
        "ES.FUT trading should be blocked"
    );

    // Other symbols should NOT be blocked
    assert!(
        kill_switch.is_trading_allowed(&KillSwitchScope::Symbol("NQ.FUT".to_string())),
        "NQ.FUT trading should still be allowed"
    );

    // Health metrics should be finite
    let (error_rate, _failures) = kill_switch.get_health_metrics();
    assert!(
        error_rate.is_finite(),
        "Error rate should be finite, got {}",
        error_rate
    );
}

// ---------------------------------------------------------------------------
// Test 3: Kill switch reset restores trading
// ---------------------------------------------------------------------------

#[tokio::test]
async fn test_kill_switch_reset_restores_trading() {
    let kill_switch = create_test_kill_switch();

    // Trigger globally
    kill_switch.trigger();
    assert!(kill_switch.is_triggered());
    assert!(!kill_switch.is_trading_allowed(&KillSwitchScope::Global));

    // Reset
    let result = kill_switch.reset(Some(KillSwitchScope::Global)).await;
    assert!(result.is_ok(), "Reset should succeed");

    // Trading should be restored
    assert!(!kill_switch.is_triggered(), "Kill switch should be cleared");
    assert!(
        kill_switch.is_trading_allowed(&KillSwitchScope::Global),
        "Trading should be allowed after reset"
    );
}

// ---------------------------------------------------------------------------
// Test 4: Multiple scoped activations and selective reset
// ---------------------------------------------------------------------------

#[tokio::test]
async fn test_multiple_scoped_activations_and_selective_reset() {
    let kill_switch = create_test_kill_switch();

    // Engage two different scopes
    kill_switch
        .engage(
            KillSwitchScope::Symbol("AAPL".to_string()),
            "AAPL halt".to_string(),
            "user".to_string(),
            false,
        )
        .await
        .expect("AAPL engage should succeed");

    kill_switch
        .engage(
            KillSwitchScope::Account("ACC-001".to_string()),
            "Account risk limit".to_string(),
            "user".to_string(),
            false,
        )
        .await
        .expect("Account engage should succeed");

    // Both should be blocked
    assert!(
        !kill_switch.is_trading_allowed(&KillSwitchScope::Symbol("AAPL".to_string())),
        "AAPL should be blocked"
    );
    assert!(
        !kill_switch.is_trading_allowed(&KillSwitchScope::Account("ACC-001".to_string())),
        "ACC-001 should be blocked"
    );

    // Global still allowed
    assert!(
        kill_switch.is_trading_allowed(&KillSwitchScope::Global),
        "Global should still be allowed"
    );

    // Reset only the symbol scope
    kill_switch
        .reset(Some(KillSwitchScope::Symbol("AAPL".to_string())))
        .await
        .expect("AAPL reset should succeed");

    // AAPL should be restored, account still blocked
    assert!(
        kill_switch.is_trading_allowed(&KillSwitchScope::Symbol("AAPL".to_string())),
        "AAPL should be allowed after reset"
    );
    assert!(
        !kill_switch.is_trading_allowed(&KillSwitchScope::Account("ACC-001".to_string())),
        "ACC-001 should remain blocked"
    );
}

// ---------------------------------------------------------------------------
// Test 5: Cascade behavior (portfolio -> strategies)
// ---------------------------------------------------------------------------

#[tokio::test]
async fn test_cascade_portfolio_to_strategies() {
    let kill_switch = create_test_kill_switch();

    // Trigger a portfolio-level kill switch with cascade=true
    kill_switch
        .engage(
            KillSwitchScope::Portfolio("portfolio1".to_string()),
            "Portfolio drawdown exceeded".to_string(),
            "risk_engine".to_string(),
            true, // cascade
        )
        .await
        .expect("Portfolio engage should succeed");

    // The portfolio itself should be blocked
    assert!(
        !kill_switch.is_trading_allowed(&KillSwitchScope::Portfolio("portfolio1".to_string())),
        "Portfolio should be blocked"
    );

    // The system is active
    let is_active = kill_switch.is_active().await.expect("is_active should work");
    assert!(is_active, "Kill switch should report as active");
}

// ---------------------------------------------------------------------------
// Test 6: Metrics tracking
// ---------------------------------------------------------------------------

#[tokio::test]
async fn test_kill_switch_metrics_tracking() {
    let kill_switch = create_test_kill_switch();

    // Initial metrics should be zero
    let (checks_before, commands_before) = kill_switch.get_metrics();
    assert_eq!(checks_before, 0, "Initial health checks should be 0");
    assert_eq!(commands_before, 0, "Initial commands should be 0");

    // Perform operations that increment counters
    kill_switch
        .engage(
            KillSwitchScope::Global,
            "Test".to_string(),
            "user".to_string(),
            false,
        )
        .await
        .expect("engage should succeed");

    kill_switch
        .reset(Some(KillSwitchScope::Global))
        .await
        .expect("reset should succeed");

    // Commands should have incremented (engage + reset = 2)
    let (_checks_after, commands_after) = kill_switch.get_metrics();
    assert_eq!(
        commands_after, 2,
        "Two commands (engage + reset) should be tracked, got {}",
        commands_after
    );

    // Health metrics: no failures (no Redis in test mode)
    let (error_rate, failures) = kill_switch.get_health_metrics();
    assert_eq!(error_rate, 0.0, "Error rate should be 0.0 with no Redis");
    assert_eq!(failures, 0, "Failures should be 0 with no Redis");
}

// ---------------------------------------------------------------------------
// Test 7: Trading gate integration
// ---------------------------------------------------------------------------

#[tokio::test]
async fn test_trading_gate_lifecycle() {
    let gate = TradingGate::new(true);
    assert!(gate.is_open(), "Gate should start open");

    // Close gate (simulating risk event)
    gate.close();
    assert!(!gate.is_open(), "Gate should be closed");

    // Reopen gate (simulating risk clearance)
    gate.open();
    assert!(gate.is_open(), "Gate should be reopened");
}

// ---------------------------------------------------------------------------
// Test 8: Kill switch health check (no Redis = healthy)
// ---------------------------------------------------------------------------

#[tokio::test]
async fn test_kill_switch_health_in_test_mode() {
    let kill_switch = create_test_kill_switch();

    let healthy = kill_switch
        .is_healthy()
        .await
        .expect("is_healthy should succeed");
    assert!(
        healthy,
        "Kill switch without Redis should report healthy (test mode)"
    );
}

// ---------------------------------------------------------------------------
// Test 9: Deactivate restores scoped trading
// ---------------------------------------------------------------------------

#[tokio::test]
async fn test_deactivate_restores_scoped_trading() {
    let kill_switch = create_test_kill_switch();

    let scope = KillSwitchScope::Strategy("momentum_v2".to_string());

    // Activate
    kill_switch
        .activate(scope.clone(), "Test halt".to_string(), "user".to_string(), false)
        .await
        .expect("activate should succeed");

    assert!(
        !kill_switch.is_trading_allowed(&scope),
        "Strategy should be blocked after activation"
    );

    // Deactivate
    kill_switch
        .deactivate(scope.clone(), "user".to_string())
        .await
        .expect("deactivate should succeed");

    assert!(
        kill_switch.is_trading_allowed(&scope),
        "Strategy should be allowed after deactivation"
    );
}
