#![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,
)]
//! Integration tests for Wave 16S adaptive features in production DQN trainer
//!
//! Verifies:
//! - Kelly criterion optimizer initialization and position sizing
//! - Volatility-adjusted epsilon exploration
//! - Risk-adjusted rewards (Sharpe ratio-based)

#[test]
fn test_kelly_criterion_parameters() {
    // Test Kelly parameters are within safe bounds
    let kelly_fractional = 0.5; // 50% Kelly (conservative)
    let kelly_max_fraction = 0.25; // Maximum 25% of portfolio
    let kelly_min_trades = 20; // Minimum trade history

    assert!(kelly_fractional > 0.0 && kelly_fractional <= 1.0,
            "Kelly fractional must be in (0, 1]");
    assert!(kelly_max_fraction > 0.0 && kelly_max_fraction <= 0.25,
            "Kelly max fraction must be in (0, 0.25] for safety");
    assert!(kelly_min_trades >= 10,
            "Minimum trades must be at least 10 for statistical significance");
}

#[test]
fn test_volatility_epsilon_adjustment() {
    // Test volatility-adjusted epsilon calculation logic
    let base_epsilon: f64 = 0.1;

    // Low volatility: reduce exploration
    let low_vol = 0.005; // 0.5%
    let low_vol_multiplier = if low_vol < 0.01 {
        0.5
    } else {
        1.0
    };
    let low_vol_epsilon = (base_epsilon * low_vol_multiplier).clamp(0.05_f64, 0.95_f64);
    assert_eq!(low_vol_epsilon, 0.05, "Low volatility should reduce epsilon to floor");

    // High volatility: increase exploration
    let high_vol = 0.08; // 8%
    let high_vol_multiplier = if high_vol > 0.05 {
        2.0
    } else {
        1.0
    };
    let high_vol_epsilon = (base_epsilon * high_vol_multiplier).clamp(0.05_f64, 0.95_f64);
    assert_eq!(high_vol_epsilon, 0.2, "High volatility should increase epsilon");

    // Moderate volatility: linear interpolation
    let mod_vol = 0.03; // 3%
    let mod_vol_multiplier = if mod_vol < 0.01 {
        0.5
    } else if mod_vol > 0.05 {
        2.0
    } else {
        0.5 + (mod_vol - 0.01) / 0.04 * 1.5
    };
    let mod_vol_epsilon = (base_epsilon * mod_vol_multiplier).clamp(0.05_f64, 0.95_f64);
    assert!(mod_vol_epsilon > 0.05 && mod_vol_epsilon < 0.2,
            "Moderate volatility should interpolate epsilon");
}

#[test]
fn test_risk_adjusted_reward_calculation() {
    // Test Sharpe-based reward adjustment
    let raw_reward = 0.01;

    // Build sample PnL history (stable positive returns)
    let pnl_history: Vec<f64> = (0..30).map(|_| 0.01).collect();

    // Calculate Sharpe
    let mean: f64 = pnl_history.iter().sum::<f64>() / pnl_history.len() as f64;
    let variance: f64 = pnl_history.iter()
        .map(|x| (x - mean).powi(2))
        .sum::<f64>() / pnl_history.len() as f64;
    let std_dev = variance.sqrt().max(1e-8);
    let sharpe = mean / std_dev;

    // Apply Sharpe adjustment
    let adjusted_reward = raw_reward * sharpe;

    // Sharpe should be very high for constant returns (low variance)
    assert!(sharpe > 100.0, "Constant returns should have very high Sharpe");
    assert!(adjusted_reward > raw_reward, "Positive Sharpe should amplify reward");

    // Test negative returns (should reduce reward)
    let negative_pnl: Vec<f64> = (0..30).map(|_| -0.01).collect();
    let neg_mean: f64 = negative_pnl.iter().sum::<f64>() / negative_pnl.len() as f64;
    let neg_variance: f64 = negative_pnl.iter()
        .map(|x| (x - neg_mean).powi(2))
        .sum::<f64>() / negative_pnl.len() as f64;
    let neg_sharpe = neg_mean / neg_variance.sqrt().max(1e-8);
    let neg_adjusted = raw_reward * neg_sharpe;

    assert!(neg_sharpe < 0.0, "Negative returns should have negative Sharpe");
    assert!(neg_adjusted < raw_reward, "Negative Sharpe should reduce reward");
}

#[test]
fn test_kelly_position_sizing_safety() {
    // Test Kelly position sizing with different win rates

    // Favorable strategy: 60% win rate, 2:1 reward/risk
    let win_prob = 0.6;
    let avg_win = 0.02; // 2%
    let avg_loss = 0.01; // 1%

    let odds = avg_win / avg_loss;
    let q = 1.0 - win_prob;
    let kelly_fraction_raw: f64 = (odds * win_prob - q) / odds;
    let kelly_fraction = kelly_fraction_raw.min(0.25); // Clamp to max 25%

    assert!(kelly_fraction_raw > 0.0, "Favorable strategy should have positive Kelly");
    assert!(kelly_fraction <= 0.25, "Kelly should be capped at 25% for safety");

    // Apply fractional Kelly (50%)
    let fractional_kelly = kelly_fraction * 0.5;
    assert!(fractional_kelly > 0.0 && fractional_kelly <= 0.125,
            "Fractional Kelly should be more conservative");

    // Unfavorable strategy: 40% win rate, 1:2 reward/risk
    let bad_win_prob = 0.4;
    let bad_avg_win = 0.01;
    let bad_avg_loss = 0.02;

    let bad_odds = bad_avg_win / bad_avg_loss;
    let bad_q = 1.0 - bad_win_prob;
    let bad_kelly = (bad_odds * bad_win_prob - bad_q) / bad_odds;

    assert!(bad_kelly <= 0.01, "Unfavorable strategy should have minimal/zero Kelly");
}

#[test]
fn test_pnl_history_buffer_management() {
    // Test PnL history circular buffer (max 1000 entries)
    use std::collections::VecDeque;

    let mut pnl_history: VecDeque<f64> = VecDeque::with_capacity(1000);

    // Fill beyond capacity
    for i in 0..1200 {
        pnl_history.push_back(i as f64 * 0.001);

        // Evict oldest when over capacity
        if pnl_history.len() > 1000 {
            pnl_history.pop_front();
        }
    }

    assert_eq!(pnl_history.len(), 1000, "Buffer should be capped at 1000");
    assert_eq!(pnl_history.front(), Some(&0.200), "Oldest entry should be from step 200");
    assert_eq!(pnl_history.back(), Some(&1.199), "Newest entry should be from step 1199");
}
