#![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,
)]
//! DQN Rainbow Feature Toggle Tests (Wave 6.4)
//!
//! Validates that all Rainbow DQN components are enabled by default
//! and can be toggled via configuration.
//!
//! Test Coverage:
//! - All Rainbow features enabled by default in DQNHyperparameters::conservative()
//! - Default parameter values match Rainbow DQN paper standards
//! - Individual features can be disabled without breaking the system
//! - Vanilla DQN configuration (all Rainbow features disabled) still works

use ml::trainers::dqn::DQNHyperparameters;

#[test]
fn test_all_rainbow_features_enabled_by_default() {
    let hyperparams = DQNHyperparameters::conservative();

    // All Rainbow components should be enabled by default
    assert_eq!(hyperparams.n_steps, 3, "Multi-step (n=3) should be enabled by default");
}

#[test]
fn test_rainbow_defaults_match_best_practice() {
    let hyperparams = DQNHyperparameters::conservative();

    // Validate default values match Rainbow DQN paper
    assert_eq!(hyperparams.num_atoms, 51, "C51 standard is 51 atoms");
    assert_eq!(hyperparams.noisy_sigma_init, 0.5, "Rainbow standard sigma_init");
    assert_eq!(hyperparams.per_alpha, 0.6, "Standard PER alpha");
    assert_eq!(hyperparams.tau, 0.001, "Standard soft update rate");
    assert_eq!(hyperparams.dueling_hidden_dim, 128, "Dueling hidden dimension should be 128");
}

#[test]
fn test_distributional_parameters_valid() {
    let hyperparams = DQNHyperparameters::conservative();

    // Validate C51 distributional RL parameters
    assert_eq!(hyperparams.num_atoms, 51, "C51 standard: 51 atoms");
    assert_eq!(hyperparams.v_min, -1000.0, "V_min should be -1000.0");
    assert_eq!(hyperparams.v_max, 1000.0, "V_max should be 1000.0");
    assert!(hyperparams.v_max > hyperparams.v_min, "V_max must be greater than V_min");
    assert!(hyperparams.num_atoms > 1, "Must have at least 2 atoms");
    assert!(hyperparams.num_atoms % 2 == 1, "Odd number of atoms is standard (51)");
}

#[test]
fn test_multi_step_returns_valid() {
    let hyperparams = DQNHyperparameters::conservative();

    // Validate n-step returns parameter
    assert_eq!(hyperparams.n_steps, 3, "Default should be 3 (Rainbow standard)");
    assert!(hyperparams.n_steps >= 1, "n_steps must be at least 1");
    assert!(hyperparams.n_steps <= 10, "n_steps should be <= 10 to avoid high variance");
}

#[test]
fn test_dueling_parameters_valid() {
    let hyperparams = DQNHyperparameters::conservative();

    // Validate dueling network parameters
    assert_eq!(hyperparams.dueling_hidden_dim, 128, "Hidden dim should be 128");
    assert!(hyperparams.dueling_hidden_dim >= 32, "Hidden dim should be at least 32");
    assert!(hyperparams.dueling_hidden_dim <= 512, "Hidden dim should be reasonable (<= 512)");
}

#[test]
fn test_noisy_nets_parameters_valid() {
    let hyperparams = DQNHyperparameters::conservative();

    // Validate noisy networks parameters
    assert_eq!(hyperparams.noisy_sigma_init, 0.5, "Sigma init should be 0.5 (Rainbow standard)");
    assert!(hyperparams.noisy_sigma_init > 0.0, "Sigma must be positive");
    assert!(hyperparams.noisy_sigma_init <= 1.0, "Sigma should be reasonable (<= 1.0)");
}

#[test]
fn test_per_parameters_valid() {
    let hyperparams = DQNHyperparameters::conservative();

    // Validate PER parameters
    assert_eq!(hyperparams.per_alpha, 0.6, "PER alpha should be 0.6");
    assert_eq!(hyperparams.per_beta_start, 0.4, "PER beta start should be 0.4");
    assert!(hyperparams.per_alpha >= 0.0 && hyperparams.per_alpha <= 1.0, "Alpha must be in [0, 1]");
    assert!(hyperparams.per_beta_start >= 0.0 && hyperparams.per_beta_start <= 1.0, "Beta must be in [0, 1]");
}

#[test]
fn test_can_create_vanilla_dqn_config() {
    // Test that we can create a vanilla DQN config with all Rainbow features disabled
    let vanilla_config = DQNHyperparameters {
        n_steps: 1,
        ..DQNHyperparameters::conservative()
    };

    // Verify vanilla DQN configuration
    assert_eq!(vanilla_config.n_steps, 1, "n_steps should be 1 for vanilla DQN");
}

#[test]
fn test_can_disable_individual_rainbow_features() {
    // Test that we can selectively disable Rainbow features
    let config_no_dueling = DQNHyperparameters {
        ..DQNHyperparameters::conservative()
    };

    let config_no_distributional = DQNHyperparameters {
        ..DQNHyperparameters::conservative()
    };

    let config_no_noisy = DQNHyperparameters {
        ..DQNHyperparameters::conservative()
    };
}

#[test]
fn test_rainbow_feature_combinations() {
    // Test common feature combinations

    // Rainbow without noisy nets (use epsilon-greedy instead)
    let rainbow_epsilon_greedy = DQNHyperparameters {
        ..DQNHyperparameters::conservative()
    };

    // Rainbow without PER (uniform replay)
    let rainbow_uniform_replay = DQNHyperparameters {
        ..DQNHyperparameters::conservative()
    };

    // Double DQN + Dueling only (no distributional, no noisy, no PER)
    let double_dueling_only = DQNHyperparameters {
        n_steps: 1,
        ..DQNHyperparameters::conservative()
    };
}

#[test]
fn test_n_step_parameter_range() {
    // Test valid n_steps range (1-10)
    for n in 1..=10 {
        let config = DQNHyperparameters {
            n_steps: n,
            ..DQNHyperparameters::conservative()
        };
        assert_eq!(config.n_steps, n);
    }
}

#[test]
fn test_num_atoms_parameter_range() {
    // Test various num_atoms values (odd numbers recommended)
    for &atoms in &[11, 21, 31, 41, 51, 61, 71, 81, 101] {
        let config = DQNHyperparameters {
            num_atoms: atoms,
            ..DQNHyperparameters::conservative()
        };
        assert_eq!(config.num_atoms, atoms);
        assert!(atoms % 2 == 1, "Odd number of atoms is standard");
    }
}

#[test]
fn test_v_min_v_max_parameter_ranges() {
    // Test various v_min/v_max ranges
    let test_ranges = [
        (-100.0, 100.0),
        (-500.0, 500.0),
        (-1000.0, 1000.0),
        (-2000.0, 2000.0),
    ];

    for (v_min, v_max) in test_ranges {
        let config = DQNHyperparameters {
            v_min,
            v_max,
            ..DQNHyperparameters::conservative()
        };
        assert_eq!(config.v_min, v_min);
        assert_eq!(config.v_max, v_max);
        assert!(config.v_max > config.v_min, "v_max must be greater than v_min");
    }
}

#[test]
fn test_backward_compatibility() {
    // Ensure conservative() produces a valid configuration
    let config = DQNHyperparameters::conservative();

    // All required fields should have valid values
    assert!(config.learning_rate > 0.0);
    assert!(config.batch_size > 0);
    assert!(config.gamma >= 0.0 && config.gamma <= 1.0);
    assert!(config.buffer_size > 0);
    assert!(config.epochs > 0);

    // Rainbow features should all be enabled
    assert_eq!(config.n_steps, 3);
}
