//! Test utilities and helper functions
//! 
//! Common testing utilities shared across all test modules.

// CANONICAL TYPE IMPORTS - Use types::prelude::Decimal
use std::collections::HashMap;
use num_traits::FromPrimitive; // For Decimal::from_f64

/// Test data generators for consistent test scenarios
pub struct TestDataGenerator {
    order_counter: std::sync::atomic::AtomicU64,
    trade_counter: std::sync::atomic::AtomicU64,
}

impl TestDataGenerator {
    pub fn new() -> Self {
        Self {
            order_counter: std::sync::atomic::AtomicU64::new(1),
            trade_counter: std::sync::atomic::AtomicU64::new(1),
        }
    }
    
    pub fn generate_order(&self, symbol: &str, side: Side, quantity: u32, price: Option<f64>) -> Order {
        let order_id = self.order_counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
        
        Order {
            id: OrderId::new(format!("TEST-ORDER-{:06}", order_id)),
            symbol: Symbol::new(symbol.to_string()).map_err(|e| format!("Failed to create symbol: {}", e)).unwrap(),
            side,
            order_type: if price.is_some() { OrderType::Limit } else { OrderType::Market },
            quantity: Quantity::new(Decimal::from(quantity)).map_err(|e| format!("Failed to create quantity: {}", e)).unwrap(),
            price: price.map(|p| Price::new(Decimal::from_f64_retain(p).unwrap()).map_err(|e| format!("Failed to create price: {}", e)).unwrap()),
            time_in_force: TimeInForce::Day,
            timestamp: chrono::Utc::now(),
            status: OrderStatus::New,
            client_id: ClientId::new("TEST-CLIENT".to_string()),
        }
    }
    
    pub fn generate_fill(&self, order: &Order, fill_quantity: Option<u32>, fill_price: Option<f64>) -> Fill {
        let trade_id = self.trade_counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
        let fill_id = format!("FILL-{:06}", trade_id);
        
        let quantity = fill_quantity
            .map(|q| Quantity::new(Decimal::from(q)).map_err(|e| format!("Failed to create fill quantity: {}", e)).unwrap())
            .unwrap_or(order.quantity.clone());

        let price = fill_price
            .map(|p| Price::new(Decimal::from_f64_retain(p).unwrap()).map_err(|e| format!("Failed to create fill price: {}", e)).unwrap())
            .or_else(|| order.price.clone())
            .unwrap_or_else(|| Price::new(Decimal::from(100)).map_err(|e| format!("Failed to create default price: {}", e)).unwrap());
        
        Fill {
            id: FillId::new(fill_id),
            order_id: order.id.clone(),
            trade_id: TradeId::new(format!("TRADE-{:06}", trade_id)),
            symbol: order.symbol.clone(),
            side: order.side,
            quantity,
            price,
            timestamp: chrono::Utc::now(),
            commission: Some(Money::new(Decimal::from_str("1.00").map_err(|e| format!("Failed to create commission decimal: {}", e)).unwrap(), Currency::USD)),
        }
    }
    
    pub fn generate_position(&self, symbol: &str, quantity: i32, avg_price: f64) -> Position {
        Position {
            symbol: Symbol::new(symbol.to_string()).map_err(|e| format!("Failed to create symbol: {}", e)).unwrap(),
            quantity: Quantity::new(Decimal::from(quantity.abs())).map_err(|e| format!("Failed to create quantity: {}", e)).unwrap(),
            average_price: Price::new(Decimal::from_f64_retain(avg_price).unwrap()).map_err(|e| format!("Failed to create average price: {}", e)).unwrap(),
            realized_pnl: PnL::new(Decimal::ZERO).map_err(|e| format!("Failed to create realized PnL: {}", e)).unwrap(),
            unrealized_pnl: PnL::new(Decimal::ZERO).map_err(|e| format!("Failed to create unrealized PnL: {}", e)).unwrap(),
            last_updated: chrono::Utc::now(),
        }
    }
    
    pub fn generate_market_data_event(&self, symbol: &str, price: f64, volume: u64) -> MarketDataEvent {
        MarketDataEvent {
            symbol: Symbol::new(symbol.to_string()).map_err(|e| format!("Failed to create symbol: {}", e)).unwrap(),
            price: Price::new(Decimal::from_f64_retain(price).unwrap()).map_err(|e| format!("Failed to create price: {}", e)).unwrap(),
            timestamp: chrono::Utc::now(),
            volume: Volume::new(Decimal::from(volume)).map_err(|e| format!("Failed to create volume: {}", e)).unwrap(),
        }
    }
}

impl Default for TestDataGenerator {
    fn default() -> Self {
        Self::new()
    }
}

/// Mock market data provider for testing
pub struct MockMarketDataProvider {
    prices: HashMap<Symbol, Price>,
    volumes: HashMap<Symbol, Volume>,
    subscribers: Vec<tokio::sync::mpsc::UnboundedSender<MarketDataEvent>>,
}

impl MockMarketDataProvider {
    pub fn new() -> Self {
        Self {
            prices: HashMap::new(),
            volumes: HashMap::new(),
            subscribers: Vec::new(),
        }
    }
    
    pub fn set_price(&mut self, symbol: Symbol, price: Price) {
        self.prices.insert(symbol.clone(), price.clone());
        
        let event = MarketDataEvent {
            symbol,
            price,
            timestamp: chrono::Utc::now(),
            volume: Volume::new(Decimal::from(1000)).map_err(|e| format!("Failed to create volume: {}", e)).unwrap(),
        };
        
        // Notify subscribers
        self.subscribers.retain(|sender| {
            sender.send(event.clone()).is_ok()
        });
    }
    
    pub fn get_price(&self, symbol: &Symbol) -> Option<&Price> {
        self.prices.get(symbol)
    }
    
    pub fn subscribe(&mut self) -> tokio::sync::mpsc::UnboundedReceiver<MarketDataEvent> {
        let (sender, receiver) = tokio::sync::mpsc::unbounded_channel();
        self.subscribers.push(sender);
        receiver
    }
}

/// Test assertions for financial calculations
pub struct FinancialAssertions;

impl FinancialAssertions {
    pub fn assert_price_equal(actual: &Price, expected: &Price, tolerance: Option<Decimal>) {
        let tolerance = tolerance.unwrap_or_else(|| Decimal::from_str("0.01").map_err(|e| format!("Failed to create tolerance: {}", e)).unwrap());
        let diff = (actual.value() - expected.value()).abs();
        assert!(
            diff <= tolerance,
            "Price assertion failed: actual={}, expected={}, tolerance={}, diff={}",
            actual.value(),
            expected.value(),
            tolerance,
            diff
        );
    }
    
    pub fn assert_quantity_equal(actual: &Quantity, expected: &Quantity, tolerance: Option<Decimal>) {
        let tolerance = tolerance.unwrap_or_else(|| Decimal::from_str("0.000001").map_err(|e| format!("Failed to create tolerance: {}", e)).unwrap());
        let diff = (actual.value() - expected.value()).abs();
        assert!(
            diff <= tolerance,
            "Quantity assertion failed: actual={}, expected={}, tolerance={}, diff={}",
            actual.value(),
            expected.value(),
            tolerance,
            diff
        );
    }
    
    pub fn assert_pnl_equal(actual: &PnL, expected: &PnL, tolerance: Option<Decimal>) {
        let tolerance = tolerance.unwrap_or_else(|| Decimal::from_str("0.01").map_err(|e| format!("Failed to create tolerance: {}", e)).unwrap());
        let diff = (actual.value() - expected.value()).abs();
        assert!(
            diff <= tolerance,
            "PnL assertion failed: actual={}, expected={}, tolerance={}, diff={}",
            actual.value(),
            expected.value(),
            tolerance,
            diff
        );
    }
    
    pub fn assert_money_equal(actual: &Money, expected: &Money, tolerance: Option<Decimal>) {
        assert_eq!(actual.currency, expected.currency, "Currency mismatch");

        let tolerance = tolerance.unwrap_or_else(|| Decimal::from_str("0.01").map_err(|e| format!("Failed to create tolerance: {}", e)).unwrap());
        let diff = (actual.amount - expected.amount).abs();
        assert!(
            diff <= tolerance,
            "Money assertion failed: actual={}, expected={}, tolerance={}, diff={}",
            actual.amount,
            expected.amount,
            tolerance,
            diff
        );
    }
    
    pub fn assert_percentage_equal(actual: Decimal, expected: Decimal, tolerance: Option<Decimal>) {
        let tolerance = tolerance.unwrap_or_else(|| Decimal::from_str("0.01").map_err(|e| format!("Failed to create tolerance: {}", e)).unwrap()); // 1%
        let diff = (actual - expected).abs();
        assert!(
            diff <= tolerance,
            "Percentage assertion failed: actual={}%, expected={}%, tolerance={}%, diff={}%",
            actual * Decimal::from(100),
            expected * Decimal::from(100),
            tolerance * Decimal::from(100),
            diff * Decimal::from(100)
        );
    }
}

/// Performance measurement utilities for tests
pub struct PerformanceMeasurement {
    measurements: HashMap<String, std::time::Duration>,
}

impl PerformanceMeasurement {
    pub fn new() -> Self {
        Self {
            measurements: HashMap::new(),
        }
    }
    
    pub fn measure<F, R>(&mut self, name: &str, f: F) -> R
    where
        F: FnOnce() -> R,
    {
        let start = std::time::Instant::now();
        let result = f();
        let duration = start.elapsed();
        
        self.measurements.insert(name.to_string(), duration);
        result
    }
    
    pub async fn measure_async<F, Fut, R>(&mut self, name: &str, f: F) -> R
    where
        F: FnOnce() -> Fut,
        Fut: std::future::Future<Output = R>,
    {
        let start = std::time::Instant::now();
        let result = f().await;
        let duration = start.elapsed();
        
        self.measurements.insert(name.to_string(), duration);
        result
    }
    
    pub fn get_measurement(&self, name: &str) -> Option<std::time::Duration> {
        self.measurements.get(name).copied()
    }
    
    pub fn assert_performance(&self, name: &str, max_duration: std::time::Duration) {
        if let Some(actual) = self.get_measurement(name) {
            assert!(
                actual <= max_duration,
                "Performance assertion failed for '{}': actual={:?}, max={:?}",
                name, actual, max_duration
            );
        } else {
            panic!("No measurement found for '{}'", name);
        }
    }
    
    pub fn print_summary(&self) {
        println!("\n=== Performance Summary ===");
        for (name, duration) in &self.measurements {
            println!("{}: {:?}", name, duration);
        }
        println!("==========================\n");
    }
}

impl Default for PerformanceMeasurement {
    fn default() -> Self {
        Self::new()
    }
}

/// Test scenario builders for complex integration tests
pub struct TestScenarioBuilder {
    orders: Vec<Order>,
    market_events: Vec<MarketDataEvent>,
    expected_fills: Vec<Fill>,
    generator: TestDataGenerator,
}

impl TestScenarioBuilder {
    pub fn new() -> Self {
        Self {
            orders: Vec::new(),
            market_events: Vec::new(),
            expected_fills: Vec::new(),
            generator: TestDataGenerator::new(),
        }
    }
    
    pub fn with_buy_order(mut self, symbol: &str, quantity: u32, price: f64) -> Self {
        let order = self.generator.generate_order(symbol, Side::Buy, quantity, Some(price));
        self.orders.push(order);
        self
    }
    
    pub fn with_sell_order(mut self, symbol: &str, quantity: u32, price: f64) -> Self {
        let order = self.generator.generate_order(symbol, Side::Sell, quantity, Some(price));
        self.orders.push(order);
        self
    }
    
    pub fn with_market_data(mut self, symbol: &str, price: f64, volume: u64) -> Self {
        let event = self.generator.generate_market_data_event(symbol, price, volume);
        self.market_events.push(event);
        self
    }
    
    pub fn expect_fill(mut self, order_index: usize, quantity: Option<u32>, price: Option<f64>) -> Self {
        if let Some(order) = self.orders.get(order_index) {
            let fill = self.generator.generate_fill(order, quantity, price);
            self.expected_fills.push(fill);
        }
        self
    }
    
    pub fn build(self) -> TestScenario {
        TestScenario {
            orders: self.orders,
            market_events: self.market_events,
            expected_fills: self.expected_fills,
        }
    }
}

impl Default for TestScenarioBuilder {
    fn default() -> Self {
        Self::new()
    }
}

pub struct TestScenario {
    pub orders: Vec<Order>,
    pub market_events: Vec<MarketDataEvent>,
    pub expected_fills: Vec<Fill>,
}

/// Database test utilities
pub struct DatabaseTestUtils;

impl DatabaseTestUtils {
    pub async fn setup_test_database() -> Result<String, Box<dyn std::error::Error>> {
        // Return a test database connection string
        // In a real implementation, this would set up a temporary database
        Ok(std::env::var("TEST_DATABASE_URL").unwrap_or_else(|_| "postgresql://localhost/foxhunt_test".to_string()))
    }
    
    pub async fn cleanup_test_database(_connection_string: &str) -> Result<(), Box<dyn std::error::Error>> {
        // Clean up test database
        Ok(())
    }
    
    pub async fn insert_test_data<T>(_connection: &str, _data: &[T]) -> Result<(), Box<dyn std::error::Error>>
    where
        T: serde::Serialize,
    {
        // Insert test data into database
        Ok(())
    }
}

/// Concurrency test utilities
pub struct ConcurrencyTestUtils;

impl ConcurrencyTestUtils {
    pub async fn run_concurrent_operations<F, Fut, R>(
        operations: Vec<F>,
        max_concurrent: usize,
    ) -> Vec<Result<R, Box<dyn std::error::Error + Send + Sync>>>
    where
        F: FnOnce() -> Fut + Send + 'static,
        Fut: std::future::Future<Output = Result<R, Box<dyn std::error::Error + Send + Sync>>> + Send + 'static,
        R: Send + 'static,
    {
        use tokio::sync::Semaphore;
        use std::sync::Arc;
        
        let semaphore = Arc::new(Semaphore::new(max_concurrent));
        let mut handles = Vec::new();
        
        for operation in operations {
            let permit = semaphore.clone().acquire_owned().await.unwrap();
            let handle = tokio::spawn(async move {
                let _permit = permit;
                operation().await
            });
            handles.push(handle);
        }
        
        let mut results = Vec::new();
        for handle in handles {
            match handle.await {
                Ok(result) => results.push(result),
                Err(e) => results.push(Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)),
            }
        }
        
        results
    }
    
    pub async fn stress_test_operation<F, Fut, R>(
        operation: F,
        num_iterations: usize,
        max_concurrent: usize,
    ) -> StressTestResults<R>
    where
        F: Fn() -> Fut + Send + Sync + 'static,
        Fut: std::future::Future<Output = Result<R, Box<dyn std::error::Error + Send + Sync>>> + Send + 'static,
        R: Send + 'static,
        F: Clone,
    {
        let start_time = std::time::Instant::now();
        let operations = (0..num_iterations).map(|_| operation.clone()).collect();
        let results = Self::run_concurrent_operations(operations, max_concurrent).await;
        let total_duration = start_time.elapsed();
        
        let successful = results.iter().filter(|r| r.is_ok()).count();
        let failed = results.len() - successful;
        
        StressTestResults {
            total_operations: num_iterations,
            successful_operations: successful,
            failed_operations: failed,
            total_duration,
            operations_per_second: num_iterations as f64 / total_duration.as_secs_f64(),
            results,
        }
    }
}

pub struct StressTestResults<R> {
    pub total_operations: usize,
    pub successful_operations: usize,
    pub failed_operations: usize,
    pub total_duration: std::time::Duration,
    pub operations_per_second: f64,
    pub results: Vec<Result<R, Box<dyn std::error::Error + Send + Sync>>>,
}

impl<R> StressTestResults<R> {
    pub fn success_rate(&self) -> f64 {
        self.successful_operations as f64 / self.total_operations as f64
    }
    
    pub fn assert_success_rate(&self, minimum_rate: f64) {
        let actual_rate = self.success_rate();
        assert!(
            actual_rate >= minimum_rate,
            "Success rate too low: actual={:.2}%, minimum={:.2}%",
            actual_rate * 100.0,
            minimum_rate * 100.0
        );
    }
    
    pub fn assert_throughput(&self, minimum_ops_per_sec: f64) {
        assert!(
            self.operations_per_second >= minimum_ops_per_sec,
            "Throughput too low: actual={:.2} ops/sec, minimum={:.2} ops/sec",
            self.operations_per_second,
            minimum_ops_per_sec
        );
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[tokio::test]
    async fn test_data_generator() {
        let generator = TestDataGenerator::new();
        
        let order1 = generator.generate_order("AAPL", Side::Buy, 100, Some(150.0));
        let order2 = generator.generate_order("MSFT", Side::Sell, 200, Some(300.0));
        
        // Orders should have unique IDs
        assert_ne!(order1.id.value(), order2.id.value());
        
        // Order properties should match inputs
        assert_eq!(order1.symbol.as_str(), "AAPL");
        assert_eq!(order1.side, Side::Buy);
        assert_eq!(order1.quantity.value(), Decimal::from(100));
        assert_eq!(order1.price.as_ref().unwrap().value(), Decimal::from(150));
    }
    
    #[tokio::test]
    async fn test_financial_assertions() {
        let price1 = Price::new(Decimal::from_str("150.00").unwrap()).map_err(|e| format!("Failed to create test price1: {}", e)).unwrap();
        let price2 = Price::new(Decimal::from_str("150.01").unwrap()).map_err(|e| format!("Failed to create test price2: {}", e)).unwrap();
        
        // Should pass with default tolerance
        FinancialAssertions::assert_price_equal(&price1, &price2, None);
        
        // Should pass with custom tolerance
        FinancialAssertions::assert_price_equal(
            &price1,
            &price2,
            Some(Decimal::from_str("0.02").map_err(|e| format!("Failed to create tolerance decimal: {}", e)).unwrap())
        );
    }
    
    #[test]
    fn test_performance_measurement() {
        let mut perf = PerformanceMeasurement::new();
        
        let result = perf.measure("test_operation", || {
            std::thread::sleep(std::time::Duration::from_millis(10));
            42
        });
        
        assert_eq!(result, 42);
        
        let measurement = perf.get_measurement("test_operation");
        assert!(measurement.is_some());
        assert!(measurement.unwrap() >= std::time::Duration::from_millis(10));
        
        // Should assert performance within reasonable bounds
        perf.assert_performance("test_operation", std::time::Duration::from_millis(50));
    }
    
    #[tokio::test]
    async fn test_scenario_builder() {
        let scenario = TestScenarioBuilder::new()
            .with_buy_order("AAPL", 100, 150.0)
            .with_sell_order("AAPL", 50, 151.0)
            .with_market_data("AAPL", 150.5, 10000)
            .expect_fill(0, Some(50), Some(150.5))
            .build();
        
        assert_eq!(scenario.orders.len(), 2);
        assert_eq!(scenario.market_events.len(), 1);
        assert_eq!(scenario.expected_fills.len(), 1);
        
        // Verify scenario structure
        assert_eq!(scenario.orders[0].side, Side::Buy);
        assert_eq!(scenario.orders[1].side, Side::Sell);
        assert_eq!(scenario.market_events[0].symbol.as_str(), "AAPL");
        assert_eq!(scenario.expected_fills[0].quantity.value(), Decimal::from(50));
    }
}