#![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,
)]
//! Wave 16N Integration Tests: P&L Realism
//!
//! Property tests ensuring P&L values remain realistic throughout training,
//! preventing catastrophic explosions like the Wave 16M $640 trillion bug.
//!
//! # Test Coverage
//! - P&L values within realistic bounds (<$10,000 for validation)
//! - Return percentages reasonable (±10% max for short validation runs)
//! - P&L matches position changes (accounting validation)
//! - Transaction costs correctly reduce net P&L
//! - P&L accumulation is consistent across epochs
//!
//! # Regression Prevention
//! - Detects P&L explosions (e.g., $640T from preprocessed prices)
//! - Validates accounting consistency (position changes → P&L)
//! - Ensures transaction costs are applied correctly

#![allow(unused_crate_dependencies)]

use anyhow::Result;
use ml::dqn::action_space::{ExposureLevel, FactoredAction, OrderType, Urgency};
use ml::dqn::portfolio_tracker::PortfolioTracker;
use tracing::{info, warn};

// ============================================================================
// Test 1: P&L in Realistic Range
// ============================================================================

#[test]
fn test_pnl_in_realistic_range() -> Result<()> {
    // For a 1-epoch validation run with $10,000 initial capital:
    // Expected P&L: <$10,000 (100% return is extreme but theoretically possible)
    // BUG DETECTOR: $640 trillion P&L indicates preprocessed price leak

    let initial_capital = 10_000.0;
    let mut tracker = PortfolioTracker::new(initial_capital, 0.0001, 1.0);

    // Simulate realistic trading sequence (ES.FUT)
    let price_sequence = vec![
        4500.0,  // Initial price
        4550.0,  // +$50 (+1.1%)
        4525.0,  // -$25 (-0.5%)
        4575.0,  // +$50 (+1.1%)
        4550.0,  // -$25 (-0.5%)
    ];

    for (i, &price) in price_sequence.iter().enumerate() {
        let exposure = match i % 3 {
            0 => ExposureLevel::Long100,
            1 => ExposureLevel::Flat,
            2 => ExposureLevel::Short50,
            _ => ExposureLevel::Flat,
        };

        tracker.execute_action(
            FactoredAction::new(exposure, OrderType::Market, Urgency::Normal),
            price,
            100.0,
        );

        // Check P&L at each step
        let pnl = tracker.unrealized_pnl(price);
        assert!(
            pnl.abs() < 10_000.0,
            "P&L out of realistic range at step {}: {:.2} (expected <$10K)",
            i,
            pnl
        );
    }

    let final_pnl = tracker.unrealized_pnl(*price_sequence.last().unwrap());
    info!(pnl = %format!("{:.2}", final_pnl), "P&L within realistic range (expected <$10,000)");

    // CRITICAL: Detect $640T explosion
    assert!(
        final_pnl.abs() < 1_000_000_000_000.0, // $1 trillion threshold
        "CATASTROPHIC: P&L = ${:.2e} (likely preprocessed price leak)",
        final_pnl
    );

    Ok(())
}

// ============================================================================
// Test 2: P&L Return Percentage Reasonable
// ============================================================================

#[test]
fn test_pnl_return_percentage_reasonable() -> Result<()> {
    let initial_capital = 10_000.0;
    let mut tracker = PortfolioTracker::new(initial_capital, 0.0001, 1.0);

    // Simulate moderate trading (ES.FUT, 10 steps)
    let price_sequence = vec![
        4500.0, 4520.0, 4510.0, 4530.0, 4515.0, 4540.0, 4525.0, 4550.0, 4535.0, 4560.0,
    ];

    for (i, &price) in price_sequence.iter().enumerate() {
        let exposure = if i % 2 == 0 {
            ExposureLevel::Long50
        } else {
            ExposureLevel::Flat
        };

        tracker.execute_action(
            FactoredAction::new(exposure, OrderType::Market, Urgency::Normal),
            price,
            100.0,
        );
    }

    let final_pnl = tracker.unrealized_pnl(*price_sequence.last().unwrap());
    let return_pct = (final_pnl / initial_capital) * 100.0;

    // For validation data (limited time horizon), expect ±10% max
    assert!(
        return_pct.abs() < 10.0,
        "Return percentage too high: {:.2}% (expected ±10% for short validation)",
        return_pct
    );

    info!(return_pct = %format!("{:.2}", return_pct), "Return percentage reasonable (expected +-10%)");

    Ok(())
}

// ============================================================================
// Test 3: P&L Matches Position Changes
// ============================================================================

#[test]
fn test_pnl_matches_position_changes() -> Result<()> {
    let mut tracker = PortfolioTracker::new(10_000.0, 0.0001, 1.0);

    // Test case: Long 100 contracts, price moves from $4500 → $4550 (+$50)
    // Expected P&L: 100 contracts × $50 = $5,000

    let entry_price = 4500.0;
    let exit_price = 4550.0;
    let position_size = 100.0;

    // Open long position
    tracker.execute_action(
        FactoredAction::new(ExposureLevel::Long100, OrderType::Market, Urgency::Normal),
        entry_price,
        position_size,
    );

    // Close position
    tracker.execute_action(
        FactoredAction::new(ExposureLevel::Flat, OrderType::Market, Urgency::Normal),
        exit_price,
        position_size,
    );

    let actual_pnl = tracker.unrealized_pnl(exit_price);
    let expected_pnl = position_size * (exit_price - entry_price);

    // Allow 1% tolerance for rounding
    let tolerance = expected_pnl.abs() * 0.01;
    assert!(
        (actual_pnl - expected_pnl).abs() < tolerance,
        "P&L mismatch: actual={:.2}, expected={:.2} (position × price_change)",
        actual_pnl,
        expected_pnl
    );

    info!(actual_pnl = %format!("{:.2}", actual_pnl), expected_pnl = %format!("{:.2}", expected_pnl), "P&L matches position changes");

    Ok(())
}

// ============================================================================
// Test 4: Transaction Costs Reduce P&L
// ============================================================================

#[test]
fn test_transaction_costs_reduce_pnl() -> Result<()> {
    // This test verifies that transaction costs are applied correctly
    // Expected: Net P&L < Gross P&L (after costs)

    let initial_capital = 10_000.0;
    let spread = 0.0001; // 1 basis point

    let mut tracker = PortfolioTracker::new(initial_capital, spread, 1.0);

    // Execute profitable trade
    let entry_price = 4500.0;
    let exit_price = 4550.0; // +$50 per contract
    let position_size = 100.0;

    // Open position
    tracker.execute_action(
        FactoredAction::new(ExposureLevel::Long100, OrderType::Market, Urgency::Normal),
        entry_price,
        position_size,
    );

    // Close position
    tracker.execute_action(
        FactoredAction::new(ExposureLevel::Flat, OrderType::Market, Urgency::Normal),
        exit_price,
        position_size,
    );

    // Calculate gross P&L (before costs)
    let gross_pnl = position_size * (exit_price - entry_price);

    // Calculate net P&L (after costs, from realized_pnl)
    let net_pnl = tracker.realized_pnl();

    // Transaction costs reduce P&L
    assert!(
        net_pnl < gross_pnl,
        "Net P&L ({:.2}) should be less than gross P&L ({:.2}) due to transaction costs",
        net_pnl,
        gross_pnl
    );

    let cost = gross_pnl - net_pnl;
    info!(gross_pnl = %format!("{:.2}", gross_pnl), net_pnl = %format!("{:.2}", net_pnl), cost = %format!("{:.2}", cost), "Transaction costs applied");

    Ok(())
}

// ============================================================================
// Test 5: P&L Accumulation Correct
// ============================================================================

#[test]
fn test_pnl_accumulation_correct() -> Result<()> {
    // Simulate 5 epochs of trading, verify cumulative P&L consistency
    let initial_capital = 10_000.0;
    let mut tracker = PortfolioTracker::new(initial_capital, 0.0001, 1.0);

    let mut epoch_pnls = Vec::new();

    // 5 epochs, 10 steps each
    for epoch in 0..5 {
        let base_price = 4500.0 + (epoch as f32 * 10.0); // Slight upward trend

        for step in 0..10 {
            let price = base_price + (step as f32 * 2.0);
            let exposure = match step % 3 {
                0 => ExposureLevel::Long50,
                1 => ExposureLevel::Flat,
                2 => ExposureLevel::Short50,
                _ => ExposureLevel::Flat,
            };

            tracker.execute_action(
                FactoredAction::new(exposure, OrderType::Market, Urgency::Normal),
                price,
                100.0,
            );
        }

        // Record epoch P&L
        let epoch_pnl = tracker.unrealized_pnl(base_price + 18.0);
        epoch_pnls.push(epoch_pnl);

        // Check for sudden jumps (>100% change)
        if epoch > 0 {
            let pnl_change = (epoch_pnl - epoch_pnls[epoch - 1]).abs();
            let pnl_change_pct = if epoch_pnls[epoch - 1].abs() > 0.01 {
                (pnl_change / epoch_pnls[epoch - 1].abs()) * 100.0
            } else {
                0.0
            };

            assert!(
                pnl_change_pct < 100.0,
                "Sudden P&L jump at epoch {}: {:.2}% (expected <100%)",
                epoch,
                pnl_change_pct
            );
        }
    }

    info!(epoch_count = epoch_pnls.len(), "P&L accumulation consistent across epochs (no sudden jumps >100%)");
    info!(?epoch_pnls, "Epoch P&Ls");

    Ok(())
}

// ============================================================================
// Test 6: Long Position P&L Calculation
// ============================================================================

#[test]
fn test_long_position_pnl_calculation() -> Result<()> {
    let mut tracker = PortfolioTracker::new(10_000.0, 0.0001, 1.0);

    // Open long position at $4500
    tracker.execute_action(
        FactoredAction::new(ExposureLevel::Long100, OrderType::Market, Urgency::Normal),
        4500.0,
        100.0,
    );

    // Test 1: Price rises to $4600 (+$100 per contract)
    let pnl_up = tracker.unrealized_pnl(4600.0);
    let expected_up = 100.0 * (4600.0 - 4500.0); // +$10,000
    assert!(
        (pnl_up - expected_up).abs() < 1.0,
        "Long position P&L (up): {:.2}, expected {:.2}",
        pnl_up,
        expected_up
    );

    // Test 2: Price falls to $4400 (-$100 per contract)
    let pnl_down = tracker.unrealized_pnl(4400.0);
    let expected_down = 100.0 * (4400.0 - 4500.0); // -$10,000
    assert!(
        (pnl_down - expected_down).abs() < 1.0,
        "Long position P&L (down): {:.2}, expected {:.2}",
        pnl_down,
        expected_down
    );

    info!(pnl_up = %format!("{:.2}", pnl_up), pnl_down = %format!("{:.2}", pnl_down), "Long position P&L");

    Ok(())
}

// ============================================================================
// Test 7: Short Position P&L Calculation
// ============================================================================

#[test]
fn test_short_position_pnl_calculation() -> Result<()> {
    let mut tracker = PortfolioTracker::new(10_000.0, 0.0001, 1.0);

    // Open short position at $4500
    tracker.execute_action(
        FactoredAction::new(ExposureLevel::Short100, OrderType::Market, Urgency::Normal),
        4500.0,
        100.0,
    );

    // Test 1: Price falls to $4400 (+$100 profit per contract for short)
    let pnl_down = tracker.unrealized_pnl(4400.0);
    let expected_down = -100.0 * (4400.0 - 4500.0); // +$10,000
    assert!(
        (pnl_down - expected_down).abs() < 1.0,
        "Short position P&L (down): {:.2}, expected {:.2}",
        pnl_down,
        expected_down
    );

    // Test 2: Price rises to $4600 (-$100 loss per contract for short)
    let pnl_up = tracker.unrealized_pnl(4600.0);
    let expected_up = -100.0 * (4600.0 - 4500.0); // -$10,000
    assert!(
        (pnl_up - expected_up).abs() < 1.0,
        "Short position P&L (up): {:.2}, expected {:.2}",
        pnl_up,
        expected_up
    );

    info!(pnl_down = %format!("{:.2}", pnl_down), pnl_up = %format!("{:.2}", pnl_up), "Short position P&L");

    Ok(())
}

// ============================================================================
// Test 8: P&L Explosion Detector (Regression Prevention)
// ============================================================================

#[test]
fn test_pnl_explosion_detector() -> Result<()> {
    // This test specifically targets the Wave 16M $640 trillion bug
    // Root cause: Preprocessed z-scores (-3 to +3) used as prices

    let mut tracker = PortfolioTracker::new(10_000.0, 0.0001, 1.0);

    // Simulate bug scenario: z-score used as price
    let preprocessed_z_score = -2.5; // This should NEVER be a price

    tracker.execute_action(
        FactoredAction::new(ExposureLevel::Long100, OrderType::Market, Urgency::Normal),
        preprocessed_z_score,
        100.0,
    );

    let pnl = tracker.unrealized_pnl(preprocessed_z_score);

    // BUG DETECTOR: If P&L > $1 billion, preprocessed prices are leaking
    if pnl.abs() > 1_000_000_000.0 {
        panic!(
            "❌ CATASTROPHIC P&L EXPLOSION DETECTED: ${:.2e}\n\
             This indicates preprocessed prices (z-scores) are being used in P&L calculations.\n\
             Root cause: Feature extraction using normalized prices instead of raw prices.",
            pnl
        );
    }

    warn!("Bug detector active: Testing preprocessed price leak");
    warn!("If this test fails, preprocessed prices are contaminating P&L");

    Ok(())
}
