/// WAVE 26 P1.5: TDD test for expanded hyperopt learning rate range
///
/// CRITICAL BUG: Current range [2e-5, 8e-5] excludes production default 1e-4
/// This test verifies the fix to expand range to [1e-5, 3e-4] (30x range)
///
/// Test Requirements:
/// 1. Learning rate lower bound must be 1e-5 (log scale)
/// 2. Learning rate upper bound must be 3e-4 (log scale)
/// 3. Range must include production default 1e-4
/// 4. Warmup ratio must be added to search space [0.0, 0.2]

#[cfg(test)]
mod wave26_hyperopt_lr_range_tests {
    use ml::hyperopt::adapters::dqn::DQNParams;
    use ml::hyperopt::search_space::ParameterSpace;

    #[test]
    fn test_learning_rate_range_includes_production_default() {
        // CRITICAL: Production default is 1e-4
        let production_lr = 1e-4;

        let bounds = DQNParams::continuous_bounds();

        // Learning rate is at index 0 in continuous_bounds
        let (lr_min_log, lr_max_log) = bounds[0];

        // Verify new expanded range
        let lr_min = lr_min_log.exp();
        let lr_max = lr_max_log.exp();

        // Test 1: Lower bound is 1e-5
        assert!(
            (lr_min - 1e-5).abs() < 1e-7,
            "Learning rate lower bound should be 1e-5, got {}",
            lr_min
        );

        // Test 2: Upper bound is 3e-4
        assert!(
            (lr_max - 3e-4).abs() < 1e-6,
            "Learning rate upper bound should be 3e-4, got {}",
            lr_max
        );

        // Test 3: Range includes production default
        assert!(
            lr_min <= production_lr && production_lr <= lr_max,
            "Production default {} must be within range [{}, {}]",
            production_lr, lr_min, lr_max
        );

        // Test 4: Range is 30x (3e-4 / 1e-5 = 30)
        let range_multiplier = lr_max / lr_min;
        assert!(
            (range_multiplier - 30.0).abs() < 0.1,
            "Range multiplier should be 30x, got {}",
            range_multiplier
        );
    }

    #[test]
    fn test_warmup_ratio_in_search_space() {
        let bounds = DQNParams::continuous_bounds();

        // Warmup ratio should be added after Kelly parameters
        // Index 22: warmup_ratio (new parameter)
        assert_eq!(
            bounds.len(),
            23,
            "Expected 23 parameters (22 existing + 1 warmup_ratio), got {}",
            bounds.len()
        );

        let (warmup_min, warmup_max) = bounds[22];

        // Test warmup ratio bounds [0.0, 0.2] (0-20% warmup)
        assert_eq!(
            warmup_min, 0.0,
            "Warmup ratio lower bound should be 0.0, got {}",
            warmup_min
        );

        assert_eq!(
            warmup_max, 0.2,
            "Warmup ratio upper bound should be 0.2 (20%), got {}",
            warmup_max
        );
    }

    #[test]
    fn test_from_continuous_validates_warmup_ratio() {
        // Create continuous vector with all 23 parameters
        let continuous = vec![
            1e-4_f64.ln(), // learning_rate (production default, within new range)
            92.0,          // batch_size
            0.9588,        // gamma
            97_273_f64.ln(), // buffer_size
            1.404,         // hold_penalty_weight
            5.563,         // max_position_absolute
            24.77_f64.ln(), // huber_delta
            0.01,          // entropy_coefficient
            1.0,           // transaction_cost_multiplier
            0.6,           // per_alpha
            0.4,           // per_beta_start
            -2.0,          // v_min
            2.0,           // v_max
            0.5_f64.ln(),  // noisy_sigma_init
            256.0,         // dueling_hidden_dim
            3.0,           // n_steps
            101.0,         // num_atoms
            1.5,           // minimum_profit_factor
            0.5,           // kelly_fractional
            0.25,          // kelly_max_fraction
            20.0,          // kelly_min_trades
            20.0,          // volatility_window
            0.1,           // warmup_ratio (10% warmup)
        ];

        let params = DQNParams::from_continuous(&continuous).unwrap();

        // Verify learning rate is production default
        assert!(
            (params.learning_rate - 1e-4).abs() < 1e-6,
            "Learning rate should be 1e-4, got {}",
            params.learning_rate
        );

        // Verify warmup ratio is extracted
        assert!(
            (params.warmup_ratio - 0.1).abs() < 1e-6,
            "Warmup ratio should be 0.1, got {}",
            params.warmup_ratio
        );
    }

    #[test]
    fn test_warmup_ratio_bounds_clamping() {
        // Test minimum warmup ratio (0.0)
        let continuous_min = vec![
            1e-5_f64.ln(), 64.0, 0.95, 50_000_f64.ln(), 1.0, 4.0, 15.0_f64.ln(), 0.0, 0.5,
            0.4, 0.2, -3.0, 1.0, 0.1_f64.ln(), 128.0, 1.0, 51.0, 1.1,
            0.25, 0.1, 10.0, 10.0,
            0.0, // warmup_ratio min
        ];

        let params_min = DQNParams::from_continuous(&continuous_min).unwrap();
        assert!(
            (params_min.warmup_ratio - 0.0).abs() < 1e-6,
            "Warmup ratio min should be 0.0, got {}",
            params_min.warmup_ratio
        );

        // Test maximum warmup ratio (0.2)
        let continuous_max = vec![
            3e-4_f64.ln(), 160.0, 0.99, 100_000_f64.ln(), 2.0, 8.0, 40.0_f64.ln(), 0.1, 2.0,
            0.8, 0.6, -1.0, 3.0, 1.0_f64.ln(), 512.0, 5.0, 201.0, 2.0,
            1.0, 0.5, 50.0, 30.0,
            0.2, // warmup_ratio max
        ];

        let params_max = DQNParams::from_continuous(&continuous_max).unwrap();
        assert!(
            (params_max.warmup_ratio - 0.2).abs() < 1e-6,
            "Warmup ratio max should be 0.2, got {}",
            params_max.warmup_ratio
        );
    }

    #[test]
    fn test_param_names_includes_warmup_ratio() {
        let names = DQNParams::param_names();

        assert_eq!(
            names.len(),
            23,
            "Expected 23 parameter names, got {}",
            names.len()
        );

        assert_eq!(
            names[22],
            "warmup_ratio",
            "Parameter 22 should be 'warmup_ratio', got '{}'",
            names[22]
        );
    }
}
