//! Test Harness for Foxhunt HFT Integration Testing
//! 
//! Provides utilities for comprehensive end-to-end testing including:
//! - gRPC service clients
//! - Test data generation
//! - Database fixtures
//! - Performance monitoring
//! - Service orchestration

pub mod grpc_clients;
pub mod test_data;
pub mod fixtures;
pub mod performance;
pub mod docker_compose;
pub mod proto;

use std::sync::Arc;
use std::time::Duration;
use tokio::time::timeout;
use anyhow::Result;

/// Core test harness for managing service lifecycles and test execution
pub struct TestHarness {
    pub grpc_clients: grpc_clients::GrpcClients,
    pub test_data: test_data::TestDataGenerator,
    pub fixtures: fixtures::TestFixtures,
    pub performance: performance::PerformanceMonitor,
}

impl TestHarness {
    /// Initialize test harness with all required components
    pub async fn new() -> Result<Self> {
        let grpc_clients = grpc_clients::GrpcClients::new().await?;
        let test_data = test_data::TestDataGenerator::new();
        let fixtures = fixtures::TestFixtures::new().await?;
        let performance = performance::PerformanceMonitor::new();

        Ok(Self {
            grpc_clients,
            test_data,
            fixtures,
            performance,
        })
    }

    /// Setup test environment with all services
    pub async fn setup(&mut self) -> Result<()> {
        // Start database fixtures
        self.fixtures.setup().await?;
        
        // Wait for services to be ready
        self.wait_for_services().await?;
        
        // Generate test data
        self.test_data.generate_synthetic_data().await?;
        
        Ok(())
    }

    /// Cleanup test environment
    pub async fn cleanup(&mut self) -> Result<()> {
        self.fixtures.cleanup().await?;
        Ok(())
    }

    /// Wait for all services to be healthy
    async fn wait_for_services(&self) -> Result<()> {
        let timeout_duration = Duration::from_secs(60);
        
        timeout(timeout_duration, async {
            loop {
                if self.grpc_clients.are_all_healthy().await? {
                    break;
                }
                tokio::time::sleep(Duration::from_millis(500)).await;
            }
            Ok::<(), anyhow::Error>(())
        }).await??;
        
        Ok(())
    }

    /// Execute end-to-end test scenario with performance monitoring
    pub async fn execute_scenario<F, Fut>(&mut self, name: &str, test_fn: F) -> Result<TestResult>
    where
        F: FnOnce(&mut TestHarness) -> Fut,
        Fut: std::future::Future<Output = Result<()>>,
    {
        let start_time = std::time::Instant::now();
        self.performance.start_scenario(name);
        
        let result = match test_fn(self).await {
            Ok(()) => TestResult::Success {
                duration: start_time.elapsed(),
                metrics: self.performance.get_metrics(name),
            },
            Err(e) => TestResult::Failure {
                duration: start_time.elapsed(),
                error: e.to_string(),
                metrics: self.performance.get_metrics(name),
            },
        };
        
        self.performance.end_scenario(name);
        Ok(result)
    }
}

/// Test execution result with performance metrics
#[derive(Debug)]
pub enum TestResult {
    Success {
        duration: Duration,
        metrics: performance::ScenarioMetrics,
    },
    Failure {
        duration: Duration,
        error: String,
        metrics: performance::ScenarioMetrics,
    },
}

impl TestResult {
    pub fn is_success(&self) -> bool {
        matches!(self, TestResult::Success { .. })
    }

    pub fn duration(&self) -> Duration {
        match self {
            TestResult::Success { duration, .. } => *duration,
            TestResult::Failure { duration, .. } => *duration,
        }
    }

    pub fn metrics(&self) -> &performance::ScenarioMetrics {
        match self {
            TestResult::Success { metrics, .. } => metrics,
            TestResult::Failure { metrics, .. } => metrics,
        }
    }
}

/// Test environment configuration
#[derive(Debug, Clone)]
pub struct TestConfig {
    pub tli_endpoint: String,
    pub ml_training_endpoint: String,
    pub trading_service_endpoint: String,
    pub database_url: String,
    pub influxdb_url: String,
    pub enable_gpu_tests: bool,
    pub performance_baseline_file: Option<String>,
}

impl Default for TestConfig {
    fn default() -> Self {
        Self {
            tli_endpoint: "http://localhost:50051".to_string(),
            ml_training_endpoint: "http://localhost:50052".to_string(),
            trading_service_endpoint: "http://localhost:50053".to_string(),
            database_url: "postgresql://test:test@localhost:5432/foxhunt_test".to_string(),
            influxdb_url: "http://localhost:8086".to_string(),
            enable_gpu_tests: false,
            performance_baseline_file: None,
        }
    }
}

/// Load test configuration from environment or config file
pub fn load_test_config() -> Result<TestConfig> {
    let mut config = TestConfig::default();
    
    // Override with environment variables if present
    if let Ok(endpoint) = std::env::var("TLI_ENDPOINT") {
        config.tli_endpoint = endpoint;
    }
    if let Ok(endpoint) = std::env::var("ML_TRAINING_ENDPOINT") {
        config.ml_training_endpoint = endpoint;
    }
    if let Ok(endpoint) = std::env::var("TRADING_SERVICE_ENDPOINT") {
        config.trading_service_endpoint = endpoint;
    }
    if let Ok(url) = std::env::var("DATABASE_URL") {
        config.database_url = url;
    }
    if let Ok(url) = std::env::var("INFLUXDB_URL") {
        config.influxdb_url = url;
    }
    if let Ok(_) = std::env::var("ENABLE_GPU_TESTS") {
        config.enable_gpu_tests = true;
    }
    
    Ok(config)
}