#![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,
)]
//! Advanced Order Types Tests
//!
//! Comprehensive test suite for advanced order type semantics:
//! - IOC (Immediate-Or-Cancel): Execute immediately, cancel remainder
//! - FOK (Fill-Or-Kill): Execute completely or reject entirely
//! - Iceberg Orders: Display quantity with hidden reserves
//! - Post-Only Orders: Maker-only execution, no taker fills
//! - GTD (Good-Till-Date): Time-based expiration handling
//!
//! Note: These tests simulate advanced order type behavior since the current
//! OrderManager implementation doesn't have specialized logic for each type.
//! These tests serve as specifications for future implementation.

use chrono::{Duration, Timelike, Utc};
use common::{OrderId, OrderSide, OrderStatus, OrderType, TimeInForce};
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use std::collections::HashMap;
use trading_engine::trading::order_manager::OrderManager;
use trading_engine::trading_operations::{ExecutionResult, LiquidityFlag, TradingOrder};

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

fn create_order_with_tif(
    id: &str,
    symbol: &str,
    side: OrderSide,
    quantity: Decimal,
    price: Decimal,
    tif: TimeInForce,
) -> TradingOrder {
    TradingOrder {
        id: id.to_string().into(),
        symbol: symbol.to_string(),
        side,
        order_type: OrderType::Limit,
        quantity,
        price,
        time_in_force: tif,
        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_iceberg_order(
    id: &str,
    symbol: &str,
    side: OrderSide,
    total_quantity: Decimal,
    display_quantity: Decimal,
    price: Decimal,
) -> TradingOrder {
    let mut order = create_order_with_tif(
        id,
        symbol,
        side,
        total_quantity,
        price,
        TimeInForce::GoodTillCancel,
    );

    // Store iceberg parameters in metadata
    order
        .metadata
        .insert("iceberg".to_string(), "true".to_string());
    order
        .metadata
        .insert("display_quantity".to_string(), display_quantity.to_string());
    order
        .metadata
        .insert("total_quantity".to_string(), total_quantity.to_string());

    order
}

fn create_post_only_order(
    id: &str,
    symbol: &str,
    side: OrderSide,
    quantity: Decimal,
    price: Decimal,
) -> TradingOrder {
    let mut order = create_order_with_tif(
        id,
        symbol,
        side,
        quantity,
        price,
        TimeInForce::GoodTillCancel,
    );

    order
        .metadata
        .insert("post_only".to_string(), "true".to_string());
    order
}

fn create_execution(
    order_id: OrderId,
    symbol: &str,
    quantity: Decimal,
    price: Decimal,
    liquidity: LiquidityFlag,
) -> ExecutionResult {
    ExecutionResult {
        order_id,
        symbol: symbol.to_string(),
        side: OrderSide::Buy,
        executed_quantity: quantity,
        execution_price: price,
        execution_time: Utc::now(),
        commission: dec!(10.0),
        liquidity_flag: liquidity,
    }
}

// =============================================================================
// 1. IOC (Immediate-Or-Cancel) Order Tests (6 tests)
// =============================================================================

#[tokio::test]
async fn test_ioc_full_fill_immediate_execution() {
    let manager = OrderManager::new();

    // IOC order for 10 BTC - full liquidity available
    let ioc_order = create_order_with_tif(
        "IOC001",
        "BTCUSD",
        OrderSide::Buy,
        dec!(10.0),
        dec!(45000.00),
        TimeInForce::ImmediateOrCancel,
    );

    manager.add_order(ioc_order.clone()).await;

    // Simulate immediate full fill
    let execution = create_execution(
        ioc_order.id.clone(),
        "BTCUSD",
        dec!(10.0),
        dec!(45000.00),
        LiquidityFlag::Taker,
    );

    let result = manager.process_execution(&execution).await;
    assert!(result.is_ok(), "IOC full fill should succeed");

    let updated = manager
        .get_order(&ioc_order.id)
        .await
        .expect("Order should exist");
    assert_eq!(updated.fill_quantity, dec!(10.0), "Should be fully filled");
    assert_eq!(
        updated.status,
        OrderStatus::Filled,
        "Status should be Filled"
    );
    assert_eq!(updated.time_in_force, TimeInForce::ImmediateOrCancel);
}

#[tokio::test]
async fn test_ioc_partial_fill_cancel_remainder() {
    let manager = OrderManager::new();

    // IOC order for 15 BTC, only 10 BTC available
    let ioc_order = create_order_with_tif(
        "IOC002",
        "BTCUSD",
        OrderSide::Buy,
        dec!(15.0),
        dec!(45000.00),
        TimeInForce::ImmediateOrCancel,
    );

    manager.add_order(ioc_order.clone()).await;

    // Simulate partial fill of 10 BTC (5 BTC should be cancelled)
    let execution = create_execution(
        ioc_order.id.clone(),
        "BTCUSD",
        dec!(10.0),
        dec!(45000.00),
        LiquidityFlag::Taker,
    );

    let result = manager.process_execution(&execution).await;
    assert!(result.is_ok(), "IOC partial fill should succeed");

    let updated = manager
        .get_order(&ioc_order.id)
        .await
        .expect("Order should exist");
    assert_eq!(
        updated.fill_quantity,
        dec!(10.0),
        "Should have partial fill"
    );
    assert_eq!(
        updated.status,
        OrderStatus::PartiallyFilled,
        "Status should be PartiallyFilled"
    );

    // In a real implementation, remainder would be auto-cancelled
    // Here we simulate the cancellation
    manager
        .cancel_order(&ioc_order.id)
        .await
        .expect("Should cancel remainder");

    let cancelled = manager
        .get_order(&ioc_order.id)
        .await
        .expect("Order should exist");
    assert_eq!(
        cancelled.status,
        OrderStatus::Cancelled,
        "Remainder should be cancelled"
    );
}

#[tokio::test]
async fn test_ioc_no_fill_immediate_cancel() {
    let manager = OrderManager::new();

    // IOC order with no matching liquidity
    let ioc_order = create_order_with_tif(
        "IOC003",
        "BTCUSD",
        OrderSide::Buy,
        dec!(10.0),
        dec!(45000.00),
        TimeInForce::ImmediateOrCancel,
    );

    manager.add_order(ioc_order.clone()).await;

    // No execution - should be cancelled immediately
    // In real implementation, exchange would reject/cancel
    let result = manager.cancel_order(&ioc_order.id).await;
    assert!(result.is_ok(), "IOC with no fill should be cancellable");

    let cancelled = manager
        .get_order(&ioc_order.id)
        .await
        .expect("Order should exist");
    assert_eq!(cancelled.status, OrderStatus::Cancelled);
    assert_eq!(
        cancelled.fill_quantity,
        Decimal::ZERO,
        "Should have zero fills"
    );
}

#[tokio::test]
async fn test_ioc_time_priority_with_multiple_orders() {
    let manager = OrderManager::new();

    // Earlier IOC order
    let ioc1 = create_order_with_tif(
        "IOC004",
        "ETHUSD",
        OrderSide::Buy,
        dec!(5.0),
        dec!(3000.00),
        TimeInForce::ImmediateOrCancel,
    );
    manager.add_order(ioc1.clone()).await;

    // Later IOC order at same price
    let ioc2 = create_order_with_tif(
        "IOC005",
        "ETHUSD",
        OrderSide::Buy,
        dec!(5.0),
        dec!(3000.00),
        TimeInForce::ImmediateOrCancel,
    );
    manager.add_order(ioc2.clone()).await;

    // First order should get priority for execution
    let exec1 = create_execution(
        ioc1.id.clone(),
        "ETHUSD",
        dec!(5.0),
        dec!(3000.00),
        LiquidityFlag::Taker,
    );
    manager
        .process_execution(&exec1)
        .await
        .expect("First IOC should execute");

    let filled = manager
        .get_order(&ioc1.id)
        .await
        .expect("Order should exist");
    assert_eq!(filled.status, OrderStatus::Filled);
    assert!(
        filled.created_at < ioc2.created_at,
        "Earlier order has time priority"
    );
}

#[tokio::test]
async fn test_ioc_multi_symbol_execution() {
    let manager = OrderManager::new();

    // IOC orders on different symbols
    let ioc_btc = create_order_with_tif(
        "IOC006",
        "BTCUSD",
        OrderSide::Sell,
        dec!(2.0),
        dec!(46000.00),
        TimeInForce::ImmediateOrCancel,
    );

    let ioc_eth = create_order_with_tif(
        "IOC007",
        "ETHUSD",
        OrderSide::Sell,
        dec!(10.0),
        dec!(3100.00),
        TimeInForce::ImmediateOrCancel,
    );

    manager.add_order(ioc_btc.clone()).await;
    manager.add_order(ioc_eth.clone()).await;

    // Execute BTC order
    let exec_btc = create_execution(
        ioc_btc.id.clone(),
        "BTCUSD",
        dec!(2.0),
        dec!(46000.00),
        LiquidityFlag::Taker,
    );
    manager
        .process_execution(&exec_btc)
        .await
        .expect("BTC IOC should execute");

    // Execute ETH order
    let exec_eth = create_execution(
        ioc_eth.id.clone(),
        "ETHUSD",
        dec!(10.0),
        dec!(3100.00),
        LiquidityFlag::Taker,
    );
    manager
        .process_execution(&exec_eth)
        .await
        .expect("ETH IOC should execute");

    let btc_order = manager
        .get_order(&ioc_btc.id)
        .await
        .expect("BTC order exists");
    let eth_order = manager
        .get_order(&ioc_eth.id)
        .await
        .expect("ETH order exists");

    assert_eq!(btc_order.status, OrderStatus::Filled);
    assert_eq!(eth_order.status, OrderStatus::Filled);
}

#[tokio::test]
async fn test_ioc_price_improvement_execution() {
    let manager = OrderManager::new();

    // IOC buy at $45000, gets filled at better price $44900
    let ioc_order = create_order_with_tif(
        "IOC008",
        "BTCUSD",
        OrderSide::Buy,
        dec!(5.0),
        dec!(45000.00),
        TimeInForce::ImmediateOrCancel,
    );

    manager.add_order(ioc_order.clone()).await;

    // Execute with price improvement
    let execution = create_execution(
        ioc_order.id.clone(),
        "BTCUSD",
        dec!(5.0),
        dec!(44900.00), // Better price than limit
        LiquidityFlag::Taker,
    );

    manager
        .process_execution(&execution)
        .await
        .expect("IOC with price improvement");

    let filled = manager
        .get_order(&ioc_order.id)
        .await
        .expect("Order should exist");
    assert_eq!(filled.average_fill_price, Some(dec!(44900.00)));
    assert!(
        filled.average_fill_price.unwrap() < ioc_order.price,
        "Got price improvement"
    );
}

// =============================================================================
// 2. FOK (Fill-Or-Kill) Order Tests (6 tests)
// =============================================================================

#[tokio::test]
async fn test_fok_full_fill_success() {
    let manager = OrderManager::new();

    // FOK order for 10 BTC - sufficient liquidity available
    let fok_order = create_order_with_tif(
        "FOK001",
        "BTCUSD",
        OrderSide::Buy,
        dec!(10.0),
        dec!(45000.00),
        TimeInForce::FillOrKill,
    );

    manager.add_order(fok_order.clone()).await;

    // Atomic full fill
    let execution = create_execution(
        fok_order.id.clone(),
        "BTCUSD",
        dec!(10.0),
        dec!(45000.00),
        LiquidityFlag::Taker,
    );

    let result = manager.process_execution(&execution).await;
    assert!(result.is_ok(), "FOK full fill should succeed");

    let filled = manager
        .get_order(&fok_order.id)
        .await
        .expect("Order should exist");
    assert_eq!(filled.fill_quantity, dec!(10.0));
    assert_eq!(filled.status, OrderStatus::Filled);
    assert_eq!(filled.time_in_force, TimeInForce::FillOrKill);
}

#[tokio::test]
async fn test_fok_partial_fill_rejected() {
    let manager = OrderManager::new();

    // FOK order for 15 BTC, only 10 BTC available - should be rejected
    let fok_order = create_order_with_tif(
        "FOK002",
        "BTCUSD",
        OrderSide::Buy,
        dec!(15.0),
        dec!(45000.00),
        TimeInForce::FillOrKill,
    );

    manager.add_order(fok_order.clone()).await;

    // Attempt partial fill - FOK should reject
    // In real implementation, exchange would reject before any fill
    // Here we simulate rejection by updating status
    let result = manager
        .update_order_status(&fok_order.id, OrderStatus::Rejected)
        .await;
    assert!(
        result.is_ok(),
        "FOK with insufficient liquidity should be rejected"
    );

    let rejected = manager
        .get_order(&fok_order.id)
        .await
        .expect("Order should exist");
    assert_eq!(rejected.status, OrderStatus::Rejected);
    assert_eq!(
        rejected.fill_quantity,
        Decimal::ZERO,
        "FOK should not partial fill"
    );
}

#[tokio::test]
async fn test_fok_insufficient_liquidity_rejection() {
    let manager = OrderManager::new();

    // FOK order with no matching liquidity
    let fok_order = create_order_with_tif(
        "FOK003",
        "ETHUSD",
        OrderSide::Sell,
        dec!(20.0),
        dec!(3000.00),
        TimeInForce::FillOrKill,
    );

    manager.add_order(fok_order.clone()).await;

    // Simulate rejection due to insufficient liquidity
    manager
        .update_order_status(&fok_order.id, OrderStatus::Rejected)
        .await
        .expect("Should reject FOK");

    let rejected = manager
        .get_order(&fok_order.id)
        .await
        .expect("Order should exist");
    assert_eq!(rejected.status, OrderStatus::Rejected);
    assert!(rejected.executed_at.is_none(), "FOK should not execute");
}

#[tokio::test]
async fn test_fok_atomic_execution_guarantee() {
    let manager = OrderManager::new();

    // FOK order must execute atomically (all-or-nothing)
    let fok_order = create_order_with_tif(
        "FOK004",
        "BTCUSD",
        OrderSide::Buy,
        dec!(8.0),
        dec!(45500.00),
        TimeInForce::FillOrKill,
    );

    manager.add_order(fok_order.clone()).await;

    // Atomic execution - either fills completely or not at all
    let execution = create_execution(
        fok_order.id.clone(),
        "BTCUSD",
        dec!(8.0), // Must be exact quantity
        dec!(45500.00),
        LiquidityFlag::Taker,
    );

    manager
        .process_execution(&execution)
        .await
        .expect("Atomic FOK execution");

    let filled = manager
        .get_order(&fok_order.id)
        .await
        .expect("Order should exist");
    assert_eq!(
        filled.fill_quantity, fok_order.quantity,
        "Must fill exact quantity"
    );
    assert_eq!(filled.status, OrderStatus::Filled, "Must be fully filled");
}

#[tokio::test]
async fn test_fok_multi_level_liquidity_check() {
    let manager = OrderManager::new();

    // FOK order that would need multiple price levels
    // If insufficient liquidity at single level, reject
    let fok_order = create_order_with_tif(
        "FOK005",
        "ETHUSD",
        OrderSide::Buy,
        dec!(50.0), // Large order
        dec!(3100.00),
        TimeInForce::FillOrKill,
    );

    manager.add_order(fok_order.clone()).await;

    // Simulate check: Only 30 ETH available at $3100
    // FOK requires all 50 ETH - should reject
    manager
        .update_order_status(&fok_order.id, OrderStatus::Rejected)
        .await
        .expect("FOK rejects on insufficient liquidity");

    let rejected = manager
        .get_order(&fok_order.id)
        .await
        .expect("Order should exist");
    assert_eq!(rejected.status, OrderStatus::Rejected);
    assert_eq!(rejected.fill_quantity, Decimal::ZERO);
}

#[tokio::test]
async fn test_fok_vs_ioc_behavior_difference() {
    let manager = OrderManager::new();

    // IOC: Partial fill acceptable
    let ioc_order = create_order_with_tif(
        "IOC009",
        "BTCUSD",
        OrderSide::Buy,
        dec!(15.0),
        dec!(45000.00),
        TimeInForce::ImmediateOrCancel,
    );

    // FOK: Must fill completely or reject
    let fok_order = create_order_with_tif(
        "FOK006",
        "BTCUSD",
        OrderSide::Buy,
        dec!(15.0),
        dec!(45000.00),
        TimeInForce::FillOrKill,
    );

    manager.add_order(ioc_order.clone()).await;
    manager.add_order(fok_order.clone()).await;

    // Scenario: Only 10 BTC available
    // IOC: Partial fill 10 BTC, cancel 5 BTC
    let ioc_exec = create_execution(
        ioc_order.id.clone(),
        "BTCUSD",
        dec!(10.0),
        dec!(45000.00),
        LiquidityFlag::Taker,
    );
    manager
        .process_execution(&ioc_exec)
        .await
        .expect("IOC partial fill");

    // FOK: Reject entire order
    manager
        .update_order_status(&fok_order.id, OrderStatus::Rejected)
        .await
        .expect("FOK rejects");

    let ioc_filled = manager.get_order(&ioc_order.id).await.expect("IOC exists");
    let fok_rejected = manager.get_order(&fok_order.id).await.expect("FOK exists");

    assert_eq!(ioc_filled.fill_quantity, dec!(10.0), "IOC accepts partial");
    assert_eq!(
        fok_rejected.fill_quantity,
        Decimal::ZERO,
        "FOK rejects partial"
    );
    assert_eq!(fok_rejected.status, OrderStatus::Rejected);
}

// =============================================================================
// 3. Iceberg Order Tests (6 tests)
// =============================================================================

#[tokio::test]
async fn test_iceberg_order_display_quantity() {
    let manager = OrderManager::new();

    // Iceberg: 100 BTC total, show only 10 BTC
    let iceberg = create_iceberg_order(
        "ICE001",
        "BTCUSD",
        OrderSide::Buy,
        dec!(100.0), // Total
        dec!(10.0),  // Display
        dec!(45000.00),
    );

    manager.add_order(iceberg.clone()).await;

    let stored = manager
        .get_order(&iceberg.id)
        .await
        .expect("Order should exist");
    assert_eq!(stored.metadata.get("iceberg"), Some(&"true".to_string()));

    // Verify display quantity is stored (flexible format check)
    let display_qty = stored
        .metadata
        .get("display_quantity")
        .expect("Should have display_quantity");
    let display_decimal: Decimal = display_qty.parse().expect("Should parse as Decimal");
    assert_eq!(display_decimal, dec!(10.0));

    // Verify total quantity is stored (flexible format check)
    let total_qty = stored
        .metadata
        .get("total_quantity")
        .expect("Should have total_quantity");
    let total_decimal: Decimal = total_qty.parse().expect("Should parse as Decimal");
    assert_eq!(total_decimal, dec!(100.0));
}

#[tokio::test]
async fn test_iceberg_order_replenishment() {
    let manager = OrderManager::new();

    // Iceberg: 100 BTC total, 10 BTC display
    let iceberg = create_iceberg_order(
        "ICE002",
        "BTCUSD",
        OrderSide::Buy,
        dec!(100.0),
        dec!(10.0),
        dec!(45000.00),
    );

    manager.add_order(iceberg.clone()).await;

    // Fill 10 BTC (display quantity)
    let exec1 = create_execution(
        iceberg.id.clone(),
        "BTCUSD",
        dec!(10.0),
        dec!(45000.00),
        LiquidityFlag::Maker,
    );
    manager.process_execution(&exec1).await.expect("First fill");

    let after_first = manager.get_order(&iceberg.id).await.expect("Order exists");
    assert_eq!(after_first.fill_quantity, dec!(10.0));
    assert_eq!(after_first.status, OrderStatus::PartiallyFilled);

    // Next 10 BTC should be visible (replenishment)
    // In real implementation, order book would show next display quantity

    // Fill another 10 BTC
    let exec2 = create_execution(
        iceberg.id.clone(),
        "BTCUSD",
        dec!(10.0),
        dec!(45000.00),
        LiquidityFlag::Maker,
    );
    manager
        .process_execution(&exec2)
        .await
        .expect("Second fill");

    let after_second = manager.get_order(&iceberg.id).await.expect("Order exists");
    assert_eq!(after_second.fill_quantity, dec!(20.0));
    assert_eq!(after_second.status, OrderStatus::PartiallyFilled);
}

#[tokio::test]
async fn test_iceberg_hidden_liquidity_management() {
    let manager = OrderManager::new();

    // Iceberg hides 90 BTC, shows 10 BTC
    let iceberg = create_iceberg_order(
        "ICE003",
        "ETHUSD",
        OrderSide::Sell,
        dec!(100.0),
        dec!(10.0),
        dec!(3000.00),
    );

    manager.add_order(iceberg.clone()).await;

    // Hidden quantity = Total - Display = 90 BTC
    let total: Decimal = iceberg
        .metadata
        .get("total_quantity")
        .unwrap()
        .parse()
        .unwrap();
    let display: Decimal = iceberg
        .metadata
        .get("display_quantity")
        .unwrap()
        .parse()
        .unwrap();
    let hidden = total - display;

    assert_eq!(hidden, dec!(90.0), "90 BTC should be hidden");
    assert_eq!(display, dec!(10.0), "10 BTC should be visible");
}

#[tokio::test]
async fn test_iceberg_price_time_priority() {
    let manager = OrderManager::new();

    // Iceberg order gets price-time priority for displayed quantity
    let iceberg = create_iceberg_order(
        "ICE004",
        "BTCUSD",
        OrderSide::Buy,
        dec!(50.0),
        dec!(5.0),
        dec!(45000.00),
    );

    manager.add_order(iceberg.clone()).await;

    // Regular order at same price, later time
    let regular = create_order_with_tif(
        "REG001",
        "BTCUSD",
        OrderSide::Buy,
        dec!(5.0),
        dec!(45000.00),
        TimeInForce::GoodTillCancel,
    );
    manager.add_order(regular.clone()).await;

    // Iceberg should have priority (earlier timestamp)
    assert!(iceberg.created_at < regular.created_at);

    // In real matching engine, iceberg's display quantity gets filled first
    let exec = create_execution(
        iceberg.id.clone(),
        "BTCUSD",
        dec!(5.0),
        dec!(45000.00),
        LiquidityFlag::Maker,
    );
    manager
        .process_execution(&exec)
        .await
        .expect("Iceberg fills first");

    let filled_iceberg = manager
        .get_order(&iceberg.id)
        .await
        .expect("Iceberg exists");
    assert_eq!(filled_iceberg.fill_quantity, dec!(5.0));
}

#[tokio::test]
async fn test_iceberg_complete_fill_sequence() {
    let manager = OrderManager::new();

    // Iceberg: 30 BTC total, 10 BTC display (3 replenishments)
    let iceberg = create_iceberg_order(
        "ICE005",
        "BTCUSD",
        OrderSide::Sell,
        dec!(30.0),
        dec!(10.0),
        dec!(46000.00),
    );

    manager.add_order(iceberg.clone()).await;

    // Fill 1: 10 BTC
    let exec1 = create_execution(
        iceberg.id.clone(),
        "BTCUSD",
        dec!(10.0),
        dec!(46000.00),
        LiquidityFlag::Taker,
    );
    manager.process_execution(&exec1).await.expect("Fill 1");

    // Fill 2: 10 BTC
    let exec2 = create_execution(
        iceberg.id.clone(),
        "BTCUSD",
        dec!(10.0),
        dec!(46000.00),
        LiquidityFlag::Taker,
    );
    manager.process_execution(&exec2).await.expect("Fill 2");

    // Fill 3: 10 BTC (completes order)
    let exec3 = create_execution(
        iceberg.id.clone(),
        "BTCUSD",
        dec!(10.0),
        dec!(46000.00),
        LiquidityFlag::Taker,
    );
    manager.process_execution(&exec3).await.expect("Fill 3");

    let completed = manager.get_order(&iceberg.id).await.expect("Order exists");
    assert_eq!(completed.fill_quantity, dec!(30.0));
    assert_eq!(completed.status, OrderStatus::Filled);
}

#[tokio::test]
async fn test_iceberg_partial_replenishment() {
    let manager = OrderManager::new();

    // Iceberg: 25 BTC total, 10 BTC display
    // Last replenishment shows 5 BTC (not full display quantity)
    let iceberg = create_iceberg_order(
        "ICE006",
        "ETHUSD",
        OrderSide::Buy,
        dec!(25.0),
        dec!(10.0),
        dec!(3000.00),
    );

    manager.add_order(iceberg.clone()).await;

    // Fill 10 BTC
    let exec1 = create_execution(
        iceberg.id.clone(),
        "ETHUSD",
        dec!(10.0),
        dec!(3000.00),
        LiquidityFlag::Maker,
    );
    manager.process_execution(&exec1).await.expect("Fill 10");

    // Fill another 10 BTC
    let exec2 = create_execution(
        iceberg.id.clone(),
        "ETHUSD",
        dec!(10.0),
        dec!(3000.00),
        LiquidityFlag::Maker,
    );
    manager.process_execution(&exec2).await.expect("Fill 20");

    // Last replenishment: only 5 BTC remaining
    let exec3 = create_execution(
        iceberg.id.clone(),
        "ETHUSD",
        dec!(5.0),
        dec!(3000.00),
        LiquidityFlag::Maker,
    );
    manager
        .process_execution(&exec3)
        .await
        .expect("Fill final 5");

    let completed = manager.get_order(&iceberg.id).await.expect("Order exists");
    assert_eq!(completed.fill_quantity, dec!(25.0));
    assert_eq!(completed.status, OrderStatus::Filled);
}

// =============================================================================
// 4. Post-Only Order Tests (6 tests)
// =============================================================================

#[tokio::test]
async fn test_post_only_maker_execution() {
    let manager = OrderManager::new();

    // Post-only order: must be maker (add liquidity)
    let post_only = create_post_only_order(
        "POST001",
        "BTCUSD",
        OrderSide::Buy,
        dec!(10.0),
        dec!(44900.00),
    );

    manager.add_order(post_only.clone()).await;

    // Execute as maker (adds liquidity to book)
    let execution = create_execution(
        post_only.id.clone(),
        "BTCUSD",
        dec!(10.0),
        dec!(44900.00),
        LiquidityFlag::Maker,
    );

    manager
        .process_execution(&execution)
        .await
        .expect("Post-only maker fill");

    let filled = manager
        .get_order(&post_only.id)
        .await
        .expect("Order exists");
    assert_eq!(filled.status, OrderStatus::Filled);
    assert_eq!(filled.metadata.get("post_only"), Some(&"true".to_string()));
}

#[tokio::test]
async fn test_post_only_reject_taker_execution() {
    let manager = OrderManager::new();

    // Post-only order that would cross spread (taker) - should reject
    let post_only = create_post_only_order(
        "POST002",
        "BTCUSD",
        OrderSide::Buy,
        dec!(5.0),
        dec!(45100.00), // Above market (would take)
    );

    manager.add_order(post_only.clone()).await;

    // Simulate rejection because it would cross spread
    manager
        .update_order_status(&post_only.id, OrderStatus::Rejected)
        .await
        .expect("Post-only rejects taker");

    let rejected = manager
        .get_order(&post_only.id)
        .await
        .expect("Order exists");
    assert_eq!(rejected.status, OrderStatus::Rejected);
    assert_eq!(
        rejected.fill_quantity,
        Decimal::ZERO,
        "No taker fills allowed"
    );
}

#[tokio::test]
async fn test_post_only_limit_order_book_placement() {
    let manager = OrderManager::new();

    // Post-only adds to order book without immediate execution
    let post_only = create_post_only_order(
        "POST003",
        "ETHUSD",
        OrderSide::Sell,
        dec!(20.0),
        dec!(3050.00), // Above market
    );

    manager.add_order(post_only.clone()).await;

    // Order should be in book, waiting as maker
    let stored = manager
        .get_order(&post_only.id)
        .await
        .expect("Order exists");
    assert_eq!(stored.status, OrderStatus::Created);
    assert!(stored.metadata.contains_key("post_only"));

    // When counterparty takes liquidity, this becomes maker
    let execution = create_execution(
        post_only.id.clone(),
        "ETHUSD",
        dec!(20.0),
        dec!(3050.00),
        LiquidityFlag::Maker,
    );
    manager
        .process_execution(&execution)
        .await
        .expect("Maker fill");

    let filled = manager
        .get_order(&post_only.id)
        .await
        .expect("Order exists");
    assert_eq!(filled.status, OrderStatus::Filled);
}

#[tokio::test]
async fn test_post_only_rebate_eligibility() {
    let manager = OrderManager::new();

    // Post-only orders eligible for maker rebates
    let post_only = create_post_only_order(
        "POST004",
        "BTCUSD",
        OrderSide::Sell,
        dec!(8.0),
        dec!(45200.00),
    );

    manager.add_order(post_only.clone()).await;

    // Execute with maker flag (eligible for rebate)
    let execution = create_execution(
        post_only.id.clone(),
        "BTCUSD",
        dec!(8.0),
        dec!(45200.00),
        LiquidityFlag::Maker,
    );

    manager
        .process_execution(&execution)
        .await
        .expect("Maker execution");

    let filled = manager
        .get_order(&post_only.id)
        .await
        .expect("Order exists");

    // Verify execution was maker (rebate eligible)
    // In real system, would check fee structure
    assert_eq!(filled.status, OrderStatus::Filled);

    // Commission would be negative (rebate) for makers
    assert!(
        execution.commission > Decimal::ZERO,
        "Maker gets rebate (shown as commission)"
    );
}

#[tokio::test]
async fn test_post_only_price_crossing_rejection() {
    let manager = OrderManager::new();

    // Market: Best bid $45000, best ask $45100
    // Post-only buy at $45150 would cross ask - reject
    let post_only = create_post_only_order(
        "POST005",
        "BTCUSD",
        OrderSide::Buy,
        dec!(3.0),
        dec!(45150.00), // Crosses spread
    );

    manager.add_order(post_only.clone()).await;

    // Check would cross spread - reject
    if post_only.price >= dec!(45100.00) {
        // Would cross ask - reject
        manager
            .update_order_status(&post_only.id, OrderStatus::Rejected)
            .await
            .expect("Reject crossing order");
    }

    let rejected = manager
        .get_order(&post_only.id)
        .await
        .expect("Order exists");
    assert_eq!(rejected.status, OrderStatus::Rejected);
    assert!(rejected.price >= dec!(45100.00), "Price would cross spread");
}

#[tokio::test]
async fn test_post_only_vs_regular_limit() {
    let manager = OrderManager::new();

    // Regular limit: can be taker or maker
    let regular = create_order_with_tif(
        "REG002",
        "BTCUSD",
        OrderSide::Buy,
        dec!(5.0),
        dec!(45000.00),
        TimeInForce::GoodTillCancel,
    );

    // Post-only: maker only
    let post_only = create_post_only_order(
        "POST006",
        "BTCUSD",
        OrderSide::Buy,
        dec!(5.0),
        dec!(45000.00),
    );

    manager.add_order(regular.clone()).await;
    manager.add_order(post_only.clone()).await;

    // Regular can execute as taker
    let regular_exec = create_execution(
        regular.id.clone(),
        "BTCUSD",
        dec!(5.0),
        dec!(45000.00),
        LiquidityFlag::Taker,
    );
    manager
        .process_execution(&regular_exec)
        .await
        .expect("Regular fills as taker");

    // Post-only must execute as maker
    let post_exec = create_execution(
        post_only.id.clone(),
        "BTCUSD",
        dec!(5.0),
        dec!(45000.00),
        LiquidityFlag::Maker,
    );
    manager
        .process_execution(&post_exec)
        .await
        .expect("Post-only fills as maker");

    let regular_filled = manager
        .get_order(&regular.id)
        .await
        .expect("Regular exists");
    let post_filled = manager
        .get_order(&post_only.id)
        .await
        .expect("Post-only exists");

    assert_eq!(regular_filled.status, OrderStatus::Filled);
    assert_eq!(post_filled.status, OrderStatus::Filled);
}

// =============================================================================
// 5. GTD (Good-Till-Date) Order Tests (6 tests)
// =============================================================================

#[tokio::test]
async fn test_gtd_order_with_future_expiration() {
    let manager = OrderManager::new();

    // GTD order expires in 1 hour
    let mut gtd_order = create_order_with_tif(
        "GTD001",
        "BTCUSD",
        OrderSide::Buy,
        dec!(10.0),
        dec!(45000.00),
        TimeInForce::GoodTillCancel, // Using GTC as base
    );

    let expiration = Utc::now() + Duration::hours(1);
    gtd_order
        .metadata
        .insert("gtd".to_string(), "true".to_string());
    gtd_order
        .metadata
        .insert("expiration".to_string(), expiration.to_rfc3339());

    manager.add_order(gtd_order.clone()).await;

    let stored = manager
        .get_order(&gtd_order.id)
        .await
        .expect("Order exists");
    assert!(stored.metadata.contains_key("gtd"));
    assert!(stored.metadata.contains_key("expiration"));

    // Verify expiration is in future
    let exp_time: chrono::DateTime<Utc> =
        stored.metadata.get("expiration").unwrap().parse().unwrap();
    assert!(exp_time > Utc::now(), "Expiration should be in future");
}

#[tokio::test]
async fn test_gtd_auto_cancellation_at_expiry() {
    let manager = OrderManager::new();

    // GTD order with immediate expiration
    let mut gtd_order = create_order_with_tif(
        "GTD002",
        "ETHUSD",
        OrderSide::Sell,
        dec!(15.0),
        dec!(3000.00),
        TimeInForce::GoodTillCancel,
    );

    let expiration = Utc::now() + Duration::seconds(1);
    gtd_order
        .metadata
        .insert("gtd".to_string(), "true".to_string());
    gtd_order
        .metadata
        .insert("expiration".to_string(), expiration.to_rfc3339());

    manager.add_order(gtd_order.clone()).await;

    // Simulate time passing
    tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;

    // Check if expired - should auto-cancel
    let exp_time: chrono::DateTime<Utc> = gtd_order
        .metadata
        .get("expiration")
        .unwrap()
        .parse()
        .unwrap();

    if Utc::now() > exp_time {
        manager
            .cancel_order(&gtd_order.id)
            .await
            .expect("Auto-cancel expired");
    }

    let cancelled = manager
        .get_order(&gtd_order.id)
        .await
        .expect("Order exists");
    assert_eq!(
        cancelled.status,
        OrderStatus::Cancelled,
        "GTD should auto-cancel"
    );
}

#[tokio::test]
async fn test_gtd_fill_before_expiry() {
    let manager = OrderManager::new();

    // GTD order expires in 5 minutes
    let mut gtd_order = create_order_with_tif(
        "GTD003",
        "BTCUSD",
        OrderSide::Buy,
        dec!(7.0),
        dec!(44800.00),
        TimeInForce::GoodTillCancel,
    );

    let expiration = Utc::now() + Duration::minutes(5);
    gtd_order
        .metadata
        .insert("gtd".to_string(), "true".to_string());
    gtd_order
        .metadata
        .insert("expiration".to_string(), expiration.to_rfc3339());

    manager.add_order(gtd_order.clone()).await;

    // Fill before expiry
    let execution = create_execution(
        gtd_order.id.clone(),
        "BTCUSD",
        dec!(7.0),
        dec!(44800.00),
        LiquidityFlag::Maker,
    );

    manager
        .process_execution(&execution)
        .await
        .expect("Fill before expiry");

    let filled = manager
        .get_order(&gtd_order.id)
        .await
        .expect("Order exists");
    assert_eq!(filled.status, OrderStatus::Filled);
    assert!(filled.executed_at.is_some());

    // Expiration time should still be stored
    assert!(filled.metadata.contains_key("expiration"));
}

#[tokio::test]
async fn test_gtd_timezone_aware_expiration() {
    let manager = OrderManager::new();

    // GTD with explicit timezone (UTC)
    let mut gtd_order = create_order_with_tif(
        "GTD004",
        "ETHUSD",
        OrderSide::Buy,
        dec!(12.0),
        dec!(2950.00),
        TimeInForce::GoodTillCancel,
    );

    // Expiration at end of trading day UTC
    let expiration = Utc::now()
        .date_naive()
        .and_hms_opt(23, 59, 59)
        .unwrap()
        .and_utc();

    gtd_order
        .metadata
        .insert("gtd".to_string(), "true".to_string());
    gtd_order
        .metadata
        .insert("expiration".to_string(), expiration.to_rfc3339());
    gtd_order
        .metadata
        .insert("timezone".to_string(), "UTC".to_string());

    manager.add_order(gtd_order.clone()).await;

    let stored = manager
        .get_order(&gtd_order.id)
        .await
        .expect("Order exists");

    // Verify timezone info stored
    assert_eq!(stored.metadata.get("timezone"), Some(&"UTC".to_string()));

    // Verify expiration is properly parsed
    let exp_time: chrono::DateTime<Utc> =
        stored.metadata.get("expiration").unwrap().parse().unwrap();
    assert!(exp_time.hour() == 23 && exp_time.minute() == 59);
}

#[tokio::test]
async fn test_gtd_partial_fill_then_expire() {
    let manager = OrderManager::new();

    // GTD order: 20 BTC, expires in 2 seconds
    let mut gtd_order = create_order_with_tif(
        "GTD005",
        "BTCUSD",
        OrderSide::Sell,
        dec!(20.0),
        dec!(45500.00),
        TimeInForce::GoodTillCancel,
    );

    let expiration = Utc::now() + Duration::seconds(2);
    gtd_order
        .metadata
        .insert("gtd".to_string(), "true".to_string());
    gtd_order
        .metadata
        .insert("expiration".to_string(), expiration.to_rfc3339());

    manager.add_order(gtd_order.clone()).await;

    // Partial fill: 12 BTC
    let execution = create_execution(
        gtd_order.id.clone(),
        "BTCUSD",
        dec!(12.0),
        dec!(45500.00),
        LiquidityFlag::Taker,
    );
    manager
        .process_execution(&execution)
        .await
        .expect("Partial fill");

    let partial = manager
        .get_order(&gtd_order.id)
        .await
        .expect("Order exists");
    assert_eq!(partial.status, OrderStatus::PartiallyFilled);
    assert_eq!(partial.fill_quantity, dec!(12.0));

    // Wait for expiration
    tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;

    // Cancel remaining 8 BTC
    manager
        .cancel_order(&gtd_order.id)
        .await
        .expect("Cancel expired remainder");

    let expired = manager
        .get_order(&gtd_order.id)
        .await
        .expect("Order exists");
    assert_eq!(expired.status, OrderStatus::Cancelled);
    assert_eq!(expired.fill_quantity, dec!(12.0), "Keeps partial fill");
}

#[tokio::test]
async fn test_gtd_expiration_priority() {
    let manager = OrderManager::new();

    // Multiple GTD orders with different expiration times
    let mut gtd1 = create_order_with_tif(
        "GTD006",
        "BTCUSD",
        OrderSide::Buy,
        dec!(5.0),
        dec!(45000.00),
        TimeInForce::GoodTillCancel,
    );
    gtd1.metadata.insert("gtd".to_string(), "true".to_string());
    gtd1.metadata.insert(
        "expiration".to_string(),
        (Utc::now() + Duration::hours(1)).to_rfc3339(),
    );

    let mut gtd2 = create_order_with_tif(
        "GTD007",
        "BTCUSD",
        OrderSide::Buy,
        dec!(5.0),
        dec!(45000.00),
        TimeInForce::GoodTillCancel,
    );
    gtd2.metadata.insert("gtd".to_string(), "true".to_string());
    gtd2.metadata.insert(
        "expiration".to_string(),
        (Utc::now() + Duration::hours(2)).to_rfc3339(),
    );

    manager.add_order(gtd1.clone()).await;
    manager.add_order(gtd2.clone()).await;

    // Both at same price - earlier order has time priority
    assert!(gtd1.created_at < gtd2.created_at);

    // Get expiration times
    let exp1: chrono::DateTime<Utc> = gtd1.metadata.get("expiration").unwrap().parse().unwrap();
    let exp2: chrono::DateTime<Utc> = gtd2.metadata.get("expiration").unwrap().parse().unwrap();

    assert!(exp1 < exp2, "First GTD expires earlier");

    // Execution fills by time priority, not expiration
    let exec = create_execution(
        gtd1.id.clone(),
        "BTCUSD",
        dec!(5.0),
        dec!(45000.00),
        LiquidityFlag::Maker,
    );
    manager
        .process_execution(&exec)
        .await
        .expect("Earlier order fills first");

    let filled = manager.get_order(&gtd1.id).await.expect("Order exists");
    assert_eq!(filled.status, OrderStatus::Filled);
}

// =============================================================================
// Summary Statistics Test
// =============================================================================

#[tokio::test]
async fn test_advanced_order_types_statistics() {
    let manager = OrderManager::new();

    // Create various advanced order types
    let ioc = create_order_with_tif(
        "STAT_IOC",
        "BTCUSD",
        OrderSide::Buy,
        dec!(10.0),
        dec!(45000.00),
        TimeInForce::ImmediateOrCancel,
    );
    let fok = create_order_with_tif(
        "STAT_FOK",
        "BTCUSD",
        OrderSide::Buy,
        dec!(10.0),
        dec!(45000.00),
        TimeInForce::FillOrKill,
    );
    let iceberg = create_iceberg_order(
        "STAT_ICE",
        "BTCUSD",
        OrderSide::Sell,
        dec!(50.0),
        dec!(10.0),
        dec!(46000.00),
    );
    let post_only = create_post_only_order(
        "STAT_POST",
        "ETHUSD",
        OrderSide::Buy,
        dec!(15.0),
        dec!(2950.00),
    );

    // Add orders
    manager.add_order(ioc.clone()).await;
    manager.add_order(fok.clone()).await;
    manager.add_order(iceberg.clone()).await;
    manager.add_order(post_only.clone()).await;

    // Execute some orders
    let ioc_exec = create_execution(
        ioc.id.clone(),
        "BTCUSD",
        dec!(10.0),
        dec!(45000.00),
        LiquidityFlag::Taker,
    );
    manager
        .process_execution(&ioc_exec)
        .await
        .expect("IOC fill");

    let ice_exec = create_execution(
        iceberg.id.clone(),
        "BTCUSD",
        dec!(10.0),
        dec!(46000.00),
        LiquidityFlag::Maker,
    );
    manager
        .process_execution(&ice_exec)
        .await
        .expect("Iceberg fill");

    // Reject FOK
    manager
        .update_order_status(&fok.id, OrderStatus::Rejected)
        .await
        .expect("FOK reject");

    // Get statistics
    let stats = manager.get_order_stats().await;

    assert_eq!(stats.total_orders, 4);
    assert_eq!(stats.filled_orders, 1, "IOC fully filled");
    assert_eq!(stats.partially_filled_orders, 1, "Iceberg partially filled");
    assert_eq!(stats.rejected_orders, 1, "FOK rejected");

    // Fill rate includes partial fills
    let expected_fill_rate = 2.0 / 4.0; // 2 orders with fills / 4 total
    assert!((stats.fill_rate - expected_fill_rate).abs() < 0.01);
}
