//! Service Health Smoke Tests
//!
//! Tests to validate that gRPC services are running and responsive.
//! Uses gRPC health check protocol to verify service availability.

use super::common::*;

#[tokio::test]
async fn test_trading_service_health() {
    let result = with_timeout(async {
        let url = trading_service_url();
        println!("Testing Trading Service at {}", url);

        // Try HTTP health endpoint first (faster)
        let client = reqwest::Client::new();
        let http_health_url = url.replace("50052", "8080").replace("http://", "http://");

        match client.get(format!("{}/health", http_health_url)).send().await {
            Ok(response) if response.status().is_success() => {
                println!("✅ Trading Service HTTP health check passed");
                return Ok(());
            }
            _ => {
                println!("  HTTP health check not available, trying gRPC...");
            }
        }

        // Fallback to gRPC connection test
        // Note: We can't use tonic_health without the proto definitions,
        // so we'll just test if we can connect
        match tonic::transport::Channel::from_shared(url.clone()) {
            Ok(endpoint) => {
                match endpoint.connect().await {
                    Ok(_channel) => {
                        println!("✅ Trading Service gRPC connection successful");
                        Ok(())
                    }
                    Err(e) => Err(Box::new(e) as Box<dyn std::error::Error>),
                }
            }
            Err(e) => Err(Box::new(e) as Box<dyn std::error::Error>),
        }
    }).await;

    skip_if_unavailable!("Trading Service", { result });
}

#[tokio::test]
async fn test_api_health() {
    let result = with_timeout(async {
        let url = api_url();
        println!("Testing API Gateway at {}", url);

        // Try gRPC connection
        match tonic::transport::Channel::from_shared(url.clone()) {
            Ok(endpoint) => {
                match endpoint.connect().await {
                    Ok(_channel) => {
                        println!("✅ API Gateway gRPC connection successful");
                        Ok(())
                    }
                    Err(e) => Err(Box::new(e) as Box<dyn std::error::Error>),
                }
            }
            Err(e) => Err(Box::new(e) as Box<dyn std::error::Error>),
        }
    }).await;

    skip_if_unavailable!("API", { result });
}

#[tokio::test]
#[ignore = "Backtesting Service may not be available due to configuration issues"]
async fn test_backtesting_service_health() {
    let result = with_timeout(async {
        let url = backtesting_service_url();
        println!("Testing Backtesting Service at {}", url);

        match tonic::transport::Channel::from_shared(url.clone()) {
            Ok(endpoint) => {
                match endpoint.connect().await {
                    Ok(_channel) => {
                        println!("✅ Backtesting Service gRPC connection successful");
                        Ok(())
                    }
                    Err(e) => Err(Box::new(e) as Box<dyn std::error::Error>),
                }
            }
            Err(e) => Err(Box::new(e) as Box<dyn std::error::Error>),
        }
    }).await;

    skip_if_unavailable!("Backtesting Service", { result });
}

#[tokio::test]
#[ignore = "ML Training Service may not be available due to configuration issues"]
async fn test_ml_training_service_health() {
    let result = with_timeout(async {
        let url = ml_training_service_url();
        println!("Testing ML Training Service at {}", url);

        match tonic::transport::Channel::from_shared(url.clone()) {
            Ok(endpoint) => {
                match endpoint.connect().await {
                    Ok(_channel) => {
                        println!("✅ ML Training Service gRPC connection successful");
                        Ok(())
                    }
                    Err(e) => Err(Box::new(e) as Box<dyn std::error::Error>),
                }
            }
            Err(e) => Err(Box::new(e) as Box<dyn std::error::Error>),
        }
    }).await;

    skip_if_unavailable!("ML Training Service", { result });
}

#[tokio::test]
async fn test_service_ports_listening() {
    println!("🔍 Checking service ports...");

    let ports_to_check = vec![
        (50051, "API"),  // Primary entry point
        (50052, "Trading Service"),  // Backend service
        (50053, "Backtesting Service"),
        (50054, "ML Training Service"),
    ];

    let mut listening = 0;
    let mut not_listening = 0;

    for (port, service) in ports_to_check {
        match std::net::TcpStream::connect(format!("localhost:{}", port)) {
            Ok(_) => {
                println!("  ✓ Port {} ({}) is listening", port, service);
                listening += 1;
            }
            Err(_) => {
                println!("  ✗ Port {} ({}) is not listening", port, service);
                not_listening += 1;
            }
        }
    }

    println!("\n📊 Service Port Summary:");
    println!("   Listening: {}", listening);
    println!("   Not listening: {}", not_listening);

    // Require at least Trading Service to be up
    assert!(listening >= 1, "No services are listening");
    println!("✅ Service port check complete");
}

#[tokio::test]
async fn test_service_response_times() {
    println!("⏱️  Measuring service response times...");

    let services = vec![
        (trading_service_url(), "Trading Service"),
        (api_url(), "API"),
    ];

    for (url, name) in services {
        let start = std::time::Instant::now();

        match tonic::transport::Channel::from_shared(url.clone()) {
            Ok(endpoint) => {
                match endpoint.connect().await {
                    Ok(_channel) => {
                        let elapsed = start.elapsed();
                        println!("  ✓ {} connected in {:?}", name, elapsed);

                        // Warn if connection takes too long
                        if elapsed.as_millis() > 1000 {
                            println!("    ⚠ Connection took longer than expected");
                        }
                    }
                    Err(_) => {
                        println!("  ⚠ {} not available", name);
                    }
                }
            }
            Err(_) => {
                println!("  ⚠ {} invalid URL", name);
            }
        }
    }

    println!("✅ Service response time check complete");
}

#[tokio::test]
async fn test_metrics_endpoints() {
    println!("📊 Checking metrics endpoints...");

    let client = reqwest::Client::new();
    let metrics_ports = vec![
        (9091, "API"),
        (9092, "Trading Service"),
        (9093, "Backtesting Service"),
        (9094, "ML Training Service"),
    ];

    let mut available = 0;
    let mut unavailable = 0;

    for (port, service) in metrics_ports {
        let url = format!("http://localhost:{}/metrics", port);

        match client.get(&url).send().await {
            Ok(response) if response.status().is_success() => {
                println!("  ✓ {} metrics available at port {}", service, port);
                available += 1;

                // Verify Prometheus format
                if let Ok(text) = response.text().await {
                    assert!(
                        text.contains("# HELP") || text.contains("# TYPE"),
                        "{} metrics not in Prometheus format",
                        service
                    );
                }
            }
            _ => {
                println!("  ⚠ {} metrics not available at port {}", service, port);
                unavailable += 1;
            }
        }
    }

    println!("\n📊 Metrics Endpoint Summary:");
    println!("   Available: {}", available);
    println!("   Unavailable: {}", unavailable);

    println!("✅ Metrics endpoint check complete");
}

#[tokio::test]
async fn test_service_versions() {
    println!("🔖 Checking service versions...");

    // Try to get version info from Trading Service health endpoint
    let client = reqwest::Client::new();
    let health_url = "http://localhost:8080/health";

    match client.get(health_url).send().await {
        Ok(response) if response.status().is_success() => {
            if let Ok(json) = response.json::<serde_json::Value>().await {
                if let Some(version) = json.get("version") {
                    println!("  ✓ Trading Service version: {}", version);
                }
                if let Some(service) = json.get("service") {
                    println!("  ✓ Service name: {}", service);
                }
            }
        }
        _ => {
            println!("  ⚠ Could not retrieve version information");
        }
    }

    println!("✅ Service version check complete");
}
