#![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,
)]
//! TDD Tests for Kelly Criterion Position Sizing Integration with 45-Action DQN
//!
//! This test suite validates Kelly optimal position sizing across:
//! 1. Kelly fraction calculation (raw and adjusted)
//! 2. Position sizing based on capital, win rate, edge, and confidence
//! 3. Integration with 45-action DQN action space
//! 4. Risk management constraints (min/max kelly, fractional kelly)
//! 5. Confidence thresholds and sample size requirements
//! 6. Multi-symbol and multi-strategy tracking
//!
//! Test Coverage:
//! - Basic Kelly calculations: 4 tests
//! - Position sizing: 3 tests
//! - Risk constraints: 3 tests
//! - Confidence and sample size: 2 tests
//! - Integration with DQN: 2 tests
//! - Edge cases: 2 tests
//! Total: 16 TDD tests

use std::collections::HashMap;

// Mock Kelly Criterion structures for testing
#[derive(Debug, Clone)]
pub struct KellyConfig {
    pub enabled: bool,
    pub confidence_threshold: f64,
    pub fractional_kelly: f64,  // Fractional Kelly (e.g., 0.5 for half-Kelly)
    pub min_kelly_fraction: f64,
    pub max_kelly_fraction: f64,
    pub default_position_fraction: f64,
    pub lookback_periods: usize,
}

impl Default for KellyConfig {
    fn default() -> Self {
        Self {
            enabled: true,
            confidence_threshold: 0.5,
            fractional_kelly: 1.0,  // Full Kelly by default
            min_kelly_fraction: 0.0,
            max_kelly_fraction: 0.25,  // Never risk more than 25% of capital
            default_position_fraction: 0.05,  // 5% default
            lookback_periods: 100,
        }
    }
}

#[derive(Debug, Clone)]
pub struct TradeOutcome {
    pub symbol: String,
    pub strategy_id: String,
    pub profit_loss: f64,
    pub win: bool,
}

#[derive(Debug, Clone, Default)]
pub struct KellyResult {
    pub raw_kelly_fraction: f64,
    pub adjusted_kelly_fraction: f64,
    pub confidence: f64,
    pub win_rate: f64,
    pub average_win: f64,
    pub average_loss: f64,
    pub sample_size: usize,
    pub use_kelly: bool,
    pub position_fraction: f64,
}

// Kelly Calculator Implementation
pub struct KellyCalculator {
    config: KellyConfig,
    trade_history: HashMap<(String, String), Vec<TradeOutcome>>,
}

impl KellyCalculator {
    pub fn new(config: KellyConfig) -> Self {
        Self {
            config,
            trade_history: HashMap::new(),
        }
    }

    pub fn add_trade_outcome(&mut self, outcome: TradeOutcome) {
        let key = (outcome.symbol.clone(), outcome.strategy_id.clone());
        self.trade_history.entry(key).or_insert_with(Vec::new).push(outcome);
    }

    pub fn calculate_kelly_fraction(
        &self,
        symbol: &str,
        strategy_id: &str,
    ) -> Result<KellyResult, String> {
        let key = (symbol.to_string(), strategy_id.to_string());

        let trades = self.trade_history.get(&key).cloned().unwrap_or_default();

        // Require minimum sample size
        if trades.len() < 10 {
            return Err(format!(
                "Insufficient trade history: {} trades (minimum 10 required)",
                trades.len()
            ));
        }

        // Calculate statistics
        let total_trades = trades.len();
        let wins: Vec<_> = trades.iter().filter(|t| t.win).collect();
        let losses: Vec<_> = trades.iter().filter(|t| !t.win).collect();

        let win_rate = wins.len() as f64 / total_trades as f64;
        let loss_rate = losses.len() as f64 / total_trades as f64;

        let average_win = if wins.is_empty() {
            0.0
        } else {
            wins.iter().map(|t| t.profit_loss).sum::<f64>() / wins.len() as f64
        };

        let average_loss = if losses.is_empty() {
            0.0
        } else {
            losses.iter().map(|t| t.profit_loss.abs()).sum::<f64>() / losses.len() as f64
        };

        // Kelly formula: f* = (bp - q) / b
        // where b = average_win / average_loss (odds ratio)
        //       p = win_rate
        //       q = loss_rate
        let raw_kelly = if average_loss > 0.0 && average_win > 0.0 && win_rate > 0.0 {
            let b = average_win / average_loss;
            let p = win_rate;
            let q = loss_rate;
            let kelly = (b * p - q) / b;
            if kelly > 0.0 {
                kelly
            } else {
                0.0
            }
        } else {
            0.0
        };

        // Calculate confidence based on sample size and win rate
        let confidence = self.calculate_confidence(total_trades, win_rate);

        // Determine if Kelly sizing should be used
        let use_kelly = self.config.enabled
            && confidence >= self.config.confidence_threshold
            && raw_kelly > 0.0
            && total_trades >= 20;

        // Apply fractional Kelly and caps
        let adjusted_kelly = if use_kelly {
            let fractional = raw_kelly * self.config.fractional_kelly;
            fractional
                .max(self.config.min_kelly_fraction)
                .min(self.config.max_kelly_fraction)
        } else {
            self.config.default_position_fraction
        };

        Ok(KellyResult {
            raw_kelly_fraction: raw_kelly,
            adjusted_kelly_fraction: adjusted_kelly,
            confidence,
            win_rate,
            average_win,
            average_loss,
            sample_size: total_trades,
            use_kelly,
            position_fraction: adjusted_kelly,
        })
    }

    fn calculate_confidence(&self, sample_size: usize, win_rate: f64) -> f64 {
        // Sample size confidence (larger samples = higher confidence)
        let size_confidence = (sample_size as f64 / 100.0).min(1.0);

        // Win rate confidence (avoid extreme win rates which may be overfitting)
        let rate_confidence = if (0.3..=0.7).contains(&win_rate) {
            1.0  // Reasonable win rates
        } else if (0.2..=0.8).contains(&win_rate) {
            0.8  // Slightly extreme but acceptable
        } else {
            0.5  // Very extreme win rates - lower confidence
        };

        // Combined confidence
        (size_confidence * rate_confidence).min(1.0)
    }

    pub fn get_position_size(
        &self,
        symbol: &str,
        strategy_id: &str,
        capital: f64,
        entry_price: f64,
    ) -> Result<f64, String> {
        if entry_price <= 0.0 {
            return Err("Entry price must be positive".to_string());
        }

        let kelly_result = self.calculate_kelly_fraction(symbol, strategy_id)?;
        let position_value = capital * kelly_result.position_fraction;
        let shares = position_value / entry_price;

        Ok(shares)
    }
}

// ============================================================================
// TEST 1: Kelly Calculator Initialization
// ============================================================================
#[test]
fn test_kelly_calculator_initialization() {
    let config = KellyConfig::default();
    let calculator = KellyCalculator::new(config.clone());

    // Verify config loaded correctly
    assert!(calculator.config.enabled);
    assert_eq!(calculator.config.fractional_kelly, 1.0);
    assert_eq!(calculator.config.max_kelly_fraction, 0.25);
    assert_eq!(calculator.config.default_position_fraction, 0.05);
    assert_eq!(calculator.config.min_kelly_fraction, 0.0);

    // Verify empty trade history
    let result = calculator.calculate_kelly_fraction("ES", "dqn_strategy");
    assert!(
        result.is_err(),
        "Should fail with no trade history"
    );
}

// ============================================================================
// TEST 2: High Edge Position (60% Win, 1.5 Ratio) → Kelly with caps
// ============================================================================
#[test]
fn test_kelly_full_position_high_edge() {
    // Use larger sample for sufficient confidence
    let mut calculator = KellyCalculator::new(KellyConfig::default());

    // Create high-edge scenario: 60% win rate, wins are 50% larger than losses
    // 100 trades = 60 wins, 40 losses → confidence = 1.0 * 1.0 = 1.0 ✓
    for _ in 0..60 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: 150.0,  // Wins: $150
            win: true,
        });
    }

    for _ in 0..40 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: -100.0,  // Losses: $100
            win: false,
        });
    }

    let result = calculator
        .calculate_kelly_fraction("ES", "dqn_strategy")
        .expect("Kelly calculation should succeed");

    // Verify statistics
    assert_eq!(result.sample_size, 100, "Should have 100 trades");
    assert_eq!(result.win_rate, 0.6, "Win rate should be 60%");
    assert_eq!(result.average_win, 150.0, "Average win should be 150");
    assert_eq!(result.average_loss, 100.0, "Average loss should be 100");

    // Kelly formula: f* = (bp - q) / b
    // b = 150/100 = 1.5
    // p = 0.6, q = 0.4
    // f* = (1.5 * 0.6 - 0.4) / 1.5 = (0.9 - 0.4) / 1.5 ≈ 0.333
    assert!(result.raw_kelly_fraction > 0.3 && result.raw_kelly_fraction < 0.35,
            "Raw Kelly should be ~0.333, got {}", result.raw_kelly_fraction);

    // With full Kelly (1.0) and cap at 0.25, adjusted should be 0.25
    assert_eq!(
        result.adjusted_kelly_fraction, 0.25,
        "Adjusted Kelly should be capped at 0.25 (max_kelly_fraction)"
    );

    // Should use Kelly sizing
    assert!(result.use_kelly, "Should use Kelly sizing");
    assert!(result.confidence >= 0.5, "Confidence should meet threshold");
}

// ============================================================================
// TEST 3: Medium Edge Position (55% Win, 1.2 Ratio) → 50% Kelly
// ============================================================================
#[test]
fn test_kelly_half_position_medium_edge() {
    let mut config = KellyConfig::default();
    config.fractional_kelly = 0.5;  // Use half-Kelly for safety
    let mut calculator = KellyCalculator::new(config);

    // Medium edge: 55% win rate, wins are 20% larger than losses
    // 100 trades = 55 wins, 45 losses → confidence = 1.0 * 1.0 = 1.0 ✓
    for _ in 0..55 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "conservative_strategy".to_string(),
            profit_loss: 120.0,  // Wins: $120
            win: true,
        });
    }

    for _ in 0..45 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "conservative_strategy".to_string(),
            profit_loss: -100.0,  // Losses: $100
            win: false,
        });
    }

    let result = calculator
        .calculate_kelly_fraction("ES", "conservative_strategy")
        .expect("Kelly calculation should succeed");

    // Verify statistics
    assert_eq!(result.sample_size, 100, "Should have 100 trades");
    assert_eq!(result.win_rate, 0.55, "Win rate should be 55%");

    // Kelly formula: b = 120/100 = 1.2
    // f* = (1.2 * 0.55 - 0.45) / 1.2 = (0.66 - 0.45) / 1.2 = 0.21 / 1.2 ≈ 0.175
    let expected_raw_kelly = (1.2 * 0.55 - 0.45) / 1.2;
    assert!(
        (result.raw_kelly_fraction - expected_raw_kelly).abs() < 0.01,
        "Raw Kelly mismatch: expected {}, got {}",
        expected_raw_kelly,
        result.raw_kelly_fraction
    );

    // Half-Kelly: 0.175 * 0.5 ≈ 0.0875
    let expected_adjusted = expected_raw_kelly * 0.5;
    assert!(
        (result.adjusted_kelly_fraction - expected_adjusted).abs() < 0.01,
        "Adjusted Kelly should be ~{} (half of {}), got {}",
        expected_adjusted,
        result.raw_kelly_fraction,
        result.adjusted_kelly_fraction
    );

    // Should use Kelly sizing with sufficient sample size
    assert!(result.use_kelly, "Should use Kelly sizing");
}

// ============================================================================
// TEST 4: Zero Position for Negative Edge (Losing Strategy)
// ============================================================================
#[test]
fn test_kelly_zero_position_negative_edge() {
    let mut calculator = KellyCalculator::new(KellyConfig::default());

    // Losing strategy: 40% win rate (60% losses)
    // 100 trades = 40 wins, 60 losses → confidence = 1.0 * 0.8 = 0.8 ✓
    for _ in 0..40 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "bad_strategy".to_string(),
            profit_loss: 100.0,
            win: true,
        });
    }

    for _ in 0..60 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "bad_strategy".to_string(),
            profit_loss: -150.0,  // Losses larger than wins
            win: false,
        });
    }

    let result = calculator
        .calculate_kelly_fraction("ES", "bad_strategy")
        .expect("Kelly calculation should succeed");

    // Verify losing strategy
    assert_eq!(result.win_rate, 0.4, "Win rate should be 40%");
    assert!(result.raw_kelly_fraction <= 0.0, "Raw Kelly should be 0 or negative for losing strategy");

    // Position should default to config.default_position_fraction (5%)
    assert_eq!(
        result.adjusted_kelly_fraction, 0.05,
        "Should use default position size (5%) for losing strategy"
    );

    // Should NOT use Kelly sizing for negative edge
    assert!(!result.use_kelly, "Should not use Kelly sizing for negative edge");
}

// ============================================================================
// TEST 5: Fractional Kelly Conservative (0.25x Kelly)
// ============================================================================
#[test]
fn test_kelly_fractional_conservative() {
    let mut config = KellyConfig::default();
    config.fractional_kelly = 0.25;  // Ultra-conservative: 1/4 Kelly
    let mut calculator = KellyCalculator::new(config);

    // Profitable strategy: 65% win rate
    // 100 trades = 65 wins, 35 losses → confidence = 1.0 * 1.0 = 1.0 ✓
    for _ in 0..65 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "NQ".to_string(),
            strategy_id: "aggressive_strategy".to_string(),
            profit_loss: 200.0,
            win: true,
        });
    }

    for _ in 0..35 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "NQ".to_string(),
            strategy_id: "aggressive_strategy".to_string(),
            profit_loss: -100.0,
            win: false,
        });
    }

    let result = calculator
        .calculate_kelly_fraction("NQ", "aggressive_strategy")
        .expect("Kelly calculation should succeed");

    // Verify strategy
    assert_eq!(result.win_rate, 0.65, "Win rate should be 65%");

    // Kelly formula: b = 200/100 = 2.0
    // f* = (2.0 * 0.65 - 0.35) / 2.0 = (1.3 - 0.35) / 2.0 = 0.95 / 2.0 = 0.475
    let expected_raw = (2.0 * 0.65 - 0.35) / 2.0;

    // Fractional: 0.475 * 0.25 = 0.11875 (not capped at max 0.25)
    let expected_fractional = expected_raw * 0.25;
    assert!(
        (result.adjusted_kelly_fraction - expected_fractional).abs() < 0.01,
        "Adjusted Kelly should be ~{}, got {}",
        expected_fractional,
        result.adjusted_kelly_fraction
    );
}

// ============================================================================
// TEST 6: Position Size Calculation with Capital and Entry Price
// ============================================================================
#[test]
fn test_kelly_position_size_calculation() {
    let mut calculator = KellyCalculator::new(KellyConfig::default());

    // Add trade history with large sample
    for _ in 0..66 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: 100.0,
            win: true,
        });
    }

    for _ in 0..34 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: -75.0,
            win: false,
        });
    }

    // Calculate position size with $100k capital, $5000 entry price
    let position_size = calculator
        .get_position_size("ES", "dqn_strategy", 100_000.0, 5000.0)
        .expect("Position size calculation should succeed");

    // Verify calculation: kelly_result.position_fraction * capital / entry_price
    let kelly_result = calculator
        .calculate_kelly_fraction("ES", "dqn_strategy")
        .expect("Kelly calculation should succeed");

    let expected_position_value = 100_000.0 * kelly_result.position_fraction;
    let expected_shares = expected_position_value / 5000.0;

    assert_eq!(
        position_size, expected_shares,
        "Position size should match calculated value"
    );

    // Verify position size is reasonable (between 0 and capital)
    assert!(position_size > 0.0, "Position size should be positive");
    assert!(position_size < 100_000.0 / 5000.0, "Position size should be less than total capital");
}

// ============================================================================
// TEST 7: Minimum Sample Size Requirement (10 Trades)
// ============================================================================
#[test]
fn test_kelly_minimum_sample_size() {
    let mut calculator = KellyCalculator::new(KellyConfig::default());

    // Add only 9 trades (below minimum of 10)
    for _ in 0..9 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: 100.0,
            win: true,
        });
    }

    let result = calculator.calculate_kelly_fraction("ES", "dqn_strategy");
    assert!(
        result.is_err(),
        "Should fail with insufficient sample size (< 10 trades)"
    );

    if let Err(msg) = result {
        assert!(
            msg.contains("Insufficient trade history"),
            "Error message should mention insufficient history"
        );
    }
}

// ============================================================================
// TEST 8: Confidence Threshold Enforcement (Small Sample)
// ============================================================================
#[test]
fn test_kelly_confidence_threshold() {
    let mut config = KellyConfig::default();
    config.confidence_threshold = 0.8;  // High confidence required
    let mut calculator = KellyCalculator::new(config);

    // Add small sample size with normal win rate
    // 20 trades = 10 wins, 10 losses → confidence = (20/100) * 1.0 = 0.2 < 0.8 ✗
    for _ in 0..10 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: 100.0,
            win: true,
        });
    }

    for _ in 0..10 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: -100.0,
            win: false,
        });
    }

    let result = calculator
        .calculate_kelly_fraction("ES", "dqn_strategy")
        .expect("Kelly calculation should succeed");

    // With only 20 trades, confidence = (20/100) * 1.0 = 0.2 < 0.8 threshold
    assert!(
        result.confidence < 0.8,
        "Confidence should be below 0.8 threshold with small sample"
    );

    // Should NOT use Kelly sizing due to confidence threshold
    assert!(!result.use_kelly, "Should not use Kelly sizing below confidence threshold");

    // Should fall back to default position
    assert_eq!(
        result.adjusted_kelly_fraction, 0.05,
        "Should use default position (5%) when confidence is too low"
    );
}

// ============================================================================
// TEST 9: Multiple Strategies Tracking
// ============================================================================
#[test]
fn test_kelly_multiple_strategies_tracking() {
    let mut calculator = KellyCalculator::new(KellyConfig::default());

    // Strategy 1: DQN (high performance)
    // 100 trades = 62.5 wins, 37.5 losses → 62.5/100 = 0.625 → confidence = 1.0 * 1.0 = 1.0 ✓
    for _ in 0..63 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: 150.0,
            win: true,
        });
    }

    for _ in 0..37 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: -100.0,
            win: false,
        });
    }

    // Strategy 2: PPO (moderate performance)
    // 100 trades = 55 wins, 45 losses → confidence = 1.0 * 1.0 = 1.0 ✓
    for _ in 0..55 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "ppo_strategy".to_string(),
            profit_loss: 120.0,
            win: true,
        });
    }

    for _ in 0..45 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "ppo_strategy".to_string(),
            profit_loss: -90.0,
            win: false,
        });
    }

    let dqn_result = calculator
        .calculate_kelly_fraction("ES", "dqn_strategy")
        .expect("DQN Kelly calculation should succeed");

    let ppo_result = calculator
        .calculate_kelly_fraction("ES", "ppo_strategy")
        .expect("PPO Kelly calculation should succeed");

    // DQN should have higher edge (63% win vs 55% win)
    assert!(dqn_result.win_rate > ppo_result.win_rate, "DQN win rate should be higher");

    // DQN should recommend larger position
    assert!(
        dqn_result.adjusted_kelly_fraction > ppo_result.adjusted_kelly_fraction,
        "DQN should recommend larger position than PPO"
    );
}

// ============================================================================
// TEST 10: Multiple Symbols Tracking
// ============================================================================
#[test]
fn test_kelly_multiple_symbols_tracking() {
    let mut calculator = KellyCalculator::new(KellyConfig::default());

    // ES Futures: Lower win rate
    // 100 trades = 40 wins, 60 losses → confidence = 1.0 * 0.8 = 0.8 ✓
    for _ in 0..40 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: 200.0,
            win: true,
        });
    }

    for _ in 0..60 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: -100.0,
            win: false,
        });
    }

    // NQ Futures: Higher win rate
    // 100 trades = 60 wins, 40 losses → confidence = 1.0 * 1.0 = 1.0 ✓
    for _ in 0..60 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "NQ".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: 150.0,
            win: true,
        });
    }

    for _ in 0..40 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "NQ".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: -100.0,
            win: false,
        });
    }

    let es_result = calculator
        .calculate_kelly_fraction("ES", "dqn_strategy")
        .expect("ES Kelly calculation should succeed");

    let nq_result = calculator
        .calculate_kelly_fraction("NQ", "dqn_strategy")
        .expect("NQ Kelly calculation should succeed");

    // NQ has higher win rate (60% vs 40%)
    assert_eq!(es_result.win_rate, 0.4, "ES win rate should be 40%");
    assert_eq!(nq_result.win_rate, 0.6, "NQ win rate should be 60%");

    // NQ should recommend larger position due to higher edge
    assert!(
        nq_result.adjusted_kelly_fraction > es_result.adjusted_kelly_fraction,
        "NQ should recommend larger position than ES"
    );
}

// ============================================================================
// TEST 11: Kelly Fraction Caps (Min and Max)
// ============================================================================
#[test]
fn test_kelly_fraction_caps() {
    let mut config = KellyConfig::default();
    config.min_kelly_fraction = 0.02;  // Minimum 2%
    config.max_kelly_fraction = 0.15;  // Maximum 15%

    let mut calculator = KellyCalculator::new(config);

    // Create extremely profitable scenario
    // 100 trades = 87.5 wins, 12.5 losses (87 wins, 13 losses)
    // confidence = 1.0 * 0.5 = 0.5 ✓
    for _ in 0..87 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: 500.0,
            win: true,
        });
    }

    for _ in 0..13 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_strategy".to_string(),
            profit_loss: -100.0,
            win: false,
        });
    }

    let result = calculator
        .calculate_kelly_fraction("ES", "dqn_strategy")
        .expect("Kelly calculation should succeed");

    // Raw Kelly would be very high with 87% win rate
    assert!(result.raw_kelly_fraction > 0.5, "Raw Kelly should be very high (>50%)");

    // But adjusted should be capped at 15%
    assert_eq!(
        result.adjusted_kelly_fraction, 0.15,
        "Adjusted Kelly should be capped at max_kelly_fraction (15%)"
    );
}

// ============================================================================
// TEST 12: Zero Profit Edge Case (Break-Even Strategy)
// ============================================================================
#[test]
fn test_kelly_zero_profit_edge_case() {
    let mut calculator = KellyCalculator::new(KellyConfig::default());

    // Break-even scenario: 50% win, equal wins and losses
    // 100 trades = 50 wins, 50 losses → confidence = 1.0 * 1.0 = 1.0 ✓
    for _ in 0..50 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "breakeven_strategy".to_string(),
            profit_loss: 100.0,
            win: true,
        });
    }

    for _ in 0..50 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "breakeven_strategy".to_string(),
            profit_loss: -100.0,
            win: false,
        });
    }

    let result = calculator
        .calculate_kelly_fraction("ES", "breakeven_strategy")
        .expect("Kelly calculation should succeed");

    // With 50% win rate and equal odds, Kelly should be 0
    assert_eq!(result.raw_kelly_fraction, 0.0, "Kelly should be 0 for break-even strategy");

    // Should use default position
    assert_eq!(result.adjusted_kelly_fraction, 0.05, "Should use default position (5%)");
}

// ============================================================================
// TEST 13: Extreme Win Rate (95% Win) - Positive Edge despite extreme rate
// ============================================================================
#[test]
fn test_kelly_extreme_win_rate_low_confidence() {
    let mut calculator = KellyCalculator::new(KellyConfig::default());

    // Extreme 95% win rate with edge (wins > losses)
    // 100 trades = 95 wins, 5 losses
    // confidence = 1.0 * 0.5 = 0.5 ✓
    for _ in 0..95 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "overfitted_strategy".to_string(),
            profit_loss: 100.0,  // Wins are $100
            win: true,
        });
    }

    for _ in 0..5 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "overfitted_strategy".to_string(),
            profit_loss: -100.0,  // Losses are $100
            win: false,
        });
    }

    let result = calculator
        .calculate_kelly_fraction("ES", "overfitted_strategy")
        .expect("Kelly calculation should succeed");

    // Extreme win rate should reduce confidence
    assert_eq!(result.win_rate, 0.95, "Win rate should be 95%");
    assert_eq!(
        result.confidence, 0.5,
        "Confidence should be 0.5 for extreme win rates (1.0 * 0.5)"
    );

    // Kelly = (1.0 * 0.95 - 0.05) / 1.0 = 0.9 (very profitable despite equal odds!)
    // With confidence >= 0.5 threshold and size >= 20, Kelly sizing is used
    // Capped at max_kelly_fraction = 0.25
    assert!(result.use_kelly, "Should use Kelly sizing with high win rate");
    assert!(result.raw_kelly_fraction > 0.0, "Raw Kelly should be positive");
    assert_eq!(result.adjusted_kelly_fraction, 0.25, "Should cap at max_kelly_fraction (25%)");
}

// ============================================================================
// TEST 14: DQN 45-Action Integration - Position Size from Action Index
// ============================================================================
#[test]
fn test_kelly_dqn_45action_position_scaling() {
    let mut calculator = KellyCalculator::new(KellyConfig::default());

    // Add trade history for DQN strategy
    // 100 trades = 62.5 wins, 37.5 losses (62 wins, 38 losses)
    for _ in 0..62 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_45action".to_string(),
            profit_loss: 150.0,
            win: true,
        });
    }

    for _ in 0..38 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "dqn_45action".to_string(),
            profit_loss: -100.0,
            win: false,
        });
    }

    let result = calculator
        .calculate_kelly_fraction("ES", "dqn_45action")
        .expect("Kelly calculation should succeed");

    assert!(result.use_kelly, "Should use Kelly sizing");
    assert!(result.adjusted_kelly_fraction > 0.0, "Position fraction should be positive");

    // Test position scaling with different entry prices
    let capital = 100_000.0;
    let es_price = 5000.0;
    let nq_price = 20_000.0;

    let es_shares = calculator
        .get_position_size("ES", "dqn_45action", capital, es_price)
        .expect("Position calculation should succeed");

    let nq_shares = calculator
        .get_position_size("ES", "dqn_45action", capital, nq_price)
        .expect("Position calculation should succeed");

    // Same Kelly fraction but different entry prices should scale appropriately
    assert!((es_shares / nq_shares - nq_price / es_price).abs() < 0.01,
            "Position scaling should be inversely proportional to entry price");
}

// ============================================================================
// TEST 15: Kelly Position Sizing with Action Masking Constraints
// ============================================================================
#[test]
fn test_kelly_with_action_masking_constraints() {
    let mut calculator = KellyCalculator::new(KellyConfig::default());

    // Add winning strategy trades
    // 100 trades = 60 wins, 40 losses → confidence = 1.0 * 1.0 = 1.0 ✓
    for _ in 0..60 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "masked_dqn".to_string(),
            profit_loss: 120.0,
            win: true,
        });
    }

    for _ in 0..40 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "masked_dqn".to_string(),
            profit_loss: -80.0,
            win: false,
        });
    }

    let _kelly_result = calculator
        .calculate_kelly_fraction("ES", "masked_dqn")
        .expect("Kelly calculation should succeed");

    // Verify Kelly sizing gives actionable position
    let capital = 50_000.0;
    let entry_price = 5000.0;

    let position_size = calculator
        .get_position_size("ES", "masked_dqn", capital, entry_price)
        .expect("Position size should be calculated");

    // Position should be within reasonable bounds for action masking
    assert!(position_size > 0.0, "Position size should be positive");
    assert!(position_size < (capital / entry_price), "Position size should be less than total capital");

    // Kelly position should be meaningful but not reckless
    let position_value = position_size * entry_price;
    let position_percent = position_value / capital;
    assert!(
        position_percent <= 0.25,
        "Kelly position should never exceed 25% of capital (max_kelly_fraction), got {}%",
        position_percent * 100.0
    );
}

// ============================================================================
// TEST 16: Rapid Adaptation to Losing Period
// ============================================================================
#[test]
fn test_kelly_rapid_adaptation_losing_period() {
    let mut calculator = KellyCalculator::new(KellyConfig::default());

    // Initial winning period: 70% win rate
    // 20 trades = 14 wins, 6 losses → confidence = (20/100) * 1.0 = 0.2 < 0.5 ✗
    for _ in 0..14 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "adaptive_dqn".to_string(),
            profit_loss: 100.0,
            win: true,
        });
    }

    for _ in 0..6 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "adaptive_dqn".to_string(),
            profit_loss: -100.0,
            win: false,
        });
    }

    let initial_result = calculator
        .calculate_kelly_fraction("ES", "adaptive_dqn")
        .expect("Initial Kelly calculation should succeed");

    assert_eq!(initial_result.win_rate, 0.7, "Initial win rate should be 70%");
    let initial_position = initial_result.adjusted_kelly_fraction;

    // Market downturn: Add 80 more losing trades
    // Total: 100 trades = 24 wins, 76 losses
    // confidence = 1.0 * 0.5 = 0.5 ✓
    for _ in 0..10 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "adaptive_dqn".to_string(),
            profit_loss: 100.0,
            win: true,
        });
    }

    for _ in 0..70 {
        calculator.add_trade_outcome(TradeOutcome {
            symbol: "ES".to_string(),
            strategy_id: "adaptive_dqn".to_string(),
            profit_loss: -150.0,  // Larger losses
            win: false,
        });
    }

    let adapted_result = calculator
        .calculate_kelly_fraction("ES", "adaptive_dqn")
        .expect("Adapted Kelly calculation should succeed");

    // Win rate should drop significantly
    assert_eq!(adapted_result.win_rate, 0.24, "Win rate should drop to 24%");

    // Position should reduce or stay at default due to losing strategy
    assert!(
        adapted_result.adjusted_kelly_fraction <= initial_position,
        "Position should reduce or stay same during losing period"
    );

    // With 24% win rate (below 50%), should use default or smaller position
    assert!(!adapted_result.use_kelly, "Should not use Kelly when win rate drops below 50%");
}
