//! Trade Reconciliation Integration Tests
//!
//! Comprehensive tests for trade reconciliation covering:
//! - Trade matching and settlement
//! - Execution history retrieval
//! - Trade event streaming
//! - Order-to-execution mapping
//! - Reconciliation edge cases

use anyhow::Result;
use std::sync::Arc;
use tonic::Request;
use trading_service::proto::trading::{
    trading_service_server::TradingService, GetExecutionHistoryRequest, GetOrderStatusRequest,
    OrderSide, OrderType, SubmitOrderRequest,
};
use trading_service::{services::trading::TradingServiceImpl, state::TradingServiceState};

/// Setup test trading service instance
async fn setup_trading_service() -> Result<TradingServiceImpl> {
    let state = Arc::new(TradingServiceState::new_for_testing().await?);
    Ok(TradingServiceImpl::new(state))
}

// ============================================================================
// Trade Matching Tests
// ============================================================================

#[tokio::test]
async fn test_order_to_execution_mapping() -> Result<()> {
    println!("\n=== Test: Order to Execution Mapping ===");

    let service = setup_trading_service().await?;
    let account_id = "reconcile_test_001";

    // Submit order
    let mut metadata = std::collections::HashMap::new();
    metadata.insert("time_in_force".to_string(), "IOC".to_string());
    metadata.insert("client_order_id".to_string(), "recon_order_001".to_string());

    let request = Request::new(SubmitOrderRequest {
        account_id: account_id.to_string(),
        symbol: "AAPL".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 100.0,
        price: None,
        stop_price: None,
        metadata,
    });

    let response = service.submit_order(request).await?;
    let order_id = response.into_inner().order_id;
    println!("  Order submitted: {}", order_id);

    // Get order status to verify execution
    let status_request = Request::new(GetOrderStatusRequest {
        order_id: order_id.clone(),
    });

    let status_response = service.get_order_status(status_request).await?;
    let order_status = status_response.into_inner();

    if let Some(order) = order_status.order {
        println!("\n  Order Details:");
        println!("  ├─ Order ID: {}", order.order_id);
        println!("  ├─ Status: {:?}", order.status);
        println!(
            "  ├─ Filled: {} / {}",
            order.filled_quantity, order.quantity
        );
        println!("  └─ Limit Price: ${:.2}", order.price.unwrap_or(0.0));
    }

    Ok(())
}

#[tokio::test]
async fn test_partial_fill_reconciliation() -> Result<()> {
    println!("\n=== Test: Partial Fill Reconciliation ===");

    let service = setup_trading_service().await?;
    let account_id = "reconcile_test_002";

    // Submit large order that might partially fill
    let mut metadata = std::collections::HashMap::new();
    metadata.insert("time_in_force".to_string(), "GTC".to_string());

    let request = Request::new(SubmitOrderRequest {
        account_id: account_id.to_string(),
        symbol: "SPY".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Limit as i32,
        quantity: 10000.0, // Large quantity
        price: Some(450.00),
        stop_price: None,
        metadata,
    });

    let response = service.submit_order(request).await?;
    let order_id = response.into_inner().order_id;
    println!("  Large order submitted: {}", order_id);

    // Check execution status
    let status_request = Request::new(GetOrderStatusRequest {
        order_id: order_id.clone(),
    });

    let status_response = service.get_order_status(status_request).await?;
    let order_status = status_response.into_inner();

    if let Some(order) = order_status.order {
        let fill_percentage = (order.filled_quantity / order.quantity) * 100.0;
        println!("\n  Fill Status:");
        println!("  ├─ Filled: {:.2}%", fill_percentage);
        println!("  ├─ Filled Quantity: {}", order.filled_quantity);
        println!("  ├─ Remaining: {}", order.quantity - order.filled_quantity);
        println!("  └─ Limit Price: ${:.2}", order.price.unwrap_or(0.0));
    }

    Ok(())
}

#[tokio::test]
async fn test_multiple_executions_single_order() -> Result<()> {
    println!("\n=== Test: Multiple Executions Single Order ===");

    let service = setup_trading_service().await?;
    let account_id = "reconcile_test_003";

    // Submit order that may execute in multiple parts
    let mut metadata = std::collections::HashMap::new();
    metadata.insert("time_in_force".to_string(), "GTC".to_string());

    let request = Request::new(SubmitOrderRequest {
        account_id: account_id.to_string(),
        symbol: "NVDA".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 500.0,
        price: None,
        stop_price: None,
        metadata,
    });

    let response = service.submit_order(request).await?;
    let order_id = response.into_inner().order_id;
    println!("  Order submitted: {}", order_id);

    // Get execution history
    let history_request = Request::new(GetExecutionHistoryRequest {
        account_id: Some(account_id.to_string()),
        symbol: Some("NVDA".to_string()),
        start_time: None,
        end_time: None,
        limit: Some(100),
    });

    let history_response = service.get_execution_history(history_request).await?;
    let history = history_response.into_inner();

    println!("\n  Executions: {}", history.executions.len());
    for (i, exec) in history.executions.into_iter().enumerate() {
        println!("  Execution {}:", i + 1);
        println!("    ├─ Quantity: {}", exec.quantity);
        println!("    ├─ Price: ${:.2}", exec.price);
        println!("    └─ Order ID: {}", exec.order_id);
    }

    Ok(())
}

// ============================================================================
// Execution History Tests
// ============================================================================

#[tokio::test]
async fn test_execution_history_retrieval() -> Result<()> {
    println!("\n=== Test: Execution History Retrieval ===");

    let service = setup_trading_service().await?;
    let account_id = "reconcile_test_004";

    // Execute several orders
    let orders = vec![("AAPL", 50.0), ("GOOGL", 25.0), ("MSFT", 75.0)];

    for (symbol, quantity) in &orders {
        let mut metadata = std::collections::HashMap::new();
        metadata.insert("time_in_force".to_string(), "IOC".to_string());

        let request = Request::new(SubmitOrderRequest {
            account_id: account_id.to_string(),
            symbol: symbol.to_string(),
            side: OrderSide::Buy as i32,
            order_type: OrderType::Market as i32,
            quantity: *quantity,
            price: None,
            stop_price: None,
            metadata,
        });

        let _ = service.submit_order(request).await?;
        println!("  Executed: {} {} shares", symbol, quantity);
    }

    // Get full execution history
    let history_request = Request::new(GetExecutionHistoryRequest {
        account_id: Some(account_id.to_string()),
        symbol: None, // All symbols
        start_time: None,
        end_time: None,
        limit: Some(100),
    });

    let history_response = service.get_execution_history(history_request).await?;
    let history = history_response.into_inner();

    println!("\n  Total Executions: {}", history.executions.len());
    for exec in &history.executions {
        println!(
            "  {}: {} shares @ ${:.2}",
            exec.symbol, exec.quantity, exec.price
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_execution_history_filtered_by_symbol() -> Result<()> {
    println!("\n=== Test: Execution History Filtered by Symbol ===");

    let service = setup_trading_service().await?;
    let account_id = "reconcile_test_005";

    // Execute orders in multiple symbols
    let symbols = vec!["AAPL", "GOOGL", "AAPL", "MSFT", "AAPL"];

    for symbol in &symbols {
        let mut metadata = std::collections::HashMap::new();
        metadata.insert("time_in_force".to_string(), "IOC".to_string());

        let request = Request::new(SubmitOrderRequest {
            account_id: account_id.to_string(),
            symbol: symbol.to_string(),
            side: OrderSide::Buy as i32,
            order_type: OrderType::Market as i32,
            quantity: 10.0,
            price: None,
            stop_price: None,
            metadata,
        });

        let _ = service.submit_order(request).await?;
    }

    // Get AAPL executions only
    let history_request = Request::new(GetExecutionHistoryRequest {
        account_id: Some(account_id.to_string()),
        symbol: Some("AAPL".to_string()),
        start_time: None,
        end_time: None,
        limit: Some(100),
    });

    let history_response = service.get_execution_history(history_request).await?;
    let history = history_response.into_inner();

    println!("\n  AAPL Executions: {}", history.executions.len());
    for exec in &history.executions {
        println!("  {}: {} shares", exec.symbol, exec.quantity);
        assert_eq!(exec.symbol, "AAPL");
    }

    Ok(())
}

#[tokio::test]
async fn test_execution_history_with_time_range() -> Result<()> {
    println!("\n=== Test: Execution History with Time Range ===");

    let service = setup_trading_service().await?;
    let account_id = "reconcile_test_006";

    let start_time = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap()
        .as_nanos() as i64;

    // Execute some orders
    let mut metadata = std::collections::HashMap::new();
    metadata.insert("time_in_force".to_string(), "IOC".to_string());

    let request = Request::new(SubmitOrderRequest {
        account_id: account_id.to_string(),
        symbol: "SPY".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 100.0,
        price: None,
        stop_price: None,
        metadata,
    });

    let _ = service.submit_order(request).await?;

    let end_time = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap()
        .as_nanos() as i64;

    // Query with time range
    let history_request = Request::new(GetExecutionHistoryRequest {
        account_id: Some(account_id.to_string()),
        symbol: None,
        start_time: Some(start_time),
        end_time: Some(end_time),
        limit: Some(100),
    });

    let history_response = service.get_execution_history(history_request).await?;
    let history = history_response.into_inner();

    println!("\n  Executions in time range: {}", history.executions.len());
    for exec in &history.executions {
        println!("  {}: {} @ ${:.2}", exec.symbol, exec.quantity, exec.price);
    }

    Ok(())
}

#[tokio::test]
async fn test_execution_history_pagination() -> Result<()> {
    println!("\n=== Test: Execution History Pagination ===");

    let service = setup_trading_service().await?;
    let account_id = "reconcile_test_007";

    // Execute many orders
    for i in 1..=15 {
        let mut metadata = std::collections::HashMap::new();
        metadata.insert("time_in_force".to_string(), "IOC".to_string());
        metadata.insert("order_num".to_string(), i.to_string());

        let request = Request::new(SubmitOrderRequest {
            account_id: account_id.to_string(),
            symbol: "QQQ".to_string(),
            side: OrderSide::Buy as i32,
            order_type: OrderType::Market as i32,
            quantity: 10.0,
            price: None,
            stop_price: None,
            metadata,
        });

        let _ = service.submit_order(request).await?;
    }

    // Get first page (limit 10)
    let page1_request = Request::new(GetExecutionHistoryRequest {
        account_id: Some(account_id.to_string()),
        symbol: Some("QQQ".to_string()),
        start_time: None,
        end_time: None,
        limit: Some(10),
    });

    let page1_response = service.get_execution_history(page1_request).await?;
    let page1 = page1_response.into_inner();

    println!("\n  Page 1: {} executions", page1.executions.len());
    println!("  (Expected max 10 due to limit)");

    Ok(())
}

// ============================================================================
// Trade Settlement Tests
// ============================================================================

#[tokio::test]
async fn test_trade_settlement_calculation() -> Result<()> {
    println!("\n=== Test: Trade Settlement Calculation ===");

    let service = setup_trading_service().await?;
    let account_id = "reconcile_test_008";

    // Execute buy order
    let mut metadata = std::collections::HashMap::new();
    metadata.insert("time_in_force".to_string(), "IOC".to_string());

    let request = Request::new(SubmitOrderRequest {
        account_id: account_id.to_string(),
        symbol: "AMD".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Limit as i32,
        quantity: 100.0,
        price: Some(120.50),
        stop_price: None,
        metadata,
    });

    let response = service.submit_order(request).await?;
    let order_id = response.into_inner().order_id;

    // Get order details
    let status_request = Request::new(GetOrderStatusRequest {
        order_id: order_id.clone(),
    });

    let status_response = service.get_order_status(status_request).await?;
    let order_status = status_response.into_inner();

    if let Some(order) = order_status.order {
        let expected_settlement = order.filled_quantity * order.price.unwrap_or(0.0);
        println!("\n  Settlement Details:");
        println!("  ├─ Filled Quantity: {}", order.filled_quantity);
        println!("  ├─ Limit Price: ${:.2}", order.price.unwrap_or(0.0));
        println!("  └─ Settlement Amount: ${:.2}", expected_settlement);
    }

    Ok(())
}

// ============================================================================
// Edge Cases and Error Handling
// ============================================================================

#[tokio::test]
async fn test_execution_history_empty_account() -> Result<()> {
    println!("\n=== Test: Execution History - Empty Account ===");

    let service = setup_trading_service().await?;

    let request = Request::new(GetExecutionHistoryRequest {
        account_id: Some("empty_account_999".to_string()),
        symbol: None,
        start_time: None,
        end_time: None,
        limit: Some(100),
    });

    let response = service.get_execution_history(request).await?;
    let history = response.into_inner();

    println!(
        "  Executions for empty account: {}",
        history.executions.len()
    );
    assert_eq!(history.executions.len(), 0);

    Ok(())
}

#[tokio::test]
async fn test_execution_history_invalid_time_range() -> Result<()> {
    println!("\n=== Test: Execution History - Invalid Time Range ===");

    let service = setup_trading_service().await?;
    let account_id = "reconcile_test_009";

    // End time before start time
    let end_time = 1000000000i64;
    let start_time = 9000000000i64;

    let request = Request::new(GetExecutionHistoryRequest {
        account_id: Some(account_id.to_string()),
        symbol: None,
        start_time: Some(start_time),
        end_time: Some(end_time),
        limit: Some(100),
    });

    let response = service.get_execution_history(request).await?;
    let history = response.into_inner();

    println!(
        "  Executions with invalid time range: {}",
        history.executions.len()
    );
    // Should return empty or error

    Ok(())
}

#[tokio::test]
async fn test_reconciliation_with_cancellation() -> Result<()> {
    println!("\n=== Test: Reconciliation with Cancellation ===");

    let service = setup_trading_service().await?;
    let account_id = "reconcile_test_010";

    // Submit order
    let mut metadata = std::collections::HashMap::new();
    metadata.insert("time_in_force".to_string(), "GTC".to_string());

    let request = Request::new(SubmitOrderRequest {
        account_id: account_id.to_string(),
        symbol: "TSLA".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Limit as i32,
        quantity: 50.0,
        price: Some(200.00),
        stop_price: None,
        metadata,
    });

    let response = service.submit_order(request).await?;
    let order_id = response.into_inner().order_id;
    println!("  Order submitted: {}", order_id);

    // Immediately cancel
    let cancel_request = Request::new(trading_service::proto::trading::CancelOrderRequest {
        order_id: order_id.clone(),
        account_id: account_id.to_string(),
    });

    let _ = service.cancel_order(cancel_request).await?;
    println!("  Order cancelled: {}", order_id);

    // Get execution history (should be minimal or none)
    let history_request = Request::new(GetExecutionHistoryRequest {
        account_id: Some(account_id.to_string()),
        symbol: Some("TSLA".to_string()),
        start_time: None,
        end_time: None,
        limit: Some(100),
    });

    let history_response = service.get_execution_history(history_request).await?;
    let history = history_response.into_inner();

    println!(
        "  Executions after cancellation: {}",
        history.executions.len()
    );

    Ok(())
}

#[tokio::test]
async fn test_average_execution_price_calculation() -> Result<()> {
    println!("\n=== Test: Average Execution Price Calculation ===");

    let service = setup_trading_service().await?;
    let account_id = "reconcile_test_011";

    // Submit order that may fill at multiple prices
    let mut metadata = std::collections::HashMap::new();
    metadata.insert("time_in_force".to_string(), "IOC".to_string());

    let request = Request::new(SubmitOrderRequest {
        account_id: account_id.to_string(),
        symbol: "SPY".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 1000.0,
        price: None,
        stop_price: None,
        metadata,
    });

    let response = service.submit_order(request).await?;
    let order_id = response.into_inner().order_id;

    // Get execution history
    let history_request = Request::new(GetExecutionHistoryRequest {
        account_id: Some(account_id.to_string()),
        symbol: Some("SPY".to_string()),
        start_time: None,
        end_time: None,
        limit: Some(100),
    });

    let history_response = service.get_execution_history(history_request).await?;
    let history = history_response.into_inner();

    // Calculate weighted average price
    let mut total_value = 0.0;
    let mut total_quantity = 0.0;

    for exec in &history.executions {
        total_value += exec.quantity * exec.price;
        total_quantity += exec.quantity;
    }

    let average_price = if total_quantity > 0.0 {
        total_value / total_quantity
    } else {
        0.0
    };

    println!("\n  Execution Analysis:");
    println!("  ├─ Number of Executions: {}", history.executions.len());
    println!("  ├─ Total Quantity: {}", total_quantity);
    println!("  ├─ Total Value: ${:.2}", total_value);
    println!("  └─ Average Price: ${:.2}", average_price);

    // Get order status to compare
    let status_request = Request::new(GetOrderStatusRequest { order_id });

    let status_response = service.get_order_status(status_request).await?;
    if let Some(order) = status_response.into_inner().order {
        println!("\n  Order Limit Price: ${:.2}", order.price.unwrap_or(0.0));
    }

    Ok(())
}
