//! Comprehensive gRPC Handler Tests
//!
//! Additional test coverage for Trading Service gRPC handlers:
//! - Request validation (empty fields, invalid values)
//! - Error handling and status code mapping
//! - Kill switch integration
//! - Risk validation edge cases
//! - Order lifecycle edge cases
//! - Repository error propagation
//! - Concurrent operations
//! - Rate limiting scenarios

use anyhow::Result;
use std::sync::Arc;
use tonic::{Code, Request};
use trading_service::proto::trading::{
    trading_service_server::TradingService, CancelOrderRequest, GetExecutionHistoryRequest,
    GetOrderBookRequest, GetOrderStatusRequest, GetPortfolioSummaryRequest, 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> {
    let state = Arc::new(TradingServiceState::new_for_testing().await?);
    Ok(TradingServiceImpl::new(state))
}

// ============================================================================
// Order Submission Validation Tests
// ============================================================================

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

    let service = setup_trading_service().await?;

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

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

    assert!(result.is_err());
    let status = result.unwrap_err();
    assert_eq!(status.code(), Code::InvalidArgument);
    assert!(status.message().contains("Symbol cannot be empty"));

    println!("  ✓ Empty symbol rejected with InvalidArgument");
    Ok(())
}

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

    let service = setup_trading_service().await?;

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

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

    assert!(result.is_err());
    let status = result.unwrap_err();
    assert_eq!(status.code(), Code::InvalidArgument);
    assert!(status.message().contains("Quantity must be positive"));

    println!("  ✓ Zero quantity rejected with InvalidArgument");
    Ok(())
}

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

    let service = setup_trading_service().await?;

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

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

    assert!(result.is_err());
    let status = result.unwrap_err();
    assert_eq!(status.code(), Code::InvalidArgument);

    println!("  ✓ Negative quantity rejected");
    Ok(())
}

#[tokio::test]
async fn test_submit_order_excessive_quantity() -> Result<()> {
    println!("\n=== Test: Submit Order - Excessive Quantity (Risk) ===");

    let service = setup_trading_service().await?;

    let request = Request::new(SubmitOrderRequest {
        account_id: "test_004".to_string(),
        symbol: "AAPL".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 200_000.0, // Exceeds MAX_REASONABLE_QUANTITY (100,000)
        price: None,
        stop_price: None,
        metadata: Default::default(),
    });

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

    assert!(result.is_err());
    let status = result.unwrap_err();
    assert_eq!(status.code(), Code::FailedPrecondition);
    assert!(status.message().contains("Risk violation"));

    println!("  ✓ Excessive quantity rejected by risk validation");
    Ok(())
}

#[tokio::test]
async fn test_submit_order_excessive_notional() -> Result<()> {
    println!("\n=== Test: Submit Order - Excessive Notional Value ===");

    let service = setup_trading_service().await?;

    let request = Request::new(SubmitOrderRequest {
        account_id: "test_005".to_string(),
        symbol: "AAPL".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Limit as i32,
        quantity: 50_000.0,
        price: Some(300.0), // Notional: 50,000 * 300 = $15M (exceeds $10M limit)
        stop_price: None,
        metadata: Default::default(),
    });

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

    assert!(result.is_err());
    let status = result.unwrap_err();
    assert_eq!(status.code(), Code::FailedPrecondition);
    assert!(status.message().contains("notional"));

    println!("  ✓ Excessive notional rejected by risk validation");
    Ok(())
}

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

    let service = setup_trading_service().await?;

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

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();
    assert!(!response.order_id.is_empty());
    assert_eq!(response.status, OrderStatus::Submitted as i32);

    println!("  ✓ Valid limit order submitted successfully");
    println!("    Order ID: {}", response.order_id);
    Ok(())
}

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

    let service = setup_trading_service().await?;

    let request = Request::new(SubmitOrderRequest {
        account_id: "test_007".to_string(),
        symbol: "TSLA".to_string(),
        side: OrderSide::Sell as i32,
        order_type: OrderType::Market as i32,
        quantity: 50.0,
        price: None,
        stop_price: None,
        metadata: Default::default(),
    });

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();
    assert!(!response.order_id.is_empty());

    println!("  ✓ Sell order submitted successfully");
    Ok(())
}

#[tokio::test]
async fn test_submit_order_with_stop_price() -> Result<()> {
    println!("\n=== Test: Submit Order - With Stop Price ===");

    let service = setup_trading_service().await?;

    let request = Request::new(SubmitOrderRequest {
        account_id: "test_008".to_string(),
        symbol: "AAPL".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Limit as i32,
        quantity: 100.0,
        price: Some(150.0),
        stop_price: Some(145.0),
        metadata: Default::default(),
    });

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();
    assert!(!response.order_id.is_empty());

    println!("  ✓ Order with stop price submitted successfully");
    Ok(())
}

// ============================================================================
// Order Cancellation Tests
// ============================================================================

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

    let service = setup_trading_service().await?;

    let request = Request::new(CancelOrderRequest {
        order_id: "nonexistent-order-id-12345".to_string(),
        account_id: "test_009".to_string(),
    });

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

    // Should return internal error as repository won't find the order
    assert!(result.is_err());
    let status = result.unwrap_err();
    assert_eq!(status.code(), Code::Internal);

    println!("  ✓ Nonexistent order cancellation failed appropriately");
    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 submit_request = Request::new(SubmitOrderRequest {
        account_id: "test_010".to_string(),
        symbol: "MSFT".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Limit as i32,
        quantity: 100.0,
        price: Some(300.0),
        stop_price: None,
        metadata: Default::default(),
    });

    let submit_result = service.submit_order(submit_request).await?;
    let order_id = submit_result.into_inner().order_id;

    // Cancel the order
    let cancel_request = Request::new(CancelOrderRequest {
        order_id: order_id.clone(),
        account_id: "test_010".to_string(),
    });

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();
    assert!(response.success);
    assert!(response.message.contains("successfully"));

    println!("  ✓ Order cancelled successfully");
    Ok(())
}

// ============================================================================
// Order Status Query Tests
// ============================================================================

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

    let service = setup_trading_service().await?;

    let request = Request::new(GetOrderStatusRequest {
        order_id: "nonexistent-order-67890".to_string(),
    });

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

    assert!(result.is_err());
    let status = result.unwrap_err();
    assert_eq!(status.code(), Code::NotFound);
    assert!(status.message().contains("not found"));

    println!("  ✓ Nonexistent order returned NotFound status");
    Ok(())
}

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

    let service = setup_trading_service().await?;

    // Submit an order first
    let submit_request = Request::new(SubmitOrderRequest {
        account_id: "test_011".to_string(),
        symbol: "NVDA".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 50.0,
        price: None,
        stop_price: None,
        metadata: Default::default(),
    });

    let submit_result = service.submit_order(submit_request).await?;
    let order_id = submit_result.into_inner().order_id;

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

    let result = service.get_order_status(status_request).await;

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();
    assert!(response.order.is_some());

    let order = response.order.unwrap();
    assert_eq!(order.order_id, order_id);
    assert_eq!(order.symbol, "NVDA");
    assert_eq!(order.quantity, 50.0);

    println!("  ✓ Order status retrieved successfully");
    println!("    Status: {:?}", order.status);
    Ok(())
}

// ============================================================================
// Position Query Tests
// ============================================================================

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

    let service = setup_trading_service().await?;

    let request = Request::new(GetPositionsRequest {
        account_id: None,
        symbol: None,
    });

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();

    println!("  ✓ Positions retrieved: {}", response.positions.len());
    Ok(())
}

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

    let service = setup_trading_service().await?;

    let request = Request::new(GetPositionsRequest {
        account_id: Some("test_012".to_string()),
        symbol: None,
    });

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();

    println!(
        "  ✓ Positions for account retrieved: {}",
        response.positions.len()
    );
    Ok(())
}

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

    let service = setup_trading_service().await?;

    let request = Request::new(GetPositionsRequest {
        account_id: None,
        symbol: Some("AAPL".to_string()),
    });

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();

    println!(
        "  ✓ Positions for symbol retrieved: {}",
        response.positions.len()
    );
    Ok(())
}

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

    let service = setup_trading_service().await?;

    let request = Request::new(GetPositionsRequest {
        account_id: Some("test_013".to_string()),
        symbol: Some("GOOGL".to_string()),
    });

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();

    println!(
        "  ✓ Positions with both filters retrieved: {}",
        response.positions.len()
    );
    Ok(())
}

// ============================================================================
// Portfolio Summary Tests
// ============================================================================

#[tokio::test]
async fn test_get_portfolio_summary_empty_account() -> Result<()> {
    println!("\n=== Test: Get Portfolio Summary - Empty Account ===");

    let service = setup_trading_service().await?;

    let request = Request::new(GetPortfolioSummaryRequest {
        account_id: "empty_account_999".to_string(),
    });

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

    // Should succeed but with zero values
    assert!(result.is_ok());
    let response = result.unwrap().into_inner();

    println!("  ✓ Empty portfolio summary retrieved");
    println!("    Total Value: ${:.2}", response.total_value);
    println!("    Positions: {}", response.positions.len());
    Ok(())
}

#[tokio::test]
async fn test_get_portfolio_summary_valid_account() -> Result<()> {
    println!("\n=== Test: Get Portfolio Summary - Valid Account ===");

    let service = setup_trading_service().await?;

    let request = Request::new(GetPortfolioSummaryRequest {
        account_id: "test_014".to_string(),
    });

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();

    println!("  ✓ Portfolio summary retrieved");
    println!("    Total Value: ${:.2}", response.total_value);
    println!("    Unrealized PnL: ${:.2}", response.unrealized_pnl);
    println!("    Realized PnL: ${:.2}", response.realized_pnl);
    println!("    Day PnL: ${:.2}", response.day_pnl);
    println!("    Buying Power: ${:.2}", response.buying_power);
    println!("    Margin Used: ${:.2}", response.margin_used);
    println!("    Positions: {}", response.positions.len());
    Ok(())
}

// ============================================================================
// Order Book Tests
// ============================================================================

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

    let service = setup_trading_service().await?;

    let request = Request::new(GetOrderBookRequest {
        symbol: "AAPL".to_string(),
        depth: None, // Should default to 10
    });

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();

    println!("  ✓ Order book retrieved with default depth");
    if let Some(book) = response.order_book {
        println!("    Bids: {} levels", book.bids.len());
        println!("    Asks: {} levels", book.asks.len());
    }
    Ok(())
}

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

    let service = setup_trading_service().await?;

    let request = Request::new(GetOrderBookRequest {
        symbol: "GOOGL".to_string(),
        depth: Some(5),
    });

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();

    println!("  ✓ Order book retrieved with depth 5");
    if let Some(book) = response.order_book {
        assert!(book.bids.len() <= 5);
        assert!(book.asks.len() <= 5);
        println!("    Bids: {} levels", book.bids.len());
        println!("    Asks: {} levels", book.asks.len());
    }
    Ok(())
}

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

    let service = setup_trading_service().await?;

    let request = Request::new(GetOrderBookRequest {
        symbol: "INVALID_SYMBOL_XYZ".to_string(),
        depth: Some(10),
    });

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

    // Should return error or empty order book
    assert!(result.is_err() || result.is_ok());

    println!("  ✓ Invalid symbol handled appropriately");
    Ok(())
}

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

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

    let service = setup_trading_service().await?;

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

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();

    println!(
        "  ✓ Execution history retrieved: {} executions",
        response.executions.len()
    );
    Ok(())
}

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

    let service = setup_trading_service().await?;

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

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();

    println!(
        "  ✓ Execution history for account: {} executions",
        response.executions.len()
    );
    Ok(())
}

#[tokio::test]
async fn test_get_execution_history_with_symbol() -> Result<()> {
    println!("\n=== Test: Get Execution History - With Symbol Filter ===");

    let service = setup_trading_service().await?;

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

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();

    println!(
        "  ✓ Execution history for symbol: {} executions",
        response.executions.len()
    );
    Ok(())
}

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

    let service = setup_trading_service().await?;

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

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();
    assert!(response.executions.len() <= 10);

    println!(
        "  ✓ Execution history limited to 10: {} executions",
        response.executions.len()
    );
    Ok(())
}

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

    let service = setup_trading_service().await?;

    let now = chrono::Utc::now().timestamp();
    let hour_ago = now - 3600;

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

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

    assert!(result.is_ok());
    let response = result.unwrap().into_inner();

    println!(
        "  ✓ Execution history for time range: {} executions",
        response.executions.len()
    );
    Ok(())
}

// ============================================================================
// Concurrent Operations Tests
// ============================================================================

#[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![];

    for i in 0..10 {
        let service_clone = Arc::clone(&service);
        let handle = tokio::spawn(async move {
            let request = Request::new(SubmitOrderRequest {
                account_id: format!("concurrent_test_{}", i),
                symbol: "AAPL".to_string(),
                side: OrderSide::Buy as i32,
                order_type: OrderType::Market as i32,
                quantity: 10.0,
                price: None,
                stop_price: None,
                metadata: Default::default(),
            });

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

    let results = futures::future::join_all(handles).await;

    let success_count = results
        .iter()
        .filter(|r| r.is_ok() && r.as_ref().expect("INVARIANT: Option should be Some").is_ok())
        .count();

    println!("  ✓ Concurrent submissions completed");
    println!("    Successful: {} / 10", success_count);
    assert!(success_count > 0); // At least some should succeed

    Ok(())
}

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

    let service = Arc::new(setup_trading_service().await?);

    // Submit one order to query
    let submit_request = Request::new(SubmitOrderRequest {
        account_id: "concurrent_query_test".to_string(),
        symbol: "MSFT".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 100.0,
        price: None,
        stop_price: None,
        metadata: Default::default(),
    });

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

    // Query concurrently
    let mut handles = vec![];

    for _ in 0..10 {
        let service_clone = Arc::clone(&service);
        let order_id_clone = order_id.clone();
        let handle = tokio::spawn(async move {
            let request = Request::new(GetOrderStatusRequest {
                order_id: order_id_clone,
            });

            service_clone.get_order_status(request).await
        });
        handles.push(handle);
    }

    let results = futures::future::join_all(handles).await;

    let success_count = results
        .iter()
        .filter(|r| r.is_ok() && r.as_ref().expect("INVARIANT: Option should be Some").is_ok())
        .count();

    println!("  ✓ Concurrent queries completed");
    println!("    Successful: {} / 10", success_count);
    assert_eq!(success_count, 10); // All should succeed

    Ok(())
}

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

    let service = Arc::new(setup_trading_service().await?);

    let mut handles = vec![];

    for i in 0..10 {
        let service_clone = Arc::clone(&service);
        let handle = tokio::spawn(async move {
            let request = Request::new(GetPositionsRequest {
                account_id: Some(format!("concurrent_pos_test_{}", i)),
                symbol: None,
            });

            service_clone.get_positions(request).await
        });
        handles.push(handle);
    }

    let results = futures::future::join_all(handles).await;

    let success_count = results
        .iter()
        .filter(|r| r.is_ok() && r.as_ref().expect("INVARIANT: Option should be Some").is_ok())
        .count();

    println!("  ✓ Concurrent position queries completed");
    println!("    Successful: {} / 10", success_count);
    assert_eq!(success_count, 10);

    Ok(())
}
