#![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,
)]
/// Test: Comprehensive Action-Position Sign Validation
///
/// Purpose: Validate that all 45 FactoredAction combinations produce correct position signs
/// and magnitudes after execution.
///
/// Coverage:
/// - All 45 action combinations (5 exposures × 3 order types × 3 urgencies)
/// - Sign convention validation (Long=positive, Short=negative, Flat=zero)
/// - Magnitude validation against target exposure
///
/// Expected Results:
/// - Long100 (exposure=0): +10.00 contracts
/// - Long50 (exposure=1): +5.00 contracts
/// - Flat (exposure=2): 0.00 contracts
/// - Short50 (exposure=3): -5.00 contracts
/// - Short100 (exposure=4): -10.00 contracts

use ml::dqn::{FactoredAction, ExposureLevel, OrderType, Urgency, PortfolioTracker};
use tracing::info;

#[test]
fn test_all_45_actions_sign_convention() {
    let price = 100.0;
    let max_position = 10.0;

    // Expected exposure values for each ExposureLevel
    let expected_exposures = [
        (ExposureLevel::Long100, 1.0, "Long100"),
        (ExposureLevel::Long50, 0.5, "Long50"),
        (ExposureLevel::Flat, 0.0, "Flat"),
        (ExposureLevel::Short50, -0.5, "Short50"),
        (ExposureLevel::Short100, -1.0, "Short100"),
    ];

    let order_types = [
        (OrderType::Market, "Market"),
        (OrderType::LimitMaker, "LimitMaker"),
        (OrderType::IoC, "IoC"),
    ];

    let urgencies = [
        (Urgency::Patient, "Patient"),
        (Urgency::Normal, "Normal"),
        (Urgency::Aggressive, "Aggressive"),
    ];

    info!("Testing All 45 Action-Position Sign Conventions");

    let mut test_count = 0;
    let mut pass_count = 0;

    // Test all combinations
    for (exposure_level, expected_target, exposure_name) in &expected_exposures {
        for (order_type, order_name) in &order_types {
            for (urgency, urgency_name) in &urgencies {
                test_count += 1;

                // Create action
                let action = FactoredAction::new(*exposure_level, *order_type, *urgency);
                let action_index = action.to_index();

                // Get target exposure from action
                let target_exposure = action.target_exposure();

                // Verify target exposure matches expected
                assert!(
                    (target_exposure - expected_target).abs() < 1e-6,
                    "Action {} ({}, {}, {}): target_exposure mismatch. Expected: {}, Got: {}",
                    action_index,
                    exposure_name,
                    order_name,
                    urgency_name,
                    expected_target,
                    target_exposure
                );

                // Create fresh portfolio tracker
                let mut portfolio = PortfolioTracker::new(10000.0, 0.01, 0.0);

                // Execute action
                portfolio.execute_action(action, price as f32, max_position as f32);

                // Get actual position
                let actual_position = portfolio.current_position() as f64;

                // Calculate expected position
                let expected_position = target_exposure * max_position;

                // Validate position matches target
                let position_error = (actual_position - expected_position).abs();
                let is_pass = position_error < 1e-6;

                if is_pass {
                    pass_count += 1;
                }

                // Log result
                info!(
                    passed = is_pass,
                    action_index,
                    exposure_name,
                    order_name,
                    urgency_name,
                    target_exposure,
                    actual_position,
                    expected_position,
                    position_error,
                    "Action sign convention check"
                );

                // Assert with detailed failure message
                assert!(
                    is_pass,
                    "\n❌ Action {} FAILED: Sign/magnitude mismatch\n\
                     Action: {} (exposure={}, order={}, urgency={})\n\
                     Target Exposure: {:+.2}\n\
                     Expected Position: {:+.2} contracts\n\
                     Actual Position: {:+.2} contracts\n\
                     Error: {:.6}\n",
                    action_index,
                    action_index,
                    exposure_name,
                    order_name,
                    urgency_name,
                    target_exposure,
                    expected_position,
                    actual_position,
                    position_error
                );
            }
        }
    }

    info!(
        test_count,
        pass_count,
        failed = test_count - pass_count,
        pass_rate_pct = (pass_count as f64 / test_count as f64) * 100.0,
        "Action sign convention test summary"
    );

    assert_eq!(
        pass_count,
        test_count,
        "\n❌ Not all actions passed sign convention validation"
    );

    info!("All 45 actions passed sign convention validation");
}

#[test]
fn test_specific_exposure_levels_detailed() {
    let price = 100.0;
    let max_position = 10.0;

    info!("Detailed Exposure Level Validation");

    // Test Long100
    {
        let action = FactoredAction::from_index(36).unwrap(); // Long100 (exposure=4), Market (order=0), Patient (urgency=0)
        let mut portfolio = PortfolioTracker::new(10000.0, 0.01, 0.0);
        portfolio.execute_action(action, price as f32, max_position as f32);

        let target = action.target_exposure();
        let position = portfolio.current_position() as f64;
        let expected = target * max_position;

        info!(target, expected, position, "Long100 exposure validation");

        assert_eq!(target, 1.0, "Long100 target exposure should be +1.0");
        assert_eq!(position, 10.0, "Long100 should result in +10.00 contracts");
        assert!(position > 0.0, "Long100 position should be POSITIVE");
    }

    // Test Long50
    {
        let action = FactoredAction::from_index(27).unwrap(); // Long50 (exposure=3), Market (order=0), Patient (urgency=0)
        let mut portfolio = PortfolioTracker::new(10000.0, 0.01, 0.0);
        portfolio.execute_action(action, price as f32, max_position as f32);

        let target = action.target_exposure();
        let position = portfolio.current_position() as f64;
        let expected = target * max_position;

        info!(target, expected, position, "Long50 exposure validation");

        assert_eq!(target, 0.5, "Long50 target exposure should be +0.5");
        assert_eq!(position, 5.0, "Long50 should result in +5.00 contracts");
        assert!(position > 0.0, "Long50 position should be POSITIVE");
    }

    // Test Flat
    {
        let action = FactoredAction::from_index(18).unwrap(); // Flat, Market, Patient
        let mut portfolio = PortfolioTracker::new(10000.0, 0.01, 0.0);
        portfolio.execute_action(action, price as f32, max_position as f32);

        let target = action.target_exposure();
        let position = portfolio.current_position() as f64;
        let expected = target * max_position;

        info!(target, expected, position, "Flat exposure validation");

        assert_eq!(target, 0.0, "Flat target exposure should be 0.0");
        assert_eq!(position, 0.0, "Flat should result in 0.00 contracts");
    }

    // Test Short50
    {
        let action = FactoredAction::from_index(9).unwrap(); // Short50 (exposure=1), Market (order=0), Patient (urgency=0)
        let mut portfolio = PortfolioTracker::new(10000.0, 0.01, 0.0);
        portfolio.execute_action(action, price as f32, max_position as f32);

        let target = action.target_exposure();
        let position = portfolio.current_position() as f64;
        let expected = target * max_position;

        info!(target, expected, position, "Short50 exposure validation");

        assert_eq!(target, -0.5, "Short50 target exposure should be -0.5");
        assert_eq!(position, -5.0, "Short50 should result in -5.00 contracts");
        assert!(position < 0.0, "Short50 position should be NEGATIVE");
    }

    // Test Short100
    {
        let action = FactoredAction::from_index(0).unwrap(); // Short100 (exposure=0), Market (order=0), Patient (urgency=0)
        let mut portfolio = PortfolioTracker::new(10000.0, 0.01, 0.0);
        portfolio.execute_action(action, price as f32, max_position as f32);

        let target = action.target_exposure();
        let position = portfolio.current_position() as f64;
        let expected = target * max_position;

        info!(target, expected, position, "Short100 exposure validation");

        assert_eq!(target, -1.0, "Short100 target exposure should be -1.0");
        assert_eq!(position, -10.0, "Short100 should result in -10.00 contracts");
        assert!(position < 0.0, "Short100 position should be NEGATIVE");
    }

    info!("All exposure levels validated successfully");
}

#[test]
fn test_position_transitions() {
    let price = 100.0;
    let max_position = 10.0;

    info!("Position Transition Validation");

    let mut portfolio = PortfolioTracker::new(10000.0, 0.01, 0.0);

    // Transition 1: Flat → Long100
    let action1 = FactoredAction::new(ExposureLevel::Long100, OrderType::Market, Urgency::Normal);
    portfolio.execute_action(action1, price as f32, max_position as f32);
    let pos1 = portfolio.current_position() as f64;
    info!(pos1, "Flat to Long100 position");
    assert_eq!(pos1, 10.0, "Should be at +10.0 after Long100");

    // Transition 2: Long100 → Short100
    let action2 = FactoredAction::new(ExposureLevel::Short100, OrderType::Market, Urgency::Normal);
    portfolio.execute_action(action2, price as f32, max_position as f32);
    let pos2 = portfolio.current_position() as f64;
    info!(pos2, "Long100 to Short100 position");
    assert_eq!(pos2, -10.0, "Should be at -10.0 after Short100");

    // Transition 3: Short100 → Flat
    let action3 = FactoredAction::new(ExposureLevel::Flat, OrderType::Market, Urgency::Normal);
    portfolio.execute_action(action3, price as f32, max_position as f32);
    let pos3 = portfolio.current_position() as f64;
    info!(pos3, "Short100 to Flat position");
    assert_eq!(pos3, 0.0, "Should be at 0.0 after Flat");

    // Transition 4: Flat → Short50
    let action4 = FactoredAction::new(ExposureLevel::Short50, OrderType::Market, Urgency::Normal);
    portfolio.execute_action(action4, price as f32, max_position as f32);
    let pos4 = portfolio.current_position() as f64;
    info!(pos4, "Flat to Short50 position");
    assert_eq!(pos4, -5.0, "Should be at -5.0 after Short50");

    // Transition 5: Short50 → Long50
    let action5 = FactoredAction::new(ExposureLevel::Long50, OrderType::Market, Urgency::Normal);
    portfolio.execute_action(action5, price as f32, max_position as f32);
    let pos5 = portfolio.current_position() as f64;
    info!(pos5, "Short50 to Long50 position");
    assert_eq!(pos5, 5.0, "Should be at +5.0 after Long50");

    info!("All position transitions validated successfully");
}
