//! Client integration tests for TLI gRPC functionality

use std::time::Duration;
use tokio::time::{sleep, timeout};
use fxt::prelude::*;
use fxt::{TliClient, ServiceEndpoints};
use crate::integration::{TestConfig, TestUtilities, integration_test};
use crate::mocks::grpc_server::MockGrpcServer;

#[tokio::test]
async fn test_client_connection_success() {
    TestUtilities::validate_test_environment().unwrap();
    
    let mock_server = MockGrpcServer::start(51000).await.unwrap();
    let mut client = TestUtilities::create_test_client(51000);
    
    // Test connection
    let result = timeout(Duration::from_secs(5), client.connect()).await;
    assert!(result.is_ok(), "Client connection should succeed");
}

#[tokio::test]
async fn test_client_connection_failure() {
    TestUtilities::validate_test_environment().unwrap();
    
    // Try to connect to non-existent server
    let mut client = TestUtilities::create_test_client(59999);
    
    let result = client.connect().await;
    // Connection should proceed without error (connections are lazy)
    assert!(result.is_ok());
    
    // But health check should fail
    let health_result = client.check_health().await;
    assert!(health_result.is_err() || health_result.unwrap().is_empty());
}

#[tokio::test]
async fn test_order_submission_workflow() {
    TestUtilities::validate_test_environment().unwrap();
    
    let mock_server = MockGrpcServer::start(51001).await.unwrap();
    let mut client = TestUtilities::create_test_client(51001);
    
    // Connect to services
    client.connect().await.unwrap();
    
    // Allow some time for connection establishment
    sleep(Duration::from_millis(100)).await;
    
    // Get trading client and submit order
    let trading_client = client.trading();
    if trading_client.is_err() {
        // Skip test if trading service not available
        return;
    }
    
    let order_request = tli::proto::trading::SubmitOrderRequest {
        symbol: "AAPL".to_string(),
        side: tli::proto::trading::OrderSide::Buy as i32,
        order_type: tli::proto::trading::OrderType::Market as i32,
        quantity: 100.0,
        price: Some(150.0),
    };
    
    let result = trading_client.unwrap()
        .submit_order(tonic::Request::new(order_request))
        .await;
    
    if let Ok(response) = result {
        let order_response = response.into_inner();
        assert!(!order_response.order_id.is_empty());
        assert_eq!(order_response.status, tli::proto::trading::OrderStatus::New as i32);
    }
}

#[tokio::test]
async fn test_health_check_workflow() {
    TestUtilities::validate_test_environment().unwrap();
    
    let mock_server = MockGrpcServer::start(51002).await.unwrap();
    let mut client = TestUtilities::create_test_client(51002);
    
    client.connect().await.unwrap();
    sleep(Duration::from_millis(100)).await;
    
    let health_status = client.check_health().await;
    
    if let Ok(status_list) = health_status {
        assert!(!status_list.is_empty());
        
        for status in status_list {
            assert!(!status.service.is_empty());
            assert!(!status.endpoint.is_empty());
        }
    }
}

#[tokio::test]
async fn test_configuration_management() {
    TestUtilities::validate_test_environment().unwrap();
    
    let mock_server = MockGrpcServer::start(51003).await.unwrap();
    let mut client = TestUtilities::create_test_client(51003);
    
    client.connect().await.unwrap();
    sleep(Duration::from_millis(100)).await;
    
    let config_client = client.config();
    if config_client.is_err() {
        return; // Skip if config service not available
    }
    
    // Test getting existing configuration
    let get_request = tli::proto::trading::GetConfigRequest {
        key: "max_order_size".to_string(),
    };
    
    let result = config_client.unwrap()
        .get_config(tonic::Request::new(get_request))
        .await;
    
    if let Ok(response) = result {
        let config_response = response.into_inner();
        assert!(config_response.config.is_some());
        
        let config = config_response.config.unwrap();
        assert_eq!(config.key, "max_order_size");
        assert!(!config.value.is_empty());
    }
}

#[tokio::test]
async fn test_monitoring_metrics() {
    TestUtilities::validate_test_environment().unwrap();
    
    let mock_server = MockGrpcServer::start(51004).await.unwrap();
    let mut client = TestUtilities::create_test_client(51004);
    
    client.connect().await.unwrap();
    sleep(Duration::from_millis(100)).await;
    
    let monitoring_client = client.monitoring();
    if monitoring_client.is_err() {
        return; // Skip if monitoring service not available
    }
    
    // Test system status
    let status_request = tli::proto::trading::GetSystemStatusRequest {};
    
    let result = monitoring_client.unwrap()
        .get_system_status(tonic::Request::new(status_request))
        .await;
    
    if let Ok(response) = result {
        let status_response = response.into_inner();
        assert!(!status_response.services.is_empty());
        
        for service in status_response.services {
            assert!(!service.name.is_empty());
            assert!(service.last_check_unix_nanos > 0);
        }
    }
}

#[tokio::test]
async fn test_concurrent_operations() {
    TestUtilities::validate_test_environment().unwrap();
    
    let mock_server = MockGrpcServer::start(51005).await.unwrap();
    
    // Create multiple clients
    let mut client1 = TestUtilities::create_test_client(51005);
    let mut client2 = TestUtilities::create_test_client(51005);
    
    // Connect both clients concurrently
    let (result1, result2) = tokio::join!(
        client1.connect(),
        client2.connect()
    );
    
    assert!(result1.is_ok());
    assert!(result2.is_ok());
    
    sleep(Duration::from_millis(100)).await;
    
    // Perform concurrent health checks
    let (health1, health2) = tokio::join!(
        client1.check_health(),
        client2.check_health()
    );
    
    // Both should succeed or fail consistently
    assert_eq!(health1.is_ok(), health2.is_ok());
}

#[tokio::test]
async fn test_error_handling() {
    TestUtilities::validate_test_environment().unwrap();
    
    let mock_server = MockGrpcServer::start(51006).await.unwrap();
    let mut client = TestUtilities::create_test_client(51006);
    
    client.connect().await.unwrap();
    sleep(Duration::from_millis(100)).await;
    
    let trading_client = client.trading();
    if trading_client.is_err() {
        return;
    }
    
    // Test invalid order submission
    let invalid_order = tli::proto::trading::SubmitOrderRequest {
        symbol: "".to_string(), // Empty symbol should cause error
        side: tli::proto::trading::OrderSide::Buy as i32,
        order_type: tli::proto::trading::OrderType::Market as i32,
        quantity: 100.0,
        price: Some(150.0),
    };
    
    let result = trading_client.unwrap()
        .submit_order(tonic::Request::new(invalid_order))
        .await;
    
    assert!(result.is_err(), "Empty symbol should cause error");
    
    if let Err(status) = result {
        assert_eq!(status.code(), tonic::Code::InvalidArgument);
    }
}

#[tokio::test]
async fn test_service_discovery() {
    TestUtilities::validate_test_environment().unwrap();
    
    // Test default endpoints
    let endpoints = ServiceEndpoints::default();
    assert!(endpoints.trading_engine.contains("localhost"));
    assert!(endpoints.risk_management.contains("localhost"));
    assert!(endpoints.ml_signals.contains("localhost"));
    assert!(endpoints.market_data.contains("localhost"));
    assert!(endpoints.health_check.contains("localhost"));
    
    // Test custom endpoints
    let custom_endpoints = ServiceEndpoints {
        trading_engine: "http://custom:8080".to_string(),
        risk_management: "http://custom:8081".to_string(),
        ml_signals: "http://custom:8082".to_string(),
        market_data: "http://custom:8083".to_string(),
        health_check: "http://custom:8084".to_string(),
    };
    
    let client = TliClient::with_endpoints(custom_endpoints.clone());
    // Verify endpoints are stored correctly
    assert_eq!(client.endpoints.trading_engine, custom_endpoints.trading_engine);
}

#[tokio::test]
async fn test_graceful_disconnection() {
    TestUtilities::validate_test_environment().unwrap();
    
    let mock_server = MockGrpcServer::start(51007).await.unwrap();
    let mut client = TestUtilities::create_test_client(51007);
    
    // Connect
    client.connect().await.unwrap();
    sleep(Duration::from_millis(100)).await;
    
    // Verify connection works
    let health_before = client.check_health().await;
    
    // Disconnect
    client.disconnect().await;
    
    // Verify clients are cleared
    assert!(client.trading().is_err());
    assert!(client.monitoring().is_err());
    assert!(client.config().is_err());
}