//! Integration tests for Trading Service
//!
//! Comprehensive tests covering:
//! - Order submission and validation
//! - Order cancellation flows
//! - Position management
//! - Risk validation
//! - Kill switch integration
//! - gRPC error handling
//! - Concurrent operations

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

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

#[tokio::test]
async fn test_submit_valid_market_order() -> Result<()> {
    println!("\n=== Test: Submit Valid Market Order ===");

    let service = setup_trading_service().await?;

    let mut metadata = std::collections::HashMap::new();
    metadata.insert("time_in_force".to_string(), "GTC".to_string());
    metadata.insert(
        "client_order_id".to_string(),
        "client_order_123".to_string(),
    );

    let request = Request::new(SubmitOrderRequest {
        account_id: "test_account_001".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 = response.into_inner();

    println!("✓ Order submitted: {}", order.order_id);
    assert_eq!(order.status, OrderStatus::Submitted as i32);
    assert!(!order.order_id.is_empty());
    assert_eq!(order.message, "Order submitted successfully");

    Ok(())
}

#[tokio::test]
async fn test_submit_valid_limit_order() -> Result<()> {
    println!("\n=== Test: Submit Valid Limit Order ===");

    let service = setup_trading_service().await?;

    let mut metadata = std::collections::HashMap::new();
    metadata.insert("time_in_force".to_string(), "DAY".to_string());

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

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

    println!("✓ Limit order submitted: {}", order.order_id);
    assert_eq!(order.status, OrderStatus::Submitted as i32);
    assert!(!order.order_id.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_submit_invalid_empty_symbol() -> Result<()> {
    println!("\n=== Test: Reject Empty Symbol ===");

    let service = setup_trading_service().await?;

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

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

    let result = service.submit_order(request).await;

    assert!(result.is_err(), "Empty symbol should be rejected");
    if let Err(status) = result {
        assert_eq!(status.code(), tonic::Code::InvalidArgument);
        println!("✓ Rejected with: {}", status.message());
        assert!(status.message().contains("Symbol cannot be empty"));
    }

    Ok(())
}

#[tokio::test]
async fn test_submit_invalid_negative_quantity() -> Result<()> {
    println!("\n=== Test: Reject Negative Quantity ===");

    let service = setup_trading_service().await?;

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

    let request = Request::new(SubmitOrderRequest {
        account_id: "test_account_004".to_string(),
        symbol: "MSFT".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: -50.0, // Invalid: negative quantity
        price: None,
        stop_price: None,
        metadata,
    });

    let result = service.submit_order(request).await;

    assert!(result.is_err(), "Negative quantity should be rejected");
    if let Err(status) = result {
        assert_eq!(status.code(), tonic::Code::InvalidArgument);
        println!("✓ Rejected with: {}", status.message());
        assert!(status.message().contains("Quantity must be positive"));
    }

    Ok(())
}

#[tokio::test]
async fn test_submit_invalid_zero_quantity() -> Result<()> {
    println!("\n=== Test: Reject Zero Quantity ===");

    let service = setup_trading_service().await?;

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

    let request = Request::new(SubmitOrderRequest {
        account_id: "test_account_005".to_string(),
        symbol: "TSLA".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 0.0, // Invalid: zero quantity
        price: None,
        stop_price: None,
        metadata,
    });

    let result = service.submit_order(request).await;

    assert!(result.is_err(), "Zero quantity should be rejected");
    if let Err(status) = result {
        assert_eq!(status.code(), tonic::Code::InvalidArgument);
        println!("✓ Rejected with: {}", status.message());
    }

    Ok(())
}

#[tokio::test]
async fn test_cancel_order_success() -> Result<()> {
    println!("\n=== Test: Cancel Order Success ===");

    let service = setup_trading_service().await?;

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

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

    let submit_response = service.submit_order(submit_req).await?;
    let order_id = submit_response.into_inner().order_id;
    println!("  Order created: {}", order_id);

    // Now cancel it
    let cancel_req = Request::new(CancelOrderRequest {
        order_id: order_id.clone(),
        account_id: "test_account_006".to_string(),
    });

    let cancel_response = service.cancel_order(cancel_req).await?;
    let cancel_result = cancel_response.into_inner();

    println!("✓ Order cancelled: {}", order_id);
    assert!(cancel_result.success);

    Ok(())
}

#[tokio::test]
async fn test_cancel_nonexistent_order() -> Result<()> {
    println!("\n=== Test: Cancel Nonexistent Order ===");

    let service = setup_trading_service().await?;

    let request = Request::new(CancelOrderRequest {
        order_id: "nonexistent_order_12345".to_string(),
        account_id: "test_account_007".to_string(),
    });

    let result = service.cancel_order(request).await;

    // Should either return error or indicate failure in response
    match result {
        Ok(response) => {
            let cancel_result = response.into_inner();
            assert!(
                !cancel_result.success,
                "Cancelling nonexistent order should fail"
            );
            println!(
                "✓ Cancellation failed as expected: {}",
                cancel_result.message
            );
        },
        Err(status) => {
            println!("✓ Rejected with status: {}", status.code());
        },
    }

    Ok(())
}

#[tokio::test]
async fn test_get_order_status() -> Result<()> {
    println!("\n=== Test: Get Order Status ===");

    let service = setup_trading_service().await?;

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

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

    let submit_response = service.submit_order(submit_req).await?;
    let order_id = submit_response.into_inner().order_id;

    // Get status
    let status_req = Request::new(GetOrderStatusRequest {
        order_id: order_id.clone(),
    });

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

    println!(
        "✓ Order status retrieved: {:?}",
        order_status.order.as_ref().map(|o| o.status)
    );
    assert!(order_status.order.is_some());
    assert!(!order_status.order.unwrap().order_id.is_empty());

    Ok(())
}

#[tokio::test]
async fn test_get_positions() -> Result<()> {
    println!("\n=== Test: Get Positions ===");

    let service = setup_trading_service().await?;

    let request = Request::new(GetPositionsRequest {
        account_id: Some("test_account_009".to_string()),
        symbol: None, // Get all positions
    });

    let response = service.get_positions(request).await?;
    let positions = response.into_inner();

    println!("✓ Retrieved {} positions", positions.positions.len());

    Ok(())
}

#[tokio::test]
async fn test_concurrent_order_submissions() -> Result<()> {
    println!("\n=== Test: Concurrent Order Submissions ===");

    let service = Arc::new(setup_trading_service().await?);
    let mut handles = vec![];

    // Submit 10 orders concurrently
    for i in 1..=10 {
        let svc = service.clone();
        let handle = tokio::spawn(async move {
            let mut metadata = std::collections::HashMap::new();
            metadata.insert("time_in_force".to_string(), "GTC".to_string());
            metadata.insert(
                "client_order_id".to_string(),
                format!("concurrent_order_{}", i),
            );

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

            svc.submit_order(request).await
        });
        handles.push(handle);
    }

    // Wait for all to complete
    let mut success_count = 0;
    for handle in handles {
        if let Ok(Ok(_)) = handle.await {
            success_count += 1;
        }
    }

    println!(
        "✓ {}/10 concurrent orders submitted successfully",
        success_count
    );
    assert_eq!(success_count, 10, "All concurrent orders should succeed");

    Ok(())
}

#[tokio::test]
async fn test_risk_violation_rejection() -> Result<()> {
    println!("\n=== Test: Risk Violation Rejection ===");

    let service = setup_trading_service().await?;

    // Attempt to submit an order with very large quantity that should trigger risk limits
    let mut metadata = std::collections::HashMap::new();
    metadata.insert("time_in_force".to_string(), "GTC".to_string());

    let request = Request::new(SubmitOrderRequest {
        account_id: "test_account_010".to_string(),
        symbol: "AAPL".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 1_000_000.0, // Very large quantity (exceeds 100k limit)
        price: None,
        stop_price: None,
        metadata,
    });

    let result = service.submit_order(request).await;

    // Should be rejected due to risk limits (quantity > 100,000)
    assert!(result.is_err(), "Large quantity should be rejected");
    if let Err(status) = result {
        assert_eq!(status.code(), tonic::Code::FailedPrecondition);
        println!("✓ Risk violation rejected: {}", status.message());
        assert!(
            status.message().contains("Risk violation")
                || status.message().contains("exceeds maximum")
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_kill_switch_blocks_trading() -> Result<()> {
    println!("\n=== Test: Kill Switch Blocks Trading ===");

    let service = setup_trading_service().await?;

    // Trigger kill switch (implementation depends on state configuration)
    // For now, this is a placeholder for when kill switch is active

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

    let request = Request::new(SubmitOrderRequest {
        account_id: "test_account_011".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 result = service.submit_order(request).await;

    // When kill switch is active, should be rejected
    // This test will evolve based on kill switch implementation
    println!("  Kill switch test result: {:?}", result.is_ok());

    Ok(())
}

#[tokio::test]
async fn test_order_submission_latency() -> Result<()> {
    println!("\n=== Test: Order Submission Latency ===");

    let service = setup_trading_service().await?;
    let mut latencies = vec![];

    // Submit 100 orders and measure latency
    for i in 1..=100 {
        let mut metadata = std::collections::HashMap::new();
        metadata.insert("time_in_force".to_string(), "GTC".to_string());
        metadata.insert("client_order_id".to_string(), format!("perf_order_{}", i));

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

        let start = std::time::Instant::now();
        let _ = service.submit_order(request).await;
        let elapsed = start.elapsed();
        latencies.push(elapsed);
    }

    // Calculate percentiles
    latencies.sort();
    let p50 = latencies[49];
    let p95 = latencies[94];
    let p99 = latencies[98];

    println!("\n  Latency Metrics:");
    println!("  ├─ P50:  {:?}", p50);
    println!("  ├─ P95:  {:?}", p95);
    println!("  └─ P99:  {:?}", p99);

    // Warn if latencies are too high (thresholds depend on requirements)
    if p99 > std::time::Duration::from_millis(100) {
        println!("  ⚠ WARNING: P99 latency exceeds 100ms");
    }

    Ok(())
}
