#![cfg(feature = "__integration_tests")]
#![allow(unexpected_cfgs)]
//! End-to-End Integration Tests: TLI → API Gateway → Trading Service
//!
//! This test suite validates the complete flow from a TLI client through the API Gateway
//! to the Trading Service, including:
//! - JWT authentication and authorization
//! - Order submission and lifecycle
//! - Position management queries
//! - Market data subscriptions
//! - Real-time order updates
//!
//! Test Count: 15 tests
//! Coverage: Full trading service integration

use anyhow::Result;
use std::time::Duration as StdDuration;
use tokio::time::timeout;
use tonic::{transport::Channel, Request, Status};
use uuid::Uuid;

// Common test utilities (JWT auth helpers and DBN data)
mod common;
use common::auth_helpers::{create_auth_interceptor, get_api_addr, TestAuthConfig};

#[path = "common/dbn_helpers.rs"]
mod dbn_helpers;
use dbn_helpers::get_dbn_manager;

// Generated proto code
pub mod trading {
    tonic::include_proto!("trading");
}

use trading::{
    trading_service_client::TradingServiceClient, CancelOrderRequest, GetAccountInfoRequest,
    GetOrderStatusRequest, GetPositionsRequest, MarketDataType, OrderSide, OrderType,
    SubmitOrderRequest, SubscribeMarketDataRequest, SubscribeOrderUpdatesRequest,
};

/// Create an authenticated trading service client
async fn create_authenticated_client() -> Result<
    TradingServiceClient<
        tonic::service::interceptor::InterceptedService<
            Channel,
            impl Fn(Request<()>) -> Result<Request<()>, Status> + Clone,
        >,
    >,
> {
    let config = TestAuthConfig::trader()
        .with_user_id("test_trader_001")
        .with_roles(vec!["trader".to_string()])
        .with_permissions(vec![
            "api.access".to_string(),
            "trading.submit".to_string(),
            "trading.view".to_string(),
            "trading.cancel".to_string(),
        ]);

    let interceptor = create_auth_interceptor(config)?;

    let api_addr = get_api_addr();
    let channel = Channel::from_shared(api_addr)?.connect().await?;
    let client = TradingServiceClient::with_interceptor(channel, interceptor);

    Ok(client)
}

// ============================================================================
// SECTION 1: ORDER SUBMISSION E2E TESTS (5 tests)
// ============================================================================

#[tokio::test]
async fn test_e2e_order_submission_market_order() -> Result<()> {
    println!("\n=== E2E Test: Market Order Submission via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    // Use ES.FUT with realistic data from DBN files
    // Note: ES.FUT is available in test data, quantity scaled appropriately for futures
    let request = Request::new(SubmitOrderRequest {
        symbol: "ES.FUT".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 1.0, // 1 contract for ES.FUT futures
        price: None,
        stop_price: None,
        time_in_force: "GTC".to_string(),
        client_order_id: Uuid::new_v4().to_string(),
    });

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

    assert!(order.success, "Order submission should succeed");
    assert!(!order.order_id.is_empty(), "Should return order ID");

    println!("✓ Market order submitted successfully");
    println!("  Order ID: {}", order.order_id);
    println!("  Symbol: ES.FUT (Real DBN data)");
    println!("  Message: {}", order.message);

    Ok(())
}

#[tokio::test]
async fn test_e2e_order_submission_limit_order() -> Result<()> {
    println!("\n=== E2E Test: Limit Order Submission via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    // Get realistic price from DBN data
    let dbn_manager = get_dbn_manager().await?;
    let realistic_price = dbn_manager
        .create_realistic_order_price("ES.FUT", "sell", 10)
        .await?;

    println!(
        "  Using realistic limit price from DBN data: ${:.2}",
        realistic_price
    );

    let request = Request::new(SubmitOrderRequest {
        symbol: "ES.FUT".to_string(),
        side: OrderSide::Sell as i32,
        order_type: OrderType::Limit as i32,
        quantity: 1.0,
        price: Some(realistic_price),
        stop_price: None,
        time_in_force: "GTC".to_string(),
        client_order_id: Uuid::new_v4().to_string(),
    });

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

    assert!(order.success, "Limit order submission should succeed");
    assert!(!order.order_id.is_empty(), "Should return order ID");

    println!("✓ Limit order submitted successfully");
    println!("  Order ID: {}", order.order_id);
    println!("  Symbol: ES.FUT (Real DBN data)");
    println!("  Limit Price: ${:.2}", realistic_price);

    Ok(())
}

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

    // Create unauthenticated client (no JWT)
    let api_addr = get_api_addr();
    let channel = Channel::from_shared(api_addr)?.connect().await?;
    let mut client = TradingServiceClient::new(channel);

    let request = Request::new(SubmitOrderRequest {
        symbol: "ES.FUT".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 1.0,
        price: None,
        stop_price: None,
        time_in_force: "GTC".to_string(),
        client_order_id: Uuid::new_v4().to_string(),
    });

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

    assert!(result.is_err(), "Unauthenticated request should fail");

    if let Err(status) = result {
        assert_eq!(status.code(), tonic::Code::Unauthenticated);
        println!("✓ Unauthenticated request correctly rejected");
        println!("  Symbol: ES.FUT (Real DBN data)");
        println!("  Error: {}", status.message());
    }

    Ok(())
}

#[tokio::test]
async fn test_e2e_order_cancellation() -> Result<()> {
    println!("\n=== E2E Test: Order Cancellation via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    // Get realistic price from DBN data
    let dbn_manager = get_dbn_manager().await?;
    let realistic_price = dbn_manager
        .create_realistic_order_price("ES.FUT", "buy", 50)
        .await?;

    println!(
        "  Using realistic limit price from DBN data: ${:.2}",
        realistic_price
    );

    // First, submit an order
    let submit_request = Request::new(SubmitOrderRequest {
        symbol: "ES.FUT".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Limit as i32,
        quantity: 1.0,
        price: Some(realistic_price),
        stop_price: None,
        time_in_force: "GTC".to_string(),
        client_order_id: Uuid::new_v4().to_string(),
    });

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

    println!("✓ Order submitted: {} (ES.FUT, Real DBN data)", order_id);

    // Wait a moment for order to be processed
    tokio::time::sleep(StdDuration::from_millis(100)).await;

    // Now cancel the order
    let cancel_request = Request::new(CancelOrderRequest {
        order_id: order_id.clone(),
        symbol: "ES.FUT".to_string(),
    });

    let cancel_response = client.cancel_order(cancel_request).await?;
    let cancel_result = cancel_response.into_inner();

    assert!(cancel_result.success, "Order cancellation should succeed");
    println!("✓ Order cancelled successfully");
    println!("  Message: {}", cancel_result.message);

    Ok(())
}

#[tokio::test]
async fn test_e2e_order_status_query() -> Result<()> {
    println!("\n=== E2E Test: Order Status Query via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    // Submit an order first
    let submit_request = Request::new(SubmitOrderRequest {
        symbol: "ES.FUT".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 1.0,
        price: None,
        stop_price: None,
        time_in_force: "IOC".to_string(),
        client_order_id: Uuid::new_v4().to_string(),
    });

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

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

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

    assert_eq!(order_status.order_id, order_id);
    assert_eq!(order_status.symbol, "ES.FUT");

    println!("✓ Order status retrieved successfully");
    println!("  Order ID: {}", order_status.order_id);
    println!("  Symbol: {} (Real DBN data)", order_status.symbol);
    println!("  Status: {:?}", order_status.status);

    Ok(())
}

// ============================================================================
// SECTION 2: POSITION MANAGEMENT E2E TESTS (3 tests)
// ============================================================================

#[tokio::test]
async fn test_e2e_get_all_positions() -> Result<()> {
    println!("\n=== E2E Test: Get All Positions via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    let request = Request::new(GetPositionsRequest {
        symbol: None, // Get all positions
    });

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

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

    for position in &positions.positions {
        println!(
            "  - {}: {} @ ${}",
            position.symbol, position.quantity, position.market_price
        );
    }

    Ok(())
}

#[tokio::test]
async fn test_e2e_get_position_by_symbol() -> Result<()> {
    println!("\n=== E2E Test: Get Position by Symbol via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    let request = Request::new(GetPositionsRequest {
        symbol: Some("ES.FUT".to_string()),
    });

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

    println!("✓ ES.FUT position retrieved (Real DBN data)");

    if let Some(position) = positions.positions.first() {
        assert_eq!(position.symbol, "ES.FUT");
        println!("  Quantity: {}", position.quantity);
        println!("  Market Value: ${}", position.market_value);
        println!("  Unrealized PnL: ${}", position.unrealized_pnl);
    }

    Ok(())
}

#[tokio::test]
async fn test_e2e_get_account_info() -> Result<()> {
    println!("\n=== E2E Test: Get Account Info via API Gateway ===");

    let mut client = create_authenticated_client().await?;

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

    let response = client.get_account_info(request).await?;
    let account = response.into_inner();

    assert_eq!(account.account_id, "test_trader_001");

    println!("✓ Account information retrieved");
    println!("  Account ID: {}", account.account_id);
    println!("  Total Value: ${}", account.total_value);
    println!("  Cash Balance: ${}", account.cash_balance);
    println!("  Buying Power: ${}", account.buying_power);

    Ok(())
}

// ============================================================================
// SECTION 3: REAL-TIME DATA STREAMING E2E TESTS (4 tests)
// ============================================================================

#[tokio::test]
async fn test_e2e_market_data_subscription() -> Result<()> {
    println!("\n=== E2E Test: Market Data Subscription via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    // Subscribe to ES.FUT with real DBN data available
    let request = Request::new(SubscribeMarketDataRequest {
        symbols: vec!["ES.FUT".to_string()],
        data_types: vec![
            MarketDataType::Trades as i32,
            MarketDataType::Quotes as i32,
            MarketDataType::Bars as i32,
        ],
    });

    let mut stream = client.subscribe_market_data(request).await?.into_inner();

    println!("✓ Market data stream established for ES.FUT (Real DBN data)");

    // Try to receive market data events (with timeout)
    // NOTE: Market data is external - we may not receive events in test environment
    let mut events_received = 0;
    while let Ok(event_result) = timeout(StdDuration::from_secs(2), stream.message()).await {
        if let Ok(Some(_event)) = event_result {
            events_received += 1;
            println!("  Received market data event #{}", events_received);

            if events_received >= 3 {
                break;
            }
        }
    }

    // In E2E test, just verify stream was established successfully
    // Actual market data reception depends on external data feeds
    if events_received > 0 {
        println!(
            "✓ Received {} market data events from ES.FUT",
            events_received
        );
    } else {
        println!("✓ Stream established (no market data available in test environment)");
        println!("  Note: Real DBN data (ES.FUT) available for backtesting");
    }

    Ok(())
}

#[tokio::test]
async fn test_e2e_order_updates_subscription() -> Result<()> {
    println!("\n=== E2E Test: Order Updates Subscription via API Gateway ===");

    let mut client = create_authenticated_client().await?;

    let request = Request::new(SubscribeOrderUpdatesRequest {
        account_id: Some("test_trader_001".to_string()),
    });

    let mut stream = client.subscribe_order_updates(request).await?.into_inner();

    println!("✓ Order updates stream established");

    // Submit an order to generate an update (using ES.FUT with real DBN data)
    let submit_request = Request::new(SubmitOrderRequest {
        symbol: "ES.FUT".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 1.0,
        price: None,
        stop_price: None,
        time_in_force: "IOC".to_string(),
        client_order_id: Uuid::new_v4().to_string(),
    });

    client.submit_order(submit_request).await?;
    println!("✓ Test order submitted (ES.FUT, Real DBN data)");

    // Wait for order update (with timeout)
    if let Ok(Ok(Some(update))) = timeout(StdDuration::from_secs(3), stream.message()).await {
        println!("✓ Received order update");
        println!("  Order ID: {}", update.order_id);
        println!("  Symbol: ES.FUT (Real DBN data)");
        println!("  Status: {:?}", update.status);
        println!("  Message: {}", update.message);
    }

    Ok(())
}

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

    let mut handles = vec![];

    // Submit 10 concurrent orders (all ES.FUT with real DBN data)
    for i in 0..10 {
        let handle = tokio::spawn(async move {
            let mut client = create_authenticated_client().await.unwrap();

            let request = Request::new(SubmitOrderRequest {
                symbol: "ES.FUT".to_string(),
                side: if i % 2 == 0 {
                    OrderSide::Buy
                } else {
                    OrderSide::Sell
                } as i32,
                order_type: OrderType::Market as i32,
                quantity: 1.0,
                price: None,
                stop_price: None,
                time_in_force: "IOC".to_string(),
                client_order_id: Uuid::new_v4().to_string(),
            });

            client.submit_order(request).await
        });

        handles.push(handle);
    }

    let results = futures::future::join_all(handles).await;
    let successful = results
        .iter()
        .filter(|r| {
            if let Ok(Ok(response)) = r {
                response.get_ref().success
            } else {
                false
            }
        })
        .count();

    println!("✓ Concurrent order submission test completed");
    println!("  Symbol: ES.FUT (Real DBN data)");
    println!("  Total orders: 10");
    println!("  Successful: {}", successful);

    assert!(
        successful >= 8,
        "At least 80% of concurrent orders should succeed"
    );

    Ok(())
}

#[tokio::test]
async fn test_e2e_gateway_request_routing() -> Result<()> {
    println!("\n=== E2E Test: API Gateway Request Routing ===");

    let mut client = create_authenticated_client().await?;

    // Test multiple different request types to verify routing

    // 1. Account info request
    let account_request = Request::new(GetAccountInfoRequest {
        account_id: "test_trader_001".to_string(),
    });
    let account_response = client.get_account_info(account_request).await;
    assert!(
        account_response.is_ok(),
        "Account info request should be routed correctly"
    );
    println!("✓ Account info routed successfully");

    // 2. Positions request
    let positions_request = Request::new(GetPositionsRequest { symbol: None });
    let positions_response = client.get_positions(positions_request).await;
    assert!(
        positions_response.is_ok(),
        "Positions request should be routed correctly"
    );
    println!("✓ Positions request routed successfully");

    // 3. Order status request (for non-existent order)
    let status_request = Request::new(GetOrderStatusRequest {
        order_id: "non_existent_order_123".to_string(),
    });
    let _status_routed = client.get_order_status(status_request).await;
    // This might fail (order doesn't exist), but gateway routing should work
    println!("✓ Order status request routed successfully");

    println!("✓ All API Gateway routing tests passed");

    Ok(())
}

// ============================================================================
// SECTION 4: ERROR HANDLING AND RESILIENCE E2E TESTS (3 tests)
// ============================================================================

#[tokio::test]
async fn test_e2e_invalid_symbol_handling() -> Result<()> {
    println!("\n=== E2E Test: Invalid Symbol Error Handling ===");

    let mut client = create_authenticated_client().await?;

    let request = Request::new(SubmitOrderRequest {
        symbol: "INVALID_SYMBOL_XYZ".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: 0.1,
        price: None,
        stop_price: None,
        time_in_force: "GTC".to_string(),
        client_order_id: Uuid::new_v4().to_string(),
    });

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

    // Should either reject with validation error or return unsuccessful response
    if let Err(status) = result {
        println!("✓ Invalid symbol correctly rejected");
        println!("  Error code: {:?}", status.code());
        println!("  Error message: {}", status.message());
    } else if let Ok(response) = result {
        assert!(
            !response.into_inner().success,
            "Invalid symbol should not succeed"
        );
        println!("✓ Invalid symbol rejected in response");
    }

    Ok(())
}

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

    let mut client = create_authenticated_client().await?;

    let request = Request::new(SubmitOrderRequest {
        symbol: "ES.FUT".to_string(),
        side: OrderSide::Buy as i32,
        order_type: OrderType::Market as i32,
        quantity: -0.5, // Invalid negative quantity
        price: None,
        stop_price: None,
        time_in_force: "GTC".to_string(),
        client_order_id: Uuid::new_v4().to_string(),
    });

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

    assert!(
        result.is_err() || !result.unwrap().into_inner().success,
        "Negative quantity should be rejected"
    );

    println!("✓ Negative quantity correctly rejected");
    println!("  Symbol: ES.FUT (Real DBN data)");

    Ok(())
}

#[tokio::test]
async fn test_e2e_gateway_timeout_handling() -> Result<()> {
    println!("\n=== E2E Test: Gateway Timeout Handling ===");

    let mut client = create_authenticated_client().await?;

    // Set a very short timeout to simulate timeout scenario
    let request = Request::new(GetPositionsRequest { symbol: None });

    // Wrap request in a timeout
    match timeout(StdDuration::from_millis(1), client.get_positions(request)).await {
        Ok(Ok(_)) => {
            println!("✓ Request completed within timeout");
        },
        Ok(Err(status)) => {
            println!("✓ Request failed gracefully: {:?}", status.code());
        },
        Err(_) => {
            println!("✓ Timeout handled correctly");
        },
    }

    Ok(())
}
