#![allow(
    clippy::assertions_on_constants,
    clippy::assertions_on_result_states,
    clippy::clone_on_copy,
    clippy::decimal_literal_representation,
    clippy::doc_markdown,
    clippy::empty_line_after_doc_comments,
    clippy::field_reassign_with_default,
    clippy::get_unwrap,
    clippy::identity_op,
    clippy::inconsistent_digit_grouping,
    clippy::indexing_slicing,
    clippy::integer_division,
    clippy::len_zero,
    clippy::let_underscore_must_use,
    clippy::manual_div_ceil,
    clippy::manual_let_else,
    clippy::manual_range_contains,
    clippy::modulo_arithmetic,
    clippy::needless_range_loop,
    clippy::non_ascii_literal,
    clippy::redundant_clone,
    clippy::shadow_reuse,
    clippy::shadow_same,
    clippy::shadow_unrelated,
    clippy::single_match_else,
    clippy::str_to_string,
    clippy::string_slice,
    clippy::tests_outside_test_module,
    clippy::too_many_lines,
    clippy::unnecessary_wraps,
    clippy::unseparated_literal_suffix,
    clippy::use_debug,
    clippy::useless_vec,
    clippy::wildcard_enum_match_arm,
    clippy::else_if_without_else,
    clippy::expect_used,
    clippy::missing_const_for_fn,
    clippy::similar_names,
    clippy::type_complexity,
    clippy::collapsible_else_if,
    clippy::doc_lazy_continuation,
    clippy::items_after_test_module,
    clippy::map_clone,
    clippy::multiple_unsafe_ops_per_block,
    clippy::unwrap_or_default,
    clippy::assign_op_pattern,
    clippy::needless_borrow,
    clippy::println_empty_string,
    clippy::unnecessary_cast,
    clippy::used_underscore_binding,
    clippy::create_dir,
    clippy::implicit_saturating_sub,
    clippy::exit,
    clippy::expect_fun_call,
    clippy::too_many_arguments,
    clippy::unnecessary_map_or,
    clippy::unwrap_used,
    dead_code,
    unused_imports,
    unused_variables,
    clippy::cloned_ref_to_slice_refs,
    clippy::neg_multiply,
    clippy::while_let_loop,
    clippy::bool_assert_comparison,
    clippy::excessive_precision,
    clippy::trivially_copy_pass_by_ref,
    clippy::op_ref,
    clippy::redundant_closure,
    clippy::unnecessary_lazy_evaluations,
    clippy::if_then_some_else_none,
    clippy::unnecessary_to_owned,
    clippy::single_component_path_imports,
)]
//! PPO Stress Testing Framework Tests
//!
//! TDD Red Phase: Tests written BEFORE implementation.
//! These tests will initially fail and guide the implementation.

use ml::ppo::stress_testing::{
    PPOStressTester, StressScenario,
    flash_crash_scenario, liquidity_crisis_scenario,
};
use ml::hyperopt::adapters::PPOTrainer;
use anyhow::Result;
use tracing::{info, warn};

/// Helper to create a minimal PPO trainer for testing
fn create_test_trainer() -> Result<PPOTrainer> {
    // PPOTrainer requires a DBN data directory and episodes count
    // For stress testing, we use the test data directory
    // Tests run from the workspace root, so we need to go up one level from ml/
    let data_dir = "../test_data";
    let episodes = 100;

    PPOTrainer::new(data_dir, episodes)
}

#[test]
fn test_flash_crash_scenario() -> Result<()> {
    // TDD Red Phase: This test will fail until implementation exists

    // Setup
    let trainer = create_test_trainer()?;
    let mut stress_tester = PPOStressTester::new(trainer)?;
    let scenario = flash_crash_scenario();

    // Execute stress test
    let result = stress_tester.run_scenario(&scenario)?;

    // Validate flash crash scenario parameters
    assert_eq!(result.scenario_name, "Flash Crash");
    assert_eq!(scenario.price_shock_pct, -10.0, "Flash crash should be -10% shock");
    assert_eq!(scenario.volatility_multiplier, 3.0, "Flash crash should be 3x volatility");
    assert_eq!(scenario.spread_multiplier, 10.0, "Flash crash should be 10x spreads");
    assert_eq!(scenario.duration_steps, 300, "Flash crash should last 300 steps (5 min)");

    // Validate robustness criteria
    assert!(
        result.max_drawdown <= scenario.max_drawdown_threshold,
        "Max drawdown {:.2}% exceeded threshold {:.2}%",
        result.max_drawdown,
        scenario.max_drawdown_threshold
    );

    assert!(
        !result.bankruptcy,
        "Agent went bankrupt during flash crash (portfolio <= 0)"
    );

    assert!(
        result.action_diversity >= scenario.min_action_diversity,
        "Action diversity {:.2}% below threshold {:.2}%",
        result.action_diversity,
        scenario.min_action_diversity
    );

    info!(max_drawdown = %format!("{:.2}", result.max_drawdown), action_diversity = %format!("{:.2}", result.action_diversity), "Flash Crash Test passed");

    Ok(())
}

#[test]
fn test_liquidity_crisis_scenario() -> Result<()> {
    // TDD Red Phase: This test will fail until implementation exists

    // Setup
    let trainer = create_test_trainer()?;
    let mut stress_tester = PPOStressTester::new(trainer)?;
    let scenario = liquidity_crisis_scenario();

    // Execute stress test
    let result = stress_tester.run_scenario(&scenario)?;

    // Validate liquidity crisis scenario parameters
    assert_eq!(result.scenario_name, "Liquidity Crisis");
    assert_eq!(scenario.price_shock_pct, -2.0, "Liquidity crisis should be -2% shock");
    assert_eq!(scenario.volatility_multiplier, 2.0, "Liquidity crisis should be 2x volatility");
    assert_eq!(scenario.spread_multiplier, 50.0, "Liquidity crisis should be 50x spreads");
    assert_eq!(scenario.duration_steps, 600, "Liquidity crisis should last 600 steps (10 min)");

    // Validate agent survived extreme spread widening
    assert!(
        !result.bankruptcy,
        "Agent went bankrupt during liquidity crisis"
    );

    assert!(
        result.max_drawdown <= scenario.max_drawdown_threshold,
        "Max drawdown {:.2}% exceeded threshold {:.2}%",
        result.max_drawdown,
        scenario.max_drawdown_threshold
    );

    info!(max_drawdown = %format!("{:.2}", result.max_drawdown), final_portfolio_pct = %format!("{:.2}", result.final_portfolio_pct), "Liquidity Crisis Test passed");

    Ok(())
}

#[test]
fn test_bankruptcy_detection() -> Result<()> {
    // TDD Red Phase: Test bankruptcy detection logic

    // Setup
    let trainer = create_test_trainer()?;
    let mut stress_tester = PPOStressTester::new(trainer)?;

    // Create extreme scenario designed to cause bankruptcy
    let extreme_scenario = StressScenario {
        name: "Extreme Bankruptcy Test".to_string(),
        price_shock_pct: -95.0,  // 95% crash (unrealistic but tests edge case)
        volatility_multiplier: 10.0,
        spread_multiplier: 100.0,
        duration_steps: 100,
        max_drawdown_threshold: 100.0,  // Accept any drawdown for this test
        min_action_diversity: 0.0,       // No diversity requirement
    };

    // Execute stress test
    let result = stress_tester.run_scenario(&extreme_scenario)?;

    // Validate bankruptcy was detected
    assert!(
        result.bankruptcy,
        "Bankruptcy should be detected when portfolio <= 0"
    );

    assert!(
        !result.passed,
        "Test should FAIL when bankruptcy occurs"
    );

    assert!(
        result.failure_reasons.iter().any(|r| r.contains("Bankruptcy")),
        "Failure reasons should mention bankruptcy: {:?}",
        result.failure_reasons
    );

    info!("Bankruptcy Detection Test: Correctly detected portfolio collapse");

    Ok(())
}

#[test]
fn test_metrics_collection() -> Result<()> {
    // TDD Red Phase: Test metrics collection logic

    // Setup
    let trainer = create_test_trainer()?;
    let mut stress_tester = PPOStressTester::new(trainer)?;
    let scenario = flash_crash_scenario();

    // Execute stress test
    let result = stress_tester.run_scenario(&scenario)?;

    // Validate all metrics are collected
    assert!(
        result.max_drawdown >= 0.0,
        "Max drawdown should be non-negative: {:.2}%",
        result.max_drawdown
    );

    assert!(
        result.action_diversity >= 0.0 && result.action_diversity <= 100.0,
        "Action diversity should be 0-100%: {:.2}%",
        result.action_diversity
    );

    assert!(
        result.total_trades > 0,
        "Should have executed trades during stress test"
    );

    assert!(
        result.q_value_std >= 0.0,
        "Q-value standard deviation should be non-negative: {:.4}",
        result.q_value_std
    );

    assert!(
        result.execution_time_ms > 0,
        "Execution time should be positive: {} ms",
        result.execution_time_ms
    );

    // Validate result has scenario name
    assert_eq!(
        result.scenario_name,
        scenario.name,
        "Result should contain scenario name"
    );

    info!("Metrics Collection Test: All metrics tracked correctly");
    info!(max_drawdown = %format!("{:.2}", result.max_drawdown), "Max Drawdown (%)");
    info!(final_portfolio_pct = %format!("{:.2}", result.final_portfolio_pct), "Portfolio Change (%)");
    info!(action_diversity = %format!("{:.2}", result.action_diversity), "Action Diversity (%)");
    info!(total_trades = result.total_trades, "Total Trades");
    info!(q_value_std = %format!("{:.4}", result.q_value_std), "Q-Value Std");
    info!(execution_time_ms = result.execution_time_ms, "Execution Time (ms)");

    Ok(())
}

#[test]
fn test_scenario_definitions() {
    // Validate all 8 predefined scenarios exist and have reasonable parameters
    let scenarios = vec![
        flash_crash_scenario(),
        liquidity_crisis_scenario(),
        ml::ppo::stress_testing::vix_spike_scenario(),
        ml::ppo::stress_testing::trending_market_scenario(),
        ml::ppo::stress_testing::whipsaw_scenario(),
        ml::ppo::stress_testing::gap_risk_scenario(),
        ml::ppo::stress_testing::correlation_breakdown_scenario(),
        ml::ppo::stress_testing::multi_asset_stress_scenario(),
    ];

    assert_eq!(scenarios.len(), 8, "Should have 8 predefined scenarios");

    // Verify all scenarios have reasonable thresholds
    for scenario in scenarios {
        assert!(
            scenario.max_drawdown_threshold > 0.0,
            "Max drawdown threshold must be positive: {}",
            scenario.name
        );
        assert!(
            scenario.min_action_diversity >= 0.0 && scenario.min_action_diversity <= 100.0,
            "Action diversity must be 0-100%: {}",
            scenario.name
        );
        assert!(
            scenario.duration_steps > 0,
            "Duration must be positive: {}",
            scenario.name
        );
    }
}
