#![allow(
    clippy::tests_outside_test_module,
    clippy::unwrap_used,
    clippy::expect_used,
    clippy::indexing_slicing,
    clippy::str_to_string,
    clippy::string_to_string,
    clippy::assertions_on_result_states,
    clippy::assertions_on_constants,
    clippy::let_underscore_must_use,
    clippy::use_debug,
    clippy::doc_markdown,
    clippy::shadow_unrelated,
    clippy::shadow_reuse,
    clippy::similar_names,
    clippy::clone_on_copy,
    clippy::get_unwrap,
    clippy::modulo_arithmetic,
    clippy::integer_division,
    clippy::non_ascii_literal,
    clippy::useless_vec,
    clippy::useless_format,
    clippy::wildcard_enum_match_arm,
    clippy::manual_range_contains,
    clippy::const_is_empty,
    clippy::needless_range_loop,
    clippy::field_reassign_with_default,
    clippy::items_after_test_module,
    clippy::missing_const_for_fn,
    unused_imports,
    unused_variables,
    unused_mut,
    unused_assignments,
    unused_comparisons,
    unused_must_use,
    dead_code,
)]
//! Concurrency and Race Condition Tests for Trading Engine
//!
//! Tests critical concurrent access patterns in OrderManager, PositionManager,
//! and lockfree queue implementations to ensure thread safety and correct behavior
//! under high contention.

use chrono::Utc;
use common::{OrderId, OrderSide, OrderStatus, OrderType, TimeInForce};
use rust_decimal::Decimal;
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
use tokio::task::JoinSet;
use trading_engine::trading::order_manager::OrderManager;
use trading_engine::trading::position_manager::PositionManager;
use trading_engine::trading_operations::{ExecutionResult, LiquidityFlag, TradingOrder};

// ============================================================================
// Helper Functions
// ============================================================================

fn create_test_order(symbol: &str, quantity: &str, price: &str, side: OrderSide) -> TradingOrder {
    TradingOrder {
        id: OrderId::new(),
        symbol: symbol.to_string(),
        side,
        order_type: OrderType::Limit,
        quantity: Decimal::from_str(quantity).unwrap(),
        price: Decimal::from_str(price).unwrap(),
        time_in_force: TimeInForce::Day,
        account_id: None,
        metadata: HashMap::new(),
        created_at: Utc::now(),
        submitted_at: None,
        executed_at: None,
        status: OrderStatus::Created,
        fill_quantity: Decimal::ZERO,
        average_fill_price: None,
    }
}

fn create_test_execution(
    symbol: &str,
    quantity: Decimal,
    price: Decimal,
    side: OrderSide,
) -> ExecutionResult {
    ExecutionResult {
        order_id: OrderId::new(),
        symbol: symbol.to_string(),
        side,
        executed_quantity: quantity,
        execution_price: price,
        commission: Decimal::from_str("0.01").unwrap(),
        execution_time: Utc::now(),
        liquidity_flag: LiquidityFlag::Maker,
    }
}

// ============================================================================
// OrderManager Concurrency Tests
// ============================================================================

#[cfg(test)]
mod order_manager_concurrency {
    use super::*;

    #[tokio::test]
    async fn test_concurrent_order_submission() {
        let order_manager = Arc::new(OrderManager::new());
        let mut join_set = JoinSet::new();

        // Submit 100 orders concurrently from 10 tasks
        for task_id in 0..10 {
            let om = Arc::clone(&order_manager);
            join_set.spawn(async move {
                for i in 0..10 {
                    let order = create_test_order(
                        "AAPL",
                        "100",
                        &format!("150.{:02}", (task_id * 10 + i) % 100),
                        OrderSide::Buy,
                    );

                    om.validate_order(&order).await.expect("Validation failed");
                    om.add_order(order).await;
                }
            });
        }

        // Wait for all tasks to complete
        while let Some(result) = join_set.join_next().await {
            result.expect("Task panicked");
        }

        // Verify all orders were added (no duplicates, no lost updates)
        // Note: This tests that concurrent adds don't corrupt the HashMap
        let orders = order_manager.get_orders(None).await;
        assert_eq!(orders.len(), 100, "All 100 orders should be tracked");
    }

    #[tokio::test]
    async fn test_concurrent_order_status_updates() {
        let order_manager = Arc::new(OrderManager::new());

        // Add 50 orders
        let mut order_ids = Vec::new();
        for i in 0..50 {
            let order = create_test_order("MSFT", "100", &format!("300.{:02}", i), OrderSide::Buy);
            let order_id = order.id;
            order_manager.add_order(order).await;
            order_ids.push(order_id);
        }

        // Update all order statuses concurrently
        let mut join_set = JoinSet::new();
        for order_id in order_ids.iter() {
            let om = Arc::clone(&order_manager);
            let oid = *order_id;
            join_set.spawn(async move {
                om.update_order_status(&oid, OrderStatus::Submitted)
                    .await
                    .expect("Status update failed");
            });
        }

        while let Some(result) = join_set.join_next().await {
            result.expect("Task panicked");
        }

        // Verify all orders have updated status
        for order_id in order_ids {
            let order = order_manager.get_order(&order_id).await.unwrap();
            assert_eq!(order.status, OrderStatus::Submitted);
        }
    }

    #[tokio::test]
    async fn test_concurrent_read_write_orders() {
        let order_manager = Arc::new(OrderManager::new());

        // Add initial orders
        let mut order_ids = Vec::new();
        for i in 0..20 {
            let order = create_test_order("GOOGL", "50", &format!("2800.{:02}", i), OrderSide::Buy);
            let order_id = order.id;
            order_manager.add_order(order).await;
            order_ids.push(order_id);
        }

        let mut join_set = JoinSet::new();

        // Writers: Update order statuses
        for order_id in order_ids.iter().take(10) {
            let om = Arc::clone(&order_manager);
            let oid = *order_id;
            join_set.spawn(async move {
                for _ in 0..5 {
                    let _ = om
                        .update_order_status(&oid, OrderStatus::PartiallyFilled)
                        .await;
                    tokio::time::sleep(Duration::from_micros(10)).await;
                }
            });
        }

        // Readers: Query orders repeatedly
        for order_id in order_ids.iter().skip(10) {
            let om = Arc::clone(&order_manager);
            let oid = *order_id;
            join_set.spawn(async move {
                for _ in 0..10 {
                    let _ = om.get_order(&oid).await;
                    tokio::time::sleep(Duration::from_micros(5)).await;
                }
            });
        }

        // Wait for all operations
        while let Some(result) = join_set.join_next().await {
            result.expect("Task panicked");
        }
    }

    #[tokio::test]
    async fn test_duplicate_order_id_concurrent() {
        let order_manager = Arc::new(OrderManager::new());
        let order = create_test_order("TSLA", "10", "250.00", OrderSide::Buy);
        let order_id = order.id;

        // Add the order once
        order_manager.add_order(order.clone()).await;

        let mut join_set = JoinSet::new();

        // Try to add duplicate order from multiple tasks
        for _ in 0..5 {
            let om = Arc::clone(&order_manager);
            let mut dup_order = order.clone();
            dup_order.id = order_id; // Force same ID

            join_set.spawn(async move { om.validate_order(&dup_order).await });
        }

        let mut validation_failures = 0;
        while let Some(result) = join_set.join_next().await {
            if result.unwrap().is_err() {
                validation_failures += 1;
            }
        }

        // All duplicate attempts should fail validation
        assert!(
            validation_failures >= 4,
            "Duplicate detection should catch concurrent submissions"
        );
    }
}

// ============================================================================
// PositionManager Concurrency Tests
// ============================================================================

#[cfg(test)]
mod position_manager_concurrency {
    use super::*;

    #[tokio::test]
    async fn test_concurrent_position_updates_same_symbol() {
        let pm = Arc::new(PositionManager::new());
        let mut join_set = JoinSet::new();

        // Execute 100 trades concurrently for the same symbol
        for i in 0..100 {
            let position_manager = Arc::clone(&pm);
            join_set.spawn(async move {
                let exec = create_test_execution(
                    "AAPL",
                    Decimal::from_str("10").unwrap(),
                    Decimal::from_str(&format!("150.{:02}", i % 100)).unwrap(),
                    OrderSide::Buy,
                );
                position_manager.update_position(&exec)
            });
        }

        // Collect results
        let mut success_count = 0;
        while let Some(result) = join_set.join_next().await {
            if result.unwrap().is_ok() {
                success_count += 1;
            }
        }

        assert_eq!(success_count, 100, "All position updates should succeed");

        // Verify final position quantity (100 trades * 10 shares each)
        let position = pm.get_position("AAPL").unwrap();
        assert_eq!(
            position.quantity,
            Decimal::from_str("1000").unwrap(),
            "Total quantity should be sum of all trades"
        );
    }

    #[tokio::test]
    async fn test_concurrent_position_updates_different_symbols() {
        let pm = Arc::new(PositionManager::new());
        let symbols = vec!["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA"];
        let mut join_set = JoinSet::new();

        // Create 50 executions (10 per symbol) concurrently
        for symbol in symbols.iter() {
            for i in 0..10 {
                let position_manager = Arc::clone(&pm);
                let sym = symbol.to_string();
                join_set.spawn(async move {
                    let exec = create_test_execution(
                        &sym,
                        Decimal::from_str("10").unwrap(),
                        Decimal::from_str(&format!("100.{:02}", i)).unwrap(),
                        OrderSide::Buy,
                    );
                    position_manager.update_position(&exec)
                });
            }
        }

        // Wait for all updates
        while let Some(result) = join_set.join_next().await {
            result.expect("Task panicked").expect("Update failed");
        }

        // Verify each symbol has correct position
        for symbol in symbols {
            let position = pm.get_position(symbol).unwrap();
            assert_eq!(
                position.quantity,
                Decimal::from_str("100").unwrap(),
                "Each symbol should have 10 trades * 10 shares"
            );
        }
    }

    #[tokio::test]
    async fn test_position_reversal_under_concurrency() {
        let pm = Arc::new(PositionManager::new());

        // First establish a long position
        let initial_exec = create_test_execution(
            "AMD",
            Decimal::from_str("100").unwrap(),
            Decimal::from_str("120.00").unwrap(),
            OrderSide::Buy,
        );
        pm.update_position(&initial_exec).unwrap();

        // Now concurrently execute sells that reverse the position
        let mut join_set = JoinSet::new();
        for i in 0..15 {
            let position_manager = Arc::clone(&pm);
            join_set.spawn(async move {
                let exec = create_test_execution(
                    "AMD",
                    Decimal::from_str("10").unwrap(),
                    Decimal::from_str(&format!("121.{:02}", i)).unwrap(),
                    OrderSide::Sell,
                );
                position_manager.update_position(&exec)
            });
        }

        while let Some(result) = join_set.join_next().await {
            result.expect("Task panicked").expect("Update failed");
        }

        // Position should be net short: 100 - 150 = -50
        let position = pm.get_position("AMD").unwrap();
        assert_eq!(
            position.quantity,
            Decimal::from_str("-50").unwrap(),
            "Position should reverse from long to short"
        );
    }

    #[tokio::test]
    async fn test_concurrent_read_write_positions() {
        let pm = Arc::new(PositionManager::new());

        // Initialize position
        let exec = create_test_execution(
            "NVDA",
            Decimal::from_str("100").unwrap(),
            Decimal::from_str("500.00").unwrap(),
            OrderSide::Buy,
        );
        pm.update_position(&exec).unwrap();

        let mut join_set = JoinSet::new();

        // Writers: Update positions
        for i in 0..20 {
            let position_manager = Arc::clone(&pm);
            join_set.spawn(async move {
                let exec = create_test_execution(
                    "NVDA",
                    Decimal::from_str("5").unwrap(),
                    Decimal::from_str(&format!("501.{:02}", i)).unwrap(),
                    OrderSide::Buy,
                );
                position_manager.update_position(&exec)
            });
        }

        // Readers: Query positions repeatedly
        for _ in 0..30 {
            let position_manager = Arc::clone(&pm);
            join_set.spawn(async move {
                for _ in 0..5 {
                    let _ = position_manager.get_position("NVDA");
                    tokio::time::sleep(Duration::from_micros(10)).await;
                }
                Ok::<(), String>(())
            });
        }

        // Collect all results
        while let Some(result) = join_set.join_next().await {
            result.expect("Task panicked").expect("Operation failed");
        }
    }

    #[tokio::test]
    async fn test_position_zero_crossing_concurrent() {
        let pm = Arc::new(PositionManager::new());

        // Start with a position
        let initial = create_test_execution(
            "INTC",
            Decimal::from_str("50").unwrap(),
            Decimal::from_str("45.00").unwrap(),
            OrderSide::Buy,
        );
        pm.update_position(&initial).unwrap();

        let mut join_set = JoinSet::new();

        // Concurrently execute sells that bring position to ~zero
        for i in 0..5 {
            let position_manager = Arc::clone(&pm);
            join_set.spawn(async move {
                let exec = create_test_execution(
                    "INTC",
                    Decimal::from_str("10").unwrap(),
                    Decimal::from_str(&format!("45.{:02}", i)).unwrap(),
                    OrderSide::Sell,
                );
                position_manager.update_position(&exec)
            });
        }

        while let Some(result) = join_set.join_next().await {
            result.expect("Task panicked").expect("Update failed");
        }

        // Position should be zero or near-zero
        let position = pm.get_position("INTC").unwrap();
        assert_eq!(
            position.quantity,
            Decimal::ZERO,
            "Position should be flat after equal buy/sell"
        );
    }
}

// ============================================================================
// Edge Case Tests
// ============================================================================

#[cfg(test)]
mod edge_case_tests {
    use super::*;

    #[test]
    fn test_position_rounding_accumulation() {
        let pm = PositionManager::new();

        // Execute many small trades with prices that might cause rounding issues
        for i in 0..1000 {
            let exec = create_test_execution(
                "TEST",
                Decimal::from_str("0.001").unwrap(),
                Decimal::from_str(&format!("100.{:03}", i % 1000)).unwrap(),
                OrderSide::Buy,
            );
            pm.update_position(&exec).unwrap();
        }

        let position = pm.get_position("TEST").unwrap();

        // Verify total quantity is correct (no rounding drift)
        assert_eq!(
            position.quantity,
            Decimal::from_str("1.000").unwrap(),
            "Rounding errors should not accumulate"
        );
    }

    #[tokio::test]
    async fn test_order_manager_empty_symbol_rejection() {
        let om = OrderManager::new();
        let mut order = create_test_order("", "100", "150.00", OrderSide::Buy);
        order.symbol = String::new();

        let result = om.validate_order(&order).await;
        assert!(result.is_err(), "Empty symbol should be rejected");
        assert!(result.unwrap_err().contains("symbol"));
    }

    #[tokio::test]
    async fn test_order_manager_zero_quantity_rejection() {
        let om = OrderManager::new();
        let mut order = create_test_order("AAPL", "0", "150.00", OrderSide::Buy);
        order.quantity = Decimal::ZERO;

        let result = om.validate_order(&order).await;
        assert!(result.is_err(), "Zero quantity should be rejected");
        assert!(result.unwrap_err().contains("quantity"));
    }

    #[tokio::test]
    async fn test_order_manager_negative_quantity_rejection() {
        let om = OrderManager::new();
        let mut order = create_test_order("AAPL", "-100", "150.00", OrderSide::Buy);
        order.quantity = Decimal::from_str("-100").unwrap();

        let result = om.validate_order(&order).await;
        assert!(result.is_err(), "Negative quantity should be rejected");
    }

    #[tokio::test]
    async fn test_order_manager_zero_price_limit_order_rejection() {
        let om = OrderManager::new();
        let mut order = create_test_order("AAPL", "100", "0", OrderSide::Buy);
        order.order_type = OrderType::Limit;
        order.price = Decimal::ZERO;

        let result = om.validate_order(&order).await;
        assert!(result.is_err(), "Zero price limit order should be rejected");
        assert!(result.unwrap_err().contains("price"));
    }

    #[tokio::test]
    async fn test_order_status_transition_invalid() {
        let om = Arc::new(OrderManager::new());
        let order = create_test_order("AAPL", "100", "150.00", OrderSide::Buy);
        let order_id = order.id;

        om.add_order(order).await;

        // Fill the order
        om.update_order_status(&order_id, OrderStatus::Filled)
            .await
            .unwrap();

        // Try to transition from Filled to Cancelled (invalid)
        let _result = om
            .update_order_status(&order_id, OrderStatus::Cancelled)
            .await;

        // Note: Current implementation doesn't validate transitions
        // This test documents current behavior and would catch if we add validation
        // In production, this should be rejected
    }

    #[test]
    fn test_position_large_quantity() {
        let pm = PositionManager::new();

        // Test with very large position size
        let exec = create_test_execution(
            "BIGPOS",
            Decimal::from_str("1000000").unwrap(),
            Decimal::from_str("100.00").unwrap(),
            OrderSide::Buy,
        );

        let result = pm.update_position(&exec);
        assert!(result.is_ok(), "Large position should be handled");

        let position = pm.get_position("BIGPOS").unwrap();
        assert_eq!(position.quantity, Decimal::from_str("1000000").unwrap());
    }

    #[test]
    fn test_position_high_precision_price() {
        let pm = PositionManager::new();

        // Test with high precision price (e.g., crypto)
        let exec = create_test_execution(
            "CRYPTO",
            Decimal::from_str("0.00123456").unwrap(),
            Decimal::from_str("45678.9012345").unwrap(),
            OrderSide::Buy,
        );

        let result = pm.update_position(&exec);
        assert!(result.is_ok(), "High precision should be handled");

        let position = pm.get_position("CRYPTO").unwrap();
        assert_eq!(position.quantity, Decimal::from_str("0.00123456").unwrap());
    }
}

// ============================================================================
// Error Recovery Tests
// ============================================================================

#[cfg(test)]
mod error_recovery_tests {
    use super::*;

    #[tokio::test]
    async fn test_order_manager_update_nonexistent_order() {
        let om = OrderManager::new();
        let fake_id = OrderId::new();

        let result = om.update_order_status(&fake_id, OrderStatus::Filled).await;
        assert!(result.is_err(), "Updating nonexistent order should fail");
        assert!(result.unwrap_err().contains("not found"));
    }

    #[tokio::test]
    async fn test_order_manager_get_nonexistent_order() {
        let om = OrderManager::new();
        let fake_id = OrderId::new();

        let result = om.get_order(&fake_id).await;
        assert!(
            result.is_none(),
            "Getting nonexistent order should return None"
        );
    }

    #[test]
    fn test_position_manager_get_nonexistent_position() {
        let pm = PositionManager::new();

        let result = pm.get_position("NONEXISTENT");
        assert!(
            result.is_none(),
            "Getting nonexistent position should return None"
        );
    }

    #[test]
    fn test_position_manager_lock_error_recovery() {
        // This test verifies that lock errors are propagated correctly
        // In practice, lock poisoning is rare and usually indicates a panic
        let pm = PositionManager::new();

        let exec = create_test_execution(
            "LOCKTEST",
            Decimal::from_str("100").unwrap(),
            Decimal::from_str("150.00").unwrap(),
            OrderSide::Buy,
        );

        let result = pm.update_position(&exec);
        assert!(result.is_ok(), "Normal lock acquisition should succeed");
    }

    #[tokio::test]
    async fn test_concurrent_operation_resilience() {
        // Test that the system remains functional even if some operations fail
        let om = Arc::new(OrderManager::new());
        let mut join_set = JoinSet::new();

        // Mix of valid and invalid operations
        for i in 0..20 {
            let order_manager = Arc::clone(&om);
            join_set.spawn(async move {
                if i % 3 == 0 {
                    // Invalid order (empty symbol)
                    let mut order = create_test_order("", "100", "150.00", OrderSide::Buy);
                    order.symbol = String::new();
                    order_manager.validate_order(&order).await
                } else {
                    // Valid order
                    let order =
                        create_test_order(&format!("SYM{}", i), "100", "150.00", OrderSide::Buy);
                    order_manager.validate_order(&order).await?;
                    order_manager.add_order(order).await;
                    Ok(())
                }
            });
        }

        let mut success_count = 0;
        let mut failure_count = 0;

        while let Some(result) = join_set.join_next().await {
            match result.unwrap() {
                Ok(_) => success_count += 1,
                Err(_) => failure_count += 1,
            }
        }

        // Should have roughly 1/3 failures and 2/3 successes
        assert!(success_count > 0, "Some operations should succeed");
        assert!(failure_count > 0, "Some operations should fail");
        assert_eq!(
            success_count + failure_count,
            20,
            "All operations should complete"
        );
    }
}
