//! Integration tests for order generation module
//!
//! Tests order generation from portfolio allocations, including:
//! - Order generation from allocations
//! - Delta orders with existing positions
//! - Order size validation
//! - Order persistence
//! - Performance benchmarks

use chrono::Utc;
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use sqlx::PgPool;
use uuid::Uuid;

use common::{OrderSide, OrderStatus, OrderType, Position, Quantity};
use trading_agent_service::orders::{OrderError, OrderGenerator, PortfolioAllocation};

/// Setup test database connection
async fn setup_database() -> PgPool {
    let database_url = std::env::var("DATABASE_URL").unwrap_or_else(|_| {
        "postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt".to_string()
    });

    let pool = sqlx::PgPool::connect(&database_url)
        .await
        .expect("Failed to connect to database");

    // Run migrations
    sqlx::migrate!("../../migrations")
        .run(&pool)
        .await
        .expect("Failed to run migrations");

    // Clean up any test data from previous runs
    cleanup_test_data(&pool).await;

    pool
}

/// Clean up test order data
async fn cleanup_test_data(pool: &PgPool) {
    let _ = sqlx::query("DELETE FROM agent_orders WHERE allocation_id LIKE 'alloc_%' OR allocation_id = 'test_strategy'")
        .execute(pool)
        .await;
}

/// Create a test allocation with weights
fn create_test_allocation(weights: Vec<(&str, f64)>) -> PortfolioAllocation {
    let mut symbol_weights = std::collections::HashMap::new();
    for (symbol, weight) in weights {
        symbol_weights.insert(symbol.to_string(), weight);
    }

    PortfolioAllocation {
        allocation_id: format!("alloc_{}", Uuid::new_v4()),
        strategy_id: "test_strategy".to_string(),
        total_capital: dec!(1_000_000.0), // $1M
        symbol_weights,
        rebalance_threshold: 0.05, // 5%
        max_position_size: 0.20,   // 20%
        created_at: Utc::now(),
    }
}

/// Create a test position
fn create_test_position(symbol: &str, quantity: Decimal, avg_price: Decimal) -> Position {
    let now = Utc::now();
    Position {
        id: Uuid::new_v4(),
        symbol: symbol.to_string(),
        quantity,
        avg_price,
        avg_cost: avg_price,
        basis: quantity * avg_price,
        average_price: avg_price,
        market_value: quantity * avg_price * dec!(1.01), // Assume 1% gain
        unrealized_pnl: quantity * avg_price * dec!(0.01),
        realized_pnl: Decimal::ZERO,
        created_at: now,
        updated_at: now,
        last_updated: now,
        current_price: Some(avg_price * dec!(1.01)),
        margin_requirement: Decimal::ZERO,
        notional_value: quantity * avg_price,
    }
}

#[tokio::test]
async fn test_generate_orders_from_allocation() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(
        pool.clone(),
        100.0,     // min_order_size: $100
        500_000.0, // max_order_size: $500K (enough for $1M allocation)
    );

    // Create allocation: 40% ES.FUT, 30% NQ.FUT, 30% ZN.FUT
    let allocation =
        create_test_allocation(vec![("ES.FUT", 0.40), ("NQ.FUT", 0.30), ("ZN.FUT", 0.30)]);

    // No existing positions - all new orders
    let current_positions = vec![];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    assert!(
        result.is_ok(),
        "Order generation should succeed: {:?}",
        result.err()
    );

    let orders = result.expect("Orders should be present");

    // Should have 3 orders (one per symbol)
    assert_eq!(orders.len(), 3, "Should generate 3 orders");

    // Verify order properties
    for order in &orders {
        assert!(
            order.quantity > Quantity::ZERO,
            "Order quantity should be positive"
        );
        assert_eq!(
            order.status,
            OrderStatus::Created,
            "Order should be in Created status"
        );
        assert_eq!(
            order.order_type,
            OrderType::Market,
            "Should use market orders"
        );
        assert_eq!(
            order.side,
            OrderSide::Buy,
            "All orders should be buys for new positions"
        );
    }

    // Verify ES.FUT gets ~40% of capital
    let _es_order = orders
        .iter()
        .find(|o| o.symbol.as_str() == "ES.FUT")
        .expect("ES order should exist");
    // At ~$5000/contract, $400K should be ~80 contracts
    // Allow some tolerance for rounding
    let expected_value = allocation.total_capital * dec!(0.40);
    assert!(
        expected_value > dec!(350_000) && expected_value < dec!(450_000),
        "ES allocation should be ~$400K"
    );
}

#[tokio::test]
async fn test_delta_orders_with_existing_positions() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0);

    // Create allocation: 50% ES.FUT, 50% NQ.FUT
    let allocation = create_test_allocation(vec![("ES.FUT", 0.50), ("NQ.FUT", 0.50)]);

    // Existing positions: 70% ES.FUT, 30% NQ.FUT (overweight ES by 20%, underweight NQ by 20%)
    // Total position value: ~$900K
    let current_positions = vec![
        create_test_position("ES.FUT", dec!(126), dec!(5000.0)), // $630K position (~70%)
        create_test_position("NQ.FUT", dec!(13.5), dec!(20000.0)), // $270K position (~30%)
    ];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    assert!(
        result.is_ok(),
        "Delta order generation should succeed: {:?}",
        result.err()
    );
    let orders = result.expect("Orders should be present");

    // Should have 2 orders (one per symbol with significant delta >5% threshold)
    assert_eq!(
        orders.len(),
        2,
        "Should generate 2 delta orders, got {}: {:?}",
        orders.len(),
        orders
            .iter()
            .map(|o| (o.symbol.as_str(), o.side.to_string()))
            .collect::<Vec<_>>()
    );

    // Find ES order - should be SELL (reduce overweight position)
    let es_order = orders
        .iter()
        .find(|o| o.symbol.as_str() == "ES.FUT")
        .expect("ES order should exist");
    assert_eq!(
        es_order.side,
        OrderSide::Sell,
        "ES order should be SELL to reduce overweight"
    );

    // Find NQ order - should be BUY (increase underweight position)
    let nq_order = orders
        .iter()
        .find(|o| o.symbol.as_str() == "NQ.FUT")
        .expect("NQ order should exist");
    assert_eq!(
        nq_order.side,
        OrderSide::Buy,
        "NQ order should be BUY to increase underweight"
    );
}

#[tokio::test]
async fn test_order_size_validation_min_size() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(
        pool.clone(),
        100_000.0,   // min_order_size: $100K (high minimum)
        1_000_000.0, // max_order_size: $1M (enough for NQ order)
    );

    // Create allocation with small weights that would generate tiny orders
    let allocation = create_test_allocation(vec![
        ("ES.FUT", 0.02), // 2% = $20K (below $100K minimum)
        ("NQ.FUT", 0.98), // 98% = $980K (above minimum, below maximum)
    ]);

    let current_positions = vec![];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    assert!(result.is_ok(), "Should succeed but filter small orders");
    let orders = result.expect("Orders should be present");

    // ES.FUT order should be filtered out (below minimum)
    // Only NQ.FUT should remain
    assert_eq!(
        orders.len(),
        1,
        "Should only generate 1 order (filtered small order)"
    );
    assert_eq!(
        orders[0].symbol.as_str(),
        "NQ.FUT",
        "Only NQ order should remain"
    );

    cleanup_test_data(&pool).await;
}

#[tokio::test]
async fn test_order_size_validation_max_size() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(
        pool.clone(),
        100.0,    // min_order_size: $100
        50_000.0, // max_order_size: $50K (low maximum)
    );

    // Create allocation with large weight
    let allocation = create_test_allocation(vec![
        ("ES.FUT", 1.0), // 100% = $1M (way above $50K maximum)
    ]);

    let current_positions = vec![];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    // Should error because order exceeds max size and can't be split
    assert!(
        result.is_err(),
        "Should fail when order exceeds max size without splitting"
    );

    match result {
        Err(OrderError::OrderSizeExceedsMaximum {
            symbol,
            size,
            max_size,
        }) => {
            assert_eq!(symbol, "ES.FUT");
            assert!(size > max_size);
        },
        _ => panic!("Expected OrderSizeExceedsMaximum error"),
    }
}

#[tokio::test]
async fn test_order_persistence() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0);

    // Create allocation
    let allocation = create_test_allocation(vec![("ES.FUT", 0.50), ("NQ.FUT", 0.50)]);

    let current_positions = vec![];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    assert!(result.is_ok(), "Order generation should succeed");
    let orders = result.expect("Orders should be present");

    // Verify orders are persisted in database
    for order in &orders {
        let row = sqlx::query_as::<
            _,
            (
                String,
                String,
                String,
                String,
                rust_decimal::Decimal,
                String,
                String,
            ),
        >(
            r#"
            SELECT order_id, allocation_id, symbol, side, quantity, order_type, status
            FROM agent_orders
            WHERE order_id = $1
            "#,
        )
        .bind(order.id.to_string())
        .fetch_optional(&pool)
        .await
        .expect("Database query should succeed");

        assert!(
            row.is_some(),
            "Order {} should be persisted in database",
            order.id
        );

        let row_data = row.expect("Row should exist");
        assert_eq!(row_data.0, order.id.to_string());
        assert_eq!(row_data.1, allocation.allocation_id);
        assert_eq!(row_data.2, order.symbol.as_str());
    }

    cleanup_test_data(&pool).await;
}

#[tokio::test]
async fn test_no_orders_when_within_threshold() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 100_000.0);

    // Create allocation: 50% ES.FUT, 50% NQ.FUT
    let allocation = create_test_allocation(vec![("ES.FUT", 0.50), ("NQ.FUT", 0.50)]);

    // Existing positions: 49% ES.FUT, 51% NQ.FUT (within 5% rebalance threshold)
    let current_positions = vec![
        create_test_position("ES.FUT", dec!(98), dec!(5000.0)), // $490K
        create_test_position("NQ.FUT", dec!(25.5), dec!(20000.0)), // $510K
    ];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    assert!(result.is_ok(), "Should succeed with no rebalancing needed");
    let orders = result.expect("Orders should be present");

    // No orders should be generated (deltas within threshold)
    assert_eq!(
        orders.len(),
        0,
        "Should not generate orders when within rebalance threshold"
    );
}

#[tokio::test]
async fn test_orders_with_new_symbols() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0);

    // Create allocation with new symbol
    let allocation = create_test_allocation(vec![
        ("ES.FUT", 0.40),
        ("NQ.FUT", 0.30),
        ("ZN.FUT", 0.30), // New symbol not in current positions
    ]);

    // Existing positions: only ES and NQ
    let current_positions = vec![
        create_test_position("ES.FUT", dec!(80), dec!(5000.0)),
        create_test_position("NQ.FUT", dec!(15), dec!(20000.0)),
    ];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    assert!(result.is_ok(), "Should handle new symbols");
    let orders = result.expect("Orders should be present");

    // Should have ZN order (new position)
    let zn_order = orders.iter().find(|o| o.symbol.as_str() == "ZN.FUT");
    assert!(
        zn_order.is_some(),
        "Should generate order for new symbol ZN.FUT"
    );

    let zn_order = zn_order.expect("ZN order should exist");
    assert_eq!(zn_order.side, OrderSide::Buy, "New position should be BUY");
}

#[tokio::test]
async fn test_performance_20_symbols() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 100_000.0); // 5% each = $50K max

    // Create allocation with 20 symbols
    let mut weights = vec![];
    let symbols = vec![
        "ES.FUT", "NQ.FUT", "ZN.FUT", "6E.FUT", "CL.FUT", "GC.FUT", "SI.FUT", "YM.FUT", "RTY.FUT",
        "ZB.FUT", "ZC.FUT", "ZS.FUT", "ZW.FUT", "NG.FUT", "HO.FUT", "RB.FUT", "6A.FUT", "6B.FUT",
        "6C.FUT", "6J.FUT",
    ];
    for symbol in &symbols {
        weights.push((*symbol, 0.05)); // 5% each = 100%
    }

    let allocation = create_test_allocation(weights);
    let current_positions = vec![];

    // Measure performance
    let start = std::time::Instant::now();
    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;
    let duration = start.elapsed();

    assert!(result.is_ok(), "Order generation should succeed");
    let orders = result.expect("Orders should be present");

    // Verify we got all 20 orders
    assert_eq!(orders.len(), 20, "Should generate 20 orders");

    // Performance check: <100ms for 20 symbols
    assert!(
        duration.as_millis() < 100,
        "Order generation should take <100ms, took {}ms",
        duration.as_millis()
    );

    println!(
        "✅ Generated {} orders in {}ms",
        orders.len(),
        duration.as_millis()
    );
}

#[tokio::test]
async fn test_invalid_allocation_total_capital_zero() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 100_000.0);

    let mut allocation = create_test_allocation(vec![("ES.FUT", 1.0)]);
    allocation.total_capital = Decimal::ZERO;

    let current_positions = vec![];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    assert!(result.is_err(), "Should fail with zero total capital");

    match result {
        Err(OrderError::InvalidAllocation { reason }) => {
            assert!(reason.contains("capital"));
        },
        _ => panic!("Expected InvalidAllocation error"),
    }
}

#[tokio::test]
async fn test_invalid_allocation_weights_exceed_one() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 100_000.0);

    // Weights sum to 1.5 (invalid)
    let allocation = create_test_allocation(vec![("ES.FUT", 0.8), ("NQ.FUT", 0.7)]);

    let current_positions = vec![];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    assert!(result.is_err(), "Should fail when weights exceed 1.0");

    match result {
        Err(OrderError::InvalidAllocation { reason }) => {
            assert!(reason.contains("weights") || reason.contains("sum"));
        },
        _ => panic!("Expected InvalidAllocation error"),
    }
}

#[tokio::test]
async fn test_database_error_handling() {
    // Create generator with invalid database URL
    let pool = sqlx::PgPool::connect_lazy("postgresql://invalid:invalid@localhost:9999/invalid")
        .expect("Lazy pool creation should succeed");

    let generator = OrderGenerator::new(pool, 100.0, 200_000.0); // Small max to avoid size error

    let allocation = create_test_allocation(vec![("ES.FUT", 0.10)]);
    let current_positions = vec![];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    // Should fail with database error
    assert!(
        result.is_err(),
        "Should fail with database connection error"
    );

    match result {
        Err(OrderError::Database(_)) => {
            // Expected
        },
        Err(e) => panic!("Expected Database error, got: {:?}", e),
        Ok(_) => panic!("Should not succeed with invalid database"),
    }
}

// ============================================================================
// ML SIGNAL TIMING AND POSITION SIZING TESTS
// ============================================================================

#[tokio::test]
async fn test_ml_confidence_based_position_sizing() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 1_000_000.0); // $1M max for 80% allocation

    // High confidence allocation: 80% ES.FUT
    let high_confidence_allocation = create_test_allocation(vec![
        ("ES.FUT", 0.80), // High ML confidence
        ("NQ.FUT", 0.20),
    ]);

    let current_positions = vec![];

    let result = generator
        .generate_orders(&high_confidence_allocation, &current_positions)
        .await;

    assert!(
        result.is_ok(),
        "High confidence order generation should succeed"
    );
    let orders = result.expect("Orders should be present");

    // Find ES order - should have large size due to high confidence
    let es_order = orders
        .iter()
        .find(|o| o.symbol.as_str() == "ES.FUT")
        .expect("ES order should exist");

    // Verify order metadata contains allocation info (confidence tracking)
    assert!(
        es_order.metadata.get("allocation_id").is_some(),
        "Order should have allocation_id in metadata"
    );
    assert!(
        es_order.metadata.get("strategy_id").is_some(),
        "Order should have strategy_id in metadata"
    );

    // Verify ES order is significantly larger (80% vs 20%)
    let nq_order = orders
        .iter()
        .find(|o| o.symbol.as_str() == "NQ.FUT")
        .expect("NQ order should exist");

    // ES should get 4x more capital than NQ
    let es_delta: f64 = es_order
        .metadata
        .get("delta_usd")
        .and_then(|v| v.as_f64())
        .expect("ES delta should be in metadata");
    let nq_delta: f64 = nq_order
        .metadata
        .get("delta_usd")
        .and_then(|v| v.as_f64())
        .expect("NQ delta should be in metadata");

    assert!(
        es_delta.abs() > nq_delta.abs() * 3.0,
        "ES order (80% confidence) should be >3x larger than NQ (20%)"
    );

    cleanup_test_data(&pool).await;
}

#[tokio::test]
async fn test_order_type_selection_market_orders() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0);

    let allocation = create_test_allocation(vec![("ES.FUT", 0.50), ("NQ.FUT", 0.50)]);

    let current_positions = vec![];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    assert!(result.is_ok(), "Order generation should succeed");
    let orders = result.expect("Orders should be present");

    // All orders should be market orders (default behavior for Trading Agent)
    for order in &orders {
        assert_eq!(
            order.order_type,
            OrderType::Market,
            "Order type should be Market for {}",
            order.symbol.as_str()
        );
        assert!(
            order.price.is_none(),
            "Market orders should not have a limit price"
        );
    }

    cleanup_test_data(&pool).await;
}

#[tokio::test]
async fn test_order_client_id_generation() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 1_000_000.0); // $1M max for 100% allocation

    let allocation = create_test_allocation(vec![("ES.FUT", 1.0)]);
    let current_positions = vec![];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    assert!(result.is_ok());
    let orders = result.expect("Orders should be present");
    assert_eq!(orders.len(), 1, "Should generate 1 order");

    let order = &orders[0];

    // Verify client_order_id is set
    assert!(
        order.client_order_id.is_some(),
        "Client order ID should be set"
    );

    let client_id = order
        .client_order_id
        .as_ref()
        .expect("Client ID should exist");

    // Verify format: "agent_<uuid>"
    assert!(
        client_id.starts_with("agent_"),
        "Client order ID should start with 'agent_', got: {}",
        client_id
    );

    // Verify it's a valid UUID after "agent_"
    let uuid_part = &client_id[6..]; // Skip "agent_"
    assert!(
        Uuid::parse_str(uuid_part).is_ok(),
        "Client order ID should contain valid UUID, got: {}",
        uuid_part
    );

    cleanup_test_data(&pool).await;
}

// ============================================================================
// ORDER BATCHING AND MULTI-ASSET TESTS
// ============================================================================

#[tokio::test]
async fn test_portfolio_rebalance_batch_orders() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 200_000.0);

    // Create allocation for 10-asset portfolio
    let allocation = create_test_allocation(vec![
        ("ES.FUT", 0.15),
        ("NQ.FUT", 0.15),
        ("ZN.FUT", 0.10),
        ("6E.FUT", 0.10),
        ("CL.FUT", 0.10),
        ("GC.FUT", 0.10),
        ("SI.FUT", 0.10),
        ("YM.FUT", 0.10),
        ("RTY.FUT", 0.05),
        ("ZB.FUT", 0.05),
    ]);

    let current_positions = vec![];

    let start = std::time::Instant::now();
    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;
    let duration = start.elapsed();

    assert!(result.is_ok(), "Batch order generation should succeed");
    let orders = result.expect("Orders should be present");

    // Should generate 10 orders (one per symbol)
    assert_eq!(orders.len(), 10, "Should generate 10 orders for portfolio");

    // Verify all orders have unique symbols
    let mut symbols: Vec<_> = orders.iter().map(|o| o.symbol.as_str()).collect();
    symbols.sort();
    symbols.dedup();
    assert_eq!(symbols.len(), 10, "All orders should have unique symbols");

    // Performance check: <50ms for 10 orders
    assert!(
        duration.as_millis() < 50,
        "10-order batch should generate in <50ms, took {}ms",
        duration.as_millis()
    );

    // Verify all orders stored in database
    for order in &orders {
        let row =
            sqlx::query_as::<_, (String,)>("SELECT order_id FROM agent_orders WHERE order_id = $1")
                .bind(order.id.to_string())
                .fetch_optional(&pool)
                .await
                .expect("Database query should succeed");

        assert!(row.is_some(), "Order {} should be in database", order.id);
    }

    cleanup_test_data(&pool).await;
}

#[tokio::test]
async fn test_partial_portfolio_rebalance() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0);

    // Target: 50% ES, 30% NQ, 20% ZN
    let allocation =
        create_test_allocation(vec![("ES.FUT", 0.50), ("NQ.FUT", 0.30), ("ZN.FUT", 0.20)]);

    // Current: 60% ES, 20% NQ, 20% ZN (only ES and NQ need rebalancing)
    let current_positions = vec![
        create_test_position("ES.FUT", dec!(120), dec!(5000.0)), // $600K (60%)
        create_test_position("NQ.FUT", dec!(10), dec!(20000.0)), // $200K (20%)
        create_test_position("ZN.FUT", dec!(1818), dec!(110.0)), // $200K (20%)
    ];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    assert!(result.is_ok(), "Partial rebalance should succeed");
    let orders = result.expect("Orders should be present");

    // Should only rebalance ES and NQ (ZN is already at target)
    // With 5% threshold ($50K), ZN delta is $0 (no change needed)
    // ES delta: $500K - $600K = -$100K (SELL, exceeds threshold)
    // NQ delta: $300K - $200K = +$100K (BUY, exceeds threshold)
    assert_eq!(
        orders.len(),
        2,
        "Should only generate orders for ES and NQ, not ZN"
    );

    // Verify ES order is SELL
    let es_order = orders.iter().find(|o| o.symbol.as_str() == "ES.FUT");
    if let Some(order) = es_order {
        assert_eq!(order.side, OrderSide::Sell, "ES should SELL to reduce");
    }

    // Verify NQ order is BUY
    let nq_order = orders.iter().find(|o| o.symbol.as_str() == "NQ.FUT");
    if let Some(order) = nq_order {
        assert_eq!(order.side, OrderSide::Buy, "NQ should BUY to increase");
    }

    // Verify no ZN order (within threshold)
    let zn_order = orders.iter().find(|o| o.symbol.as_str() == "ZN.FUT");
    assert!(
        zn_order.is_none(),
        "Should not generate order for ZN (within threshold)"
    );

    cleanup_test_data(&pool).await;
}

// ============================================================================
// ORDER VALIDATION AND RISK CHECKS
// ============================================================================

#[tokio::test]
async fn test_order_quantity_calculation() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0);

    let allocation = create_test_allocation(vec![("ES.FUT", 0.10)]); // $100K
    let current_positions = vec![];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    assert!(result.is_ok());
    let orders = result.expect("Orders should be present");
    assert_eq!(orders.len(), 1);

    let order = &orders[0];

    // At ~$5000/contract, $100K should be ~20 contracts
    // Quantity should be reasonable
    assert!(
        order.quantity > Quantity::ZERO,
        "Order quantity should be positive"
    );

    // Verify estimated price is reasonable for ES.FUT
    let estimated_price: f64 = order
        .metadata
        .get("estimated_price")
        .and_then(|v| v.as_f64())
        .expect("Estimated price should be in metadata");

    assert!(
        estimated_price > 4000.0 && estimated_price < 6000.0,
        "ES.FUT estimated price should be ~$5000, got: {}",
        estimated_price
    );

    // Verify quantity makes sense: quantity * price ≈ $100K
    let quantity_f64 = order.quantity.to_f64();
    let notional_value = quantity_f64 * estimated_price;

    assert!(
        (notional_value - 100_000.0).abs() < 10_000.0,
        "Notional value should be ~$100K, got: ${:.2}",
        notional_value
    );

    cleanup_test_data(&pool).await;
}

#[tokio::test]
async fn test_order_metadata_completeness() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0);

    let allocation = create_test_allocation(vec![("ES.FUT", 0.50)]);
    let current_positions = vec![];

    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;

    assert!(result.is_ok());
    let orders = result.expect("Orders should be present");
    assert_eq!(orders.len(), 1);

    let order = &orders[0];

    // Verify all required metadata fields are present
    assert!(
        order.metadata.get("allocation_id").is_some(),
        "allocation_id should be in metadata"
    );
    assert!(
        order.metadata.get("strategy_id").is_some(),
        "strategy_id should be in metadata"
    );
    assert!(
        order.metadata.get("delta_usd").is_some(),
        "delta_usd should be in metadata"
    );
    assert!(
        order.metadata.get("estimated_price").is_some(),
        "estimated_price should be in metadata"
    );

    // Verify metadata values are correct
    let allocation_id = order
        .metadata
        .get("allocation_id")
        .and_then(|v| v.as_str())
        .expect("allocation_id should be string");
    assert_eq!(allocation_id, allocation.allocation_id);

    let strategy_id = order
        .metadata
        .get("strategy_id")
        .and_then(|v| v.as_str())
        .expect("strategy_id should be string");
    assert_eq!(strategy_id, "test_strategy");

    cleanup_test_data(&pool).await;
}

#[tokio::test]
async fn test_order_timestamps() {
    let pool = setup_database().await;
    let generator = OrderGenerator::new(pool.clone(), 100.0, 500_000.0);

    let allocation = create_test_allocation(vec![("ES.FUT", 0.50)]);
    let current_positions = vec![];

    let before = Utc::now();
    let result = generator
        .generate_orders(&allocation, &current_positions)
        .await;
    let after = Utc::now();

    assert!(result.is_ok());
    let orders = result.expect("Orders should be present");
    assert_eq!(orders.len(), 1);

    let order = &orders[0];

    // Verify created_at is within test execution window
    let created_at = order.created_at.to_datetime();
    assert!(
        created_at >= before && created_at <= after,
        "Order created_at should be within test execution window"
    );

    // Verify order status is Created
    assert_eq!(
        order.status,
        OrderStatus::Created,
        "New orders should have Created status"
    );

    cleanup_test_data(&pool).await;
}
