//! Performance soak test for sub-50μs latency validation
//!
//! This module provides comprehensive performance testing to validate that the trading
//! service meets sub-50μs latency targets under various load conditions.

use crate::latency_recorder::{time_async, LatencyCategory, TimingGuard, LATENCY_RECORDER};
use std::sync::Arc;
use std::time::{Duration, Instant};
use tracing::{info, warn};

/// Soak test configuration
#[derive(Debug, Clone)]
pub struct SoakTestConfig {
    /// Number of iterations to run
    pub iterations: usize,
    /// Concurrent operations to simulate
    pub concurrency: usize,
    /// Test duration in seconds
    pub duration_seconds: u64,
    /// Target P99 latency in microseconds
    pub target_p99_us: f64,
    /// Warm-up iterations before measurement
    pub warmup_iterations: usize,
}

impl Default for SoakTestConfig {
    fn default() -> Self {
        Self {
            iterations: 100_000,
            concurrency: 100,
            duration_seconds: 60,
            target_p99_us: 50.0, // Sub-50μs target
            warmup_iterations: 1_000,
        }
    }
}

/// Soak test results
#[derive(Debug)]
pub struct SoakTestResults {
    pub config: SoakTestConfig,
    pub total_operations: u64,
    pub successful_operations: u64,
    pub failed_operations: u64,
    pub test_duration: Duration,
    pub operations_per_second: f64,
    pub target_met: bool,
    pub categories_passed: Vec<String>,
    pub categories_failed: Vec<String>,
}

/// Comprehensive soak test runner
pub struct SoakTestRunner {
    config: SoakTestConfig,
}

impl SoakTestRunner {
    /// Create new soak test runner with configuration
    pub fn new(config: SoakTestConfig) -> Self {
        Self { config }
    }

    /// Create soak test runner with default configuration
    ///
    /// Run comprehensive soak test
    pub async fn run_soak_test(&self) -> anyhow::Result<SoakTestResults> {
        info!(
            "Starting soak test: {} iterations, {} concurrent, {}s duration, target: {}μs P99",
            self.config.iterations,
            self.config.concurrency,
            self.config.duration_seconds,
            self.config.target_p99_us
        );

        // Reset latency recorder
        LATENCY_RECORDER.reset();

        // Warm-up phase
        info!(
            "Running warm-up with {} iterations...",
            self.config.warmup_iterations
        );
        self.run_warmup().await?;

        // Main test phase
        let start_time = Instant::now();
        let (successful, failed) = self.run_main_test().await?;
        let test_duration = start_time.elapsed();

        // Generate results
        let results = self
            .analyze_results(successful, failed, test_duration)
            .await;
        self.log_results(&results);

        Ok(results)
    }

    /// Run warm-up iterations to prepare the system
    async fn run_warmup(&self) -> anyhow::Result<()> {
        let semaphore = Arc::new(tokio::sync::Semaphore::new(self.config.concurrency));
        let mut handles = Vec::new();

        for i in 0..self.config.warmup_iterations {
            let permit = semaphore.clone().acquire_owned().await?;
            let handle = tokio::spawn(async move {
                let _permit = permit; // Hold permit until completion
                let _ = Self::simulate_order_operation(i as u64, true).await;
            });
            handles.push(handle);
        }

        // Wait for all warm-up operations to complete
        for handle in handles {
            handle.await?;
        }

        info!("Warm-up completed");
        Ok(())
    }

    /// Run main performance test
    async fn run_main_test(&self) -> anyhow::Result<(u64, u64)> {
        let semaphore = Arc::new(tokio::sync::Semaphore::new(self.config.concurrency));
        let successful = Arc::new(std::sync::atomic::AtomicU64::new(0));
        let failed = Arc::new(std::sync::atomic::AtomicU64::new(0));

        let test_start = Instant::now();
        let mut iteration = 0u64;
        let mut handles = Vec::new();

        info!("Starting main performance test...");

        // Run operations for the specified duration
        while test_start.elapsed().as_secs() < self.config.duration_seconds
            && iteration < self.config.iterations as u64
        {
            let permit = semaphore.clone().acquire_owned().await?;
            let successful_counter = Arc::clone(&successful);
            let failed_counter = Arc::clone(&failed);

            let handle = tokio::spawn(async move {
                let _permit = permit;
                match Self::simulate_order_operation(iteration, false).await {
                    Ok(_) => successful_counter.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
                    Err(_) => failed_counter.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
                };
            });

            handles.push(handle);
            iteration += 1;

            // Prevent unbounded handle accumulation
            if handles.len() >= self.config.concurrency * 2 {
                // Wait for half the handles to complete
                let to_wait = handles.len() / 2;
                for handle in handles.drain(..to_wait) {
                    handle.await?;
                }
            }
        }

        // Wait for remaining operations to complete
        for handle in handles {
            handle.await?;
        }

        let successful_count = successful.load(std::sync::atomic::Ordering::Relaxed);
        let failed_count = failed.load(std::sync::atomic::Ordering::Relaxed);

        info!(
            "Main test completed: {} successful, {} failed operations",
            successful_count, failed_count
        );

        Ok((successful_count, failed_count))
    }

    /// Simulate a complete order operation with all latency measurements
    async fn simulate_order_operation(iteration: u64, is_warmup: bool) -> anyhow::Result<()> {
        if !is_warmup {
            let _end_to_end = TimingGuard::start(LatencyCategory::EndToEndOrder);
        }

        // Simulate gRPC processing overhead
        if !is_warmup {
            let _grpc = TimingGuard::start(LatencyCategory::GrpcProcessing);
            Self::simulate_cpu_work(Duration::from_nanos(1000)).await; // 1μs of work
        }

        // Simulate order submission processing
        time_async(LatencyCategory::OrderSubmission, async {
            Self::simulate_cpu_work(Duration::from_nanos(5000)).await; // 5μs of work
        })
        .await;

        // Simulate risk validation
        time_async(LatencyCategory::RiskValidation, async {
            Self::simulate_cpu_work(Duration::from_nanos(8000)).await; // 8μs of work
        })
        .await;

        // Simulate order processing
        time_async(LatencyCategory::OrderProcessing, async {
            Self::simulate_cpu_work(Duration::from_nanos(12000)).await; // 12μs of work
        })
        .await;

        // Simulate ML inference (optional)
        if iteration % 10 == 0 {
            // Every 10th operation uses ML
            time_async(LatencyCategory::MLInference, async {
                Self::simulate_cpu_work(Duration::from_nanos(25000)).await; // 25μs of work
            })
            .await;
        }

        // Simulate database operation
        time_async(LatencyCategory::DatabaseOperation, async {
            Self::simulate_cpu_work(Duration::from_nanos(3000)).await; // 3μs of work
        })
        .await;

        // Simulate position update
        time_async(LatencyCategory::PositionUpdate, async {
            Self::simulate_cpu_work(Duration::from_nanos(2000)).await; // 2μs of work
        })
        .await;

        Ok(())
    }

    /// Simulate CPU-intensive work for a specified duration
    async fn simulate_cpu_work(duration: Duration) {
        let start = Instant::now();
        let mut counter = 0u64;

        // Busy-wait to simulate actual CPU work
        while start.elapsed() < duration {
            counter = counter.wrapping_add(1);
        }

        // Prevent optimization
        if counter == u64::MAX {
            println!("Unlikely counter value: {}", counter);
        }
    }

    /// Analyze test results and check if targets are met
    async fn analyze_results(
        &self,
        successful: u64,
        failed: u64,
        duration: Duration,
    ) -> SoakTestResults {
        let total_operations = successful + failed;
        let operations_per_second = total_operations as f64 / duration.as_secs_f64();

        // Generate latency report
        let report = LATENCY_RECORDER.generate_report();
        let mut categories_passed = Vec::new();
        let mut categories_failed = Vec::new();
        let mut overall_target_met = true;

        for category_report in &report.categories {
            if category_report.stats.p99_us() <= self.config.target_p99_us {
                categories_passed.push(category_report.category.name().to_string());
            } else {
                categories_failed.push(format!(
                    "{} (P99: {:.1}μs)",
                    category_report.category.name(),
                    category_report.stats.p99_us()
                ));
                overall_target_met = false;
            }
        }

        SoakTestResults {
            config: self.config.clone(),
            total_operations,
            successful_operations: successful,
            failed_operations: failed,
            test_duration: duration,
            operations_per_second,
            target_met: overall_target_met,
            categories_passed,
            categories_failed,
        }
    }

    /// Log comprehensive test results
    fn log_results(&self, results: &SoakTestResults) {
        info!("=== SOAK TEST RESULTS ===");
        info!("Test Configuration:");
        info!(
            "  Target P99 Latency: {:.1}μs",
            results.config.target_p99_us
        );
        info!("  Iterations: {}", results.config.iterations);
        info!("  Concurrency: {}", results.config.concurrency);
        info!("  Duration: {}s", results.config.duration_seconds);

        info!("Performance Results:");
        info!("  Total Operations: {}", results.total_operations);
        info!(
            "  Successful: {} ({:.2}%)",
            results.successful_operations,
            (results.successful_operations as f64 / results.total_operations as f64) * 100.0
        );
        info!(
            "  Failed: {} ({:.2}%)",
            results.failed_operations,
            (results.failed_operations as f64 / results.total_operations as f64) * 100.0
        );
        info!("  Operations/Second: {:.2}", results.operations_per_second);
        info!(
            "  Test Duration: {:.2}s",
            results.test_duration.as_secs_f64()
        );

        info!("Latency Target Results:");
        if results.target_met {
            info!(
                "  ✅ OVERALL TARGET MET: All categories under {}μs P99",
                results.config.target_p99_us
            );
        } else {
            warn!(
                "  ❌ OVERALL TARGET FAILED: Some categories exceed {}μs P99",
                results.config.target_p99_us
            );
        }

        if !results.categories_passed.is_empty() {
            info!(
                "  ✅ Categories that met target: {}",
                results.categories_passed.join(", ")
            );
        }

        if !results.categories_failed.is_empty() {
            warn!(
                "  ❌ Categories that failed target: {}",
                results.categories_failed.join(", ")
            );
        }

        // Log detailed latency statistics
        LATENCY_RECORDER.log_current_stats();
    }
}

/// Run quick soak test with default configuration
pub async fn run_quick_soak_test() -> anyhow::Result<bool> {
    let config = SoakTestConfig {
        iterations: 10_000,
        duration_seconds: 30,
        concurrency: 50,
        warmup_iterations: 500,
        ..SoakTestConfig::default()
    };

    let runner = SoakTestRunner::new(config);
    let results = runner.run_soak_test().await?;
    Ok(results.target_met)
}

/// Run comprehensive soak test with extensive load
pub async fn run_comprehensive_soak_test() -> anyhow::Result<bool> {
    let config = SoakTestConfig {
        iterations: 1_000_000,
        duration_seconds: 300, // 5 minutes
        concurrency: 200,
        warmup_iterations: 5_000,
        ..SoakTestConfig::default()
    };

    let runner = SoakTestRunner::new(config);
    let results = runner.run_soak_test().await?;
    Ok(results.target_met)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_quick_soak_test() {
        let config = SoakTestConfig {
            iterations: 1_000,
            duration_seconds: 5,
            concurrency: 10,
            warmup_iterations: 100,
            target_p99_us: 100.0, // More lenient for testing
        };

        let runner = SoakTestRunner::new(config);
        let results = runner
            .run_soak_test()
            .await
            .expect("Soak test should complete");

        assert!(results.total_operations > 0);
        assert!(results.operations_per_second > 0.0);

        // Test should generate latency measurements
        let report = LATENCY_RECORDER.generate_report();
        assert!(!report.categories.is_empty());
    }

    #[tokio::test]
    async fn test_cpu_work_simulation() {
        let start = Instant::now();
        SoakTestRunner::simulate_cpu_work(Duration::from_micros(10)).await;
        let elapsed = start.elapsed();

        // Should take at least the requested time (with some tolerance)
        assert!(elapsed >= Duration::from_micros(8));
        assert!(elapsed < Duration::from_micros(50)); // Should not be too slow
    }
}
