//! Performance Benchmarks Test Suite
//! 
//! Validates HFT performance requirements including:
//! - Sub-microsecond latency validation
//! - Memory usage under load
//! - Throughput stress testing  
//! - Error recovery timing
//! - Concurrent operation performance

use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
use std::thread;

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

    /// Test sub-microsecond order processing latency
    #[test]
    fn test_order_processing_latency() {
        let order_processor = create_test_order_processor();
        let test_order = create_test_order();
        
        // Warm up to eliminate cold start effects
        for _ in 0..10_000 {
            let _ = order_processor.process_order(&test_order);
        }
        
        // Measure latency over many iterations
        let iterations = 100_000;
        let mut latencies = Vec::with_capacity(iterations);
        
        for _ in 0..iterations {
            let start = Instant::now();
            let _ = order_processor.process_order(&test_order);
            let latency = start.elapsed();
            latencies.push(latency);
        }
        
        // Calculate statistics
        latencies.sort();
        let p50 = latencies[iterations / 2];
        let p95 = latencies[(iterations * 95) / 100];
        let p99 = latencies[(iterations * 99) / 100];
        let p999 = latencies[(iterations * 999) / 1000];
        
        // HFT latency requirements
        assert!(p50 < Duration::from_nanos(500), 
               "P50 latency {} exceeds 500ns requirement", p50.as_nanos());
        assert!(p95 < Duration::from_micros(1), 
               "P95 latency {} exceeds 1μs requirement", p95.as_micros());
        assert!(p99 < Duration::from_micros(2), 
               "P99 latency {} exceeds 2μs requirement", p99.as_micros());
        assert!(p999 < Duration::from_micros(5), 
               "P99.9 latency {} exceeds 5μs requirement", p999.as_micros());
        
        println!("Order Processing Latency Benchmarks:");
        println!("P50:  {:>8} ns", p50.as_nanos());
        println!("P95:  {:>8} ns", p95.as_nanos());
        println!("P99:  {:>8} ns", p99.as_nanos());
        println!("P999: {:>8} ns", p999.as_nanos());
    }

    /// Test market data processing throughput
    #[test]
    fn test_market_data_throughput() {
        let data_processor = create_test_market_data_processor();
        let test_duration = Duration::from_secs(10);
        let start_time = Instant::now();
        
        let messages_processed = Arc::new(AtomicU64::new(0));
        let stop_flag = Arc::new(std::sync::atomic::AtomicBool::new(false));
        
        // Spawn multiple producer threads
        let mut handles = vec![];
        for thread_id in 0..4 {
            let processor = data_processor.clone();
            let counter = Arc::clone(&messages_processed);
            let stop = Arc::clone(&stop_flag);
            
            let handle = thread::spawn(move || {
                let mut local_count = 0u64;
                while !stop.load(Ordering::Acquire) {
                    let market_tick = create_test_market_tick(thread_id, local_count);
                    let start = Instant::now();
                    
                    if processor.process_tick(&market_tick).is_ok() {
                        local_count += 1;
                        
                        // Verify processing latency per message
                        let processing_time = start.elapsed();
                        assert!(processing_time < Duration::from_micros(10),
                               "Individual message processing {} exceeds 10μs limit",
                               processing_time.as_micros());
                    }
                    
                    if local_count % 1000 == 0 {
                        counter.fetch_add(1000, Ordering::Relaxed);
                    }
                }
                // Add remaining count
                counter.fetch_add(local_count % 1000, Ordering::Relaxed);
            });
            handles.push(handle);
        }
        
        // Run for test duration
        thread::sleep(test_duration);
        stop_flag.store(true, Ordering::Release);
        
        // Wait for all threads to complete
        for handle in handles {
            handle.join().expect("Thread should complete");
        }
        
        let total_messages = messages_processed.load(Ordering::Acquire);
        let elapsed = start_time.elapsed();
        let throughput = total_messages as f64 / elapsed.as_secs_f64();
        
        // HFT throughput requirements: >1M messages/second
        assert!(throughput > 1_000_000.0,
               "Market data throughput {:.0} msg/s below 1M requirement", throughput);
        
        // Verify sustained performance
        assert!(throughput > 800_000.0,
               "Sustained throughput {:.0} msg/s below 800k minimum", throughput);
        
        println!("Market Data Throughput: {:.0} messages/second", throughput);
    }

    /// Test memory usage under sustained load
    #[test]
    fn test_memory_usage_under_load() {
        let system_monitor = create_test_system_monitor();
        let initial_memory = system_monitor.get_memory_usage();
        
        // Create high-frequency trading simulation
        let trading_engine = create_test_trading_engine();
        let orders_per_second = 50_000;
        let test_duration = Duration::from_secs(30);
        
        let start_time = Instant::now();
        let mut order_count = 0u64;
        
        while start_time.elapsed() < test_duration {
            // Generate burst of orders
            for _ in 0..orders_per_second {
                let order = create_test_order_with_id(order_count);
                trading_engine.submit_order(order);
                order_count += 1;
                
                // Simulate order fills
                if order_count % 10 == 0 {
                    trading_engine.report_fill(order_count - 5, 1000);
                }
            }
            
            // Check memory usage periodically
            if order_count % (orders_per_second * 5) == 0 {
                let current_memory = system_monitor.get_memory_usage();
                let memory_growth = current_memory - initial_memory;
                
                // Memory should not grow unbounded
                assert!(memory_growth < 500_000_000, // 500MB limit
                       "Memory growth {} bytes exceeds 500MB limit after {} orders",
                       memory_growth, order_count);
                
                // Memory growth rate should be sustainable
                let growth_rate = memory_growth as f64 / order_count as f64;
                assert!(growth_rate < 100.0, // Less than 100 bytes per order
                       "Memory growth rate {:.2} bytes/order exceeds 100 byte limit",
                       growth_rate);
            }
            
            thread::sleep(Duration::from_millis(1)); // 1ms intervals
        }
        
        let final_memory = system_monitor.get_memory_usage();
        let total_growth = final_memory - initial_memory;
        let growth_per_order = total_growth as f64 / order_count as f64;
        
        println!("Memory Usage Analysis:");
        println!("Total orders processed: {}", order_count);
        println!("Memory growth: {} bytes ({:.1} MB)", total_growth, total_growth as f64 / 1_000_000.0);
        println!("Growth per order: {:.2} bytes", growth_per_order);
        
        // Final memory usage validation
        assert!(growth_per_order < 50.0,
               "Average memory growth per order {:.2} bytes exceeds 50 byte limit",
               growth_per_order);
    }

    /// Test concurrent order processing performance
    #[test]
    fn test_concurrent_order_processing() {
        let order_processor = create_test_concurrent_processor();
        let orders_per_thread = 10_000;
        let num_threads = 8;
        
        let start_time = Instant::now();
        let total_processed = Arc::new(AtomicU64::new(0));
        let max_latency = Arc::new(AtomicU64::new(0));
        
        let mut handles = vec![];
        
        for thread_id in 0..num_threads {
            let processor = order_processor.clone();
            let counter = Arc::clone(&total_processed);
            let latency_tracker = Arc::clone(&max_latency);
            
            let handle = thread::spawn(move || {
                let mut thread_max_latency = 0u64;
                
                for order_id in 0..orders_per_thread {
                    let order = create_test_order_with_id((thread_id * orders_per_thread + order_id) as u64);
                    
                    let start = Instant::now();
                    let result = processor.process_order_concurrent(&order);
                    let latency_ns = start.elapsed().as_nanos() as u64;
                    
                    assert!(result.is_ok(), "Concurrent order processing failed: {:?}", result.err());
                    
                    thread_max_latency = thread_max_latency.max(latency_ns);
                    counter.fetch_add(1, Ordering::Relaxed);
                }
                
                // Update global max latency
                let current_max = latency_tracker.load(Ordering::Acquire);
                if thread_max_latency > current_max {
                    latency_tracker.compare_exchange_weak(
                        current_max, 
                        thread_max_latency, 
                        Ordering::Release, 
                        Ordering::Relaxed
                    ).ok();
                }
            });
            handles.push(handle);
        }
        
        // Wait for all threads to complete
        for handle in handles {
            handle.join().expect("Thread should complete");
        }
        
        let elapsed = start_time.elapsed();
        let total_orders = total_processed.load(Ordering::Acquire);
        let throughput = total_orders as f64 / elapsed.as_secs_f64();
        let max_latency_ns = max_latency.load(Ordering::Acquire);
        
        // Concurrent processing requirements
        assert_eq!(total_orders, (num_threads * orders_per_thread) as u64,
                  "All orders should be processed");
        assert!(throughput > 500_000.0,
               "Concurrent throughput {:.0} orders/s below 500k requirement", throughput);
        assert!(max_latency_ns < 10_000, // 10μs
               "Maximum concurrent latency {} ns exceeds 10μs limit", max_latency_ns);
        
        println!("Concurrent Processing Performance:");
        println!("Throughput: {:.0} orders/second", throughput);
        println!("Max latency: {} ns", max_latency_ns);
    }

    /// Test error recovery timing
    #[test]
    fn test_error_recovery_timing() {
        let fault_tolerant_system = create_test_fault_tolerant_system();
        
        // Test database connection recovery
        let db_recovery_start = Instant::now();
        fault_tolerant_system.simulate_database_failure();
        
        // System should detect and recover quickly
        let recovery_result = fault_tolerant_system.wait_for_recovery(Duration::from_millis(100));
        let recovery_time = db_recovery_start.elapsed();
        
        assert!(recovery_result.is_ok(), "Database recovery should succeed");
        assert!(recovery_time < Duration::from_millis(50),
               "Database recovery time {} exceeds 50ms limit", recovery_time.as_millis());
        
        // Test market data feed recovery
        let feed_recovery_start = Instant::now();
        fault_tolerant_system.simulate_feed_disruption();
        
        let feed_recovery = fault_tolerant_system.wait_for_feed_recovery(Duration::from_millis(200));
        let feed_recovery_time = feed_recovery_start.elapsed();
        
        assert!(feed_recovery.is_ok(), "Market data feed recovery should succeed");
        assert!(feed_recovery_time < Duration::from_millis(100),
               "Feed recovery time {} exceeds 100ms limit", feed_recovery_time.as_millis());
        
        // Test order routing failover
        let failover_start = Instant::now();
        fault_tolerant_system.simulate_broker_disconnect();
        
        let failover_result = fault_tolerant_system.wait_for_failover(Duration::from_millis(300));
        let failover_time = failover_start.elapsed();
        
        assert!(failover_result.is_ok(), "Broker failover should succeed");
        assert!(failover_time < Duration::from_millis(200),
               "Failover time {} exceeds 200ms limit", failover_time.as_millis());
        
        println!("Error Recovery Benchmarks:");
        println!("Database recovery: {} ms", recovery_time.as_millis());
        println!("Feed recovery: {} ms", feed_recovery_time.as_millis());
        println!("Broker failover: {} ms", failover_time.as_millis());
    }

    /// Test system performance under stress
    #[test]
    fn test_system_stress_performance() {
        let stress_tester = create_test_stress_system();
        
        // Gradually increase load and measure performance degradation
        let load_levels = vec![1_000, 5_000, 10_000, 25_000, 50_000, 100_000];
        let mut performance_results = Vec::new();
        
        for &load_level in &load_levels {
            let test_duration = Duration::from_secs(5);
            let performance = stress_tester.measure_performance_at_load(load_level, test_duration);
            
            performance_results.push((load_level, performance));
            
            // Verify performance requirements at each load level
            match load_level {
                1_000..=10_000 => {
                    assert!(performance.avg_latency < Duration::from_micros(1),
                           "Latency {} at load {} exceeds 1μs", 
                           performance.avg_latency.as_micros(), load_level);
                    assert!(performance.success_rate > 0.999,
                           "Success rate {:.4} at load {} below 99.9%", 
                           performance.success_rate, load_level);
                }
                10_001..=50_000 => {
                    assert!(performance.avg_latency < Duration::from_micros(5),
                           "Latency {} at load {} exceeds 5μs", 
                           performance.avg_latency.as_micros(), load_level);
                    assert!(performance.success_rate > 0.995,
                           "Success rate {:.4} at load {} below 99.5%", 
                           performance.success_rate, load_level);
                }
                _ => {
                    assert!(performance.avg_latency < Duration::from_micros(10),
                           "Latency {} at load {} exceeds 10μs", 
                           performance.avg_latency.as_micros(), load_level);
                    assert!(performance.success_rate > 0.99,
                           "Success rate {:.4} at load {} below 99%", 
                           performance.success_rate, load_level);
                }
            }
        }
        
        // Check for graceful degradation
        for i in 1..performance_results.len() {
            let (prev_load, prev_perf) = &performance_results[i-1];
            let (curr_load, curr_perf) = &performance_results[i];
            
            let load_increase = *curr_load as f64 / *prev_load as f64;
            let latency_increase = curr_perf.avg_latency.as_nanos() as f64 / prev_perf.avg_latency.as_nanos() as f64;
            
            // Latency should not increase faster than load squared
            assert!(latency_increase < load_increase.powi(2),
                   "Latency degradation too steep: {}x latency for {}x load",
                   latency_increase, load_increase);
        }
        
        println!("Stress Test Results:");
        for (load, perf) in performance_results {
            println!("Load {:>6}: {:>4}μs avg latency, {:.3}% success rate",
                    load, perf.avg_latency.as_micros(), perf.success_rate * 100.0);
        }
    }

    /// Test cache performance and hit rates
    #[test]
    fn test_cache_performance() {
        let cache_system = create_test_cache_system();
        let num_requests = 100_000;
        let num_unique_keys = 10_000;
        
        let start_time = Instant::now();
        let mut cache_hits = 0u64;
        let mut total_access_time = Duration::ZERO;
        
        // First pass: populate cache
        for i in 0..num_unique_keys {
            let key = format!("key_{}", i);
            let value = create_test_cache_value(i);
            
            let access_start = Instant::now();
            cache_system.put(&key, value);
            total_access_time += access_start.elapsed();
        }
        
        // Second pass: mixed read/write with high hit rate
        for i in 0..num_requests {
            let key_index = i % num_unique_keys;
            let key = format!("key_{}", key_index);
            
            let access_start = Instant::now();
            if i % 10 == 0 {
                // 10% writes
                let value = create_test_cache_value(key_index);
                cache_system.put(&key, value);
            } else {
                // 90% reads
                if let Some(_value) = cache_system.get(&key) {
                    cache_hits += 1;
                }
            }
            total_access_time += access_start.elapsed();
        }
        
        let total_time = start_time.elapsed();
        let hit_rate = cache_hits as f64 / (num_requests * 9 / 10) as f64; // Only count read requests
        let avg_access_time = total_access_time / (num_unique_keys + num_requests) as u32;
        let throughput = (num_unique_keys + num_requests) as f64 / total_time.as_secs_f64();
        
        // Cache performance requirements
        assert!(hit_rate > 0.95, "Cache hit rate {:.3} below 95% requirement", hit_rate);
        assert!(avg_access_time < Duration::from_nanos(100),
               "Average cache access time {} exceeds 100ns", avg_access_time.as_nanos());
        assert!(throughput > 1_000_000.0,
               "Cache throughput {:.0} ops/s below 1M requirement", throughput);
        
        println!("Cache Performance:");
        println!("Hit rate: {:.1}%", hit_rate * 100.0);
        println!("Avg access time: {} ns", avg_access_time.as_nanos());
        println!("Throughput: {:.0} operations/second", throughput);
    }

    // Helper functions and test implementations
    fn create_test_order_processor() -> TestOrderProcessor {
        TestOrderProcessor::new()
    }

    fn create_test_order() -> TestOrder {
        TestOrder {
            id: 12345,
            symbol: "EURUSD".to_string(),
            quantity: 100_000,
            price: 1.1025,
            side: OrderSide::Buy,
        }
    }

    fn create_test_order_with_id(id: u64) -> TestOrder {
        TestOrder {
            id,
            symbol: "EURUSD".to_string(),
            quantity: 100_000,
            price: 1.1025 + (id as f64 * 0.0001),
            side: if id % 2 == 0 { OrderSide::Buy } else { OrderSide::Sell },
        }
    }

    fn create_test_market_data_processor() -> Arc<TestMarketDataProcessor> {
        Arc::new(TestMarketDataProcessor::new())
    }

    fn create_test_market_tick(thread_id: usize, sequence: u64) -> TestMarketTick {
        TestMarketTick {
            symbol: format!("SYMBOL_{}", thread_id),
            price: 1.0 + (sequence as f64 * 0.0001),
            volume: 1000 + sequence,
            timestamp: std::time::SystemTime::now(),
        }
    }

    fn create_test_system_monitor() -> TestSystemMonitor {
        TestSystemMonitor::new()
    }

    fn create_test_trading_engine() -> TestTradingEngine {
        TestTradingEngine::new()
    }

    fn create_test_concurrent_processor() -> Arc<TestConcurrentProcessor> {
        Arc::new(TestConcurrentProcessor::new())
    }

    fn create_test_fault_tolerant_system() -> TestFaultTolerantSystem {
        TestFaultTolerantSystem::new()
    }

    fn create_test_stress_system() -> TestStressSystem {
        TestStressSystem::new()
    }

    fn create_test_cache_system() -> TestCacheSystem {
        TestCacheSystem::new()
    }

    fn create_test_cache_value(index: usize) -> TestCacheValue {
        TestCacheValue {
            data: vec![index as u8; 100], // 100 bytes per value
            timestamp: std::time::SystemTime::now(),
        }
    }
}

// Test data structures and implementations
#[derive(Debug)]
struct TestOrder {
    id: u64,
    symbol: String,
    quantity: u64,
    price: f64,
    side: OrderSide,
}

#[derive(Debug)]
// OrderSide now imported from canonical source
use common::OrderSide;

#[derive(Debug)]
struct TestOrderProcessor;

impl TestOrderProcessor {
    fn new() -> Self { Self }
    
    fn process_order(&self, _order: &TestOrder) -> Result<OrderResult, String> {
        // Simulate minimal processing time
        Ok(OrderResult::Accepted)
    }
}

#[derive(Debug)]
enum OrderResult {
    Accepted,
    Rejected,
}

#[derive(Debug)]
struct TestMarketTick {
    symbol: String,
    price: f64,
    volume: u64,
    timestamp: std::time::SystemTime,
}

#[derive(Debug)]
struct TestMarketDataProcessor {
    processed_count: AtomicU64,
}

impl TestMarketDataProcessor {
    fn new() -> Self {
        Self {
            processed_count: AtomicU64::new(0),
        }
    }
    
    fn process_tick(&self, _tick: &TestMarketTick) -> Result<(), String> {
        self.processed_count.fetch_add(1, Ordering::Relaxed);
        Ok(())
    }
}

#[derive(Debug)]
struct TestSystemMonitor {
    initial_memory: usize,
}

impl TestSystemMonitor {
    fn new() -> Self {
        Self {
            initial_memory: 100_000_000, // 100MB baseline
        }
    }
    
    fn get_memory_usage(&self) -> usize {
        // Simulate memory usage tracking
        self.initial_memory + (rand::random::<usize>() % 10_000_000)
    }
}

#[derive(Debug)]
struct TestTradingEngine {
    orders_submitted: AtomicU64,
    fills_reported: AtomicU64,
}

impl TestTradingEngine {
    fn new() -> Self {
        Self {
            orders_submitted: AtomicU64::new(0),
            fills_reported: AtomicU64::new(0),
        }
    }
    
    fn submit_order(&self, _order: TestOrder) {
        self.orders_submitted.fetch_add(1, Ordering::Relaxed);
    }
    
    fn report_fill(&self, _order_id: u64, _fill_quantity: u64) {
        self.fills_reported.fetch_add(1, Ordering::Relaxed);
    }
}

#[derive(Debug)]
struct TestConcurrentProcessor {
    processed_count: AtomicU64,
}

impl TestConcurrentProcessor {
    fn new() -> Self {
        Self {
            processed_count: AtomicU64::new(0),
        }
    }
    
    fn process_order_concurrent(&self, _order: &TestOrder) -> Result<(), String> {
        self.processed_count.fetch_add(1, Ordering::Relaxed);
        Ok(())
    }
}

#[derive(Debug)]
struct TestFaultTolerantSystem {
    db_connected: std::sync::atomic::AtomicBool,
    feed_connected: std::sync::atomic::AtomicBool,
    broker_connected: std::sync::atomic::AtomicBool,
}

impl TestFaultTolerantSystem {
    fn new() -> Self {
        Self {
            db_connected: std::sync::atomic::AtomicBool::new(true),
            feed_connected: std::sync::atomic::AtomicBool::new(true),
            broker_connected: std::sync::atomic::AtomicBool::new(true),
        }
    }
    
    fn simulate_database_failure(&self) {
        self.db_connected.store(false, Ordering::Release);
        // Simulate recovery after 20ms
        thread::spawn(|| {
            thread::sleep(Duration::from_millis(20));
        });
    }
    
    fn wait_for_recovery(&self, timeout: Duration) -> Result<(), String> {
        let start = Instant::now();
        while start.elapsed() < timeout {
            if start.elapsed() > Duration::from_millis(20) {
                self.db_connected.store(true, Ordering::Release);
                return Ok(());
            }
            thread::sleep(Duration::from_millis(1));
        }
        Err("Recovery timeout".to_string())
    }
    
    fn simulate_feed_disruption(&self) {
        self.feed_connected.store(false, Ordering::Release);
    }
    
    fn wait_for_feed_recovery(&self, timeout: Duration) -> Result<(), String> {
        let start = Instant::now();
        while start.elapsed() < timeout {
            if start.elapsed() > Duration::from_millis(50) {
                self.feed_connected.store(true, Ordering::Release);
                return Ok(());
            }
            thread::sleep(Duration::from_millis(1));
        }
        Err("Feed recovery timeout".to_string())
    }
    
    fn simulate_broker_disconnect(&self) {
        self.broker_connected.store(false, Ordering::Release);
    }
    
    fn wait_for_failover(&self, timeout: Duration) -> Result<(), String> {
        let start = Instant::now();
        while start.elapsed() < timeout {
            if start.elapsed() > Duration::from_millis(100) {
                self.broker_connected.store(true, Ordering::Release);
                return Ok(());
            }
            thread::sleep(Duration::from_millis(1));
        }
        Err("Failover timeout".to_string())
    }
}

#[derive(Debug)]
struct TestStressSystem;

#[derive(Debug, Clone)]
struct PerformanceMetrics {
    avg_latency: Duration,
    success_rate: f64,
    throughput: f64,
}

impl TestStressSystem {
    fn new() -> Self { Self }
    
    fn measure_performance_at_load(&self, load_level: u32, duration: Duration) -> PerformanceMetrics {
        let start_time = Instant::now();
        let mut total_latency = Duration::ZERO;
        let mut successful_operations = 0u32;
        let mut total_operations = 0u32;
        
        while start_time.elapsed() < duration {
            for _ in 0..load_level {
                total_operations += 1;
                
                let op_start = Instant::now();
                // Simulate operation with increasing latency based on load
                let simulated_latency = Duration::from_nanos(500 + (load_level as u64 * 10));
                std::thread::sleep(simulated_latency / 1000); // Sleep for fraction to simulate work
                
                let latency = op_start.elapsed();
                total_latency += latency;
                
                // Simulate occasional failures at high load
                if load_level > 50_000 && rand::random::<f64>() < 0.01 {
                    // 1% failure rate at high load
                } else {
                    successful_operations += 1;
                }
            }
            
            // Brief pause between load bursts
            thread::sleep(Duration::from_micros(100));
        }
        
        let avg_latency = if total_operations > 0 {
            total_latency / total_operations
        } else {
            Duration::ZERO
        };
        
        let success_rate = if total_operations > 0 {
            successful_operations as f64 / total_operations as f64
        } else {
            0.0
        };
        
        let throughput = total_operations as f64 / duration.as_secs_f64();
        
        PerformanceMetrics {
            avg_latency,
            success_rate,
            throughput,
        }
    }
}

#[derive(Debug)]
struct TestCacheSystem {
    cache: std::sync::Mutex<std::collections::HashMap<String, TestCacheValue>>,
}

#[derive(Debug, Clone)]
struct TestCacheValue {
    data: Vec<u8>,
    timestamp: std::time::SystemTime,
}

impl TestCacheSystem {
    fn new() -> Self {
        Self {
            cache: std::sync::Mutex::new(std::collections::HashMap::new()),
        }
    }
    
    fn get(&self, key: &str) -> Option<TestCacheValue> {
        let cache = self.cache.lock().unwrap();
        cache.get(key).cloned()
    }
    
    fn put(&self, key: &str, value: TestCacheValue) {
        let mut cache = self.cache.lock().unwrap();
        cache.insert(key.to_string(), value);
    }
}