//! HFT Specific Test Utilities
//!
//! Specialized testing utilities for high frequency trading components
//! with focus on performance, latency, and financial accuracy.

use super::test_safety::{TestError, TestResult};
use ::rust_decimal::Decimal;
use chrono::{DateTime, Utc};
use std::collections::VecDeque;
use std::time::{Duration, Instant};

/// Performance measurement utilities for HFT testing
pub mod performance {
    use super::*;

    /// Latency measurement with statistical analysis
    ///
    /// Collects latency measurements and provides statistical analysis
    /// including average, percentiles, min, and max values.
    #[derive(Debug)]
    pub struct LatencyMeasurement {
        /// Collection of latency measurements
        measurements: VecDeque<Duration>,
        /// Maximum number of samples to retain
        max_samples: usize,
    }

    impl LatencyMeasurement {
        /// Create a new latency measurement collector
        ///
        /// # Arguments
        /// * `max_samples` - Maximum number of samples to retain in memory
        pub fn new(max_samples: usize) -> Self {
            Self {
                measurements: VecDeque::with_capacity(max_samples),
                max_samples,
            }
        }

        /// Record a new latency measurement
        ///
        /// If the maximum number of samples is reached, the oldest sample is removed.
        ///
        /// # Arguments
        /// * `latency` - The latency duration to record
        pub fn record(&mut self, latency: Duration) {
            if self.measurements.len() >= self.max_samples {
                self.measurements.pop_front();
            }
            self.measurements.push_back(latency);
        }

        /// Calculate the average latency across all recorded measurements
        ///
        /// # Returns
        /// * `Some(Duration)` - The average latency if measurements exist
        ///
        /// * `None` - If no measurements have been recorded
        pub fn average(&self) -> Option<Duration> {
            if self.measurements.is_empty() {
                return None;
            }

            let total_nanos: u64 = self.measurements.iter().map(|d| d.as_nanos() as u64).sum();

            Some(Duration::from_nanos(
                total_nanos / self.measurements.len() as u64,
            ))
        }

        /// Calculate the specified percentile latency
        ///
        /// # Arguments
        /// * `p` - Percentile value between 0.0 and 1.0 (e.g., 0.99 for 99th percentile)
        ///
        /// # Returns
        /// * `Some(Duration)` - The percentile latency if measurements exist
        ///
        /// * `None` - If no measurements have been recorded
        pub fn percentile(&self, p: f64) -> Option<Duration> {
            if self.measurements.is_empty() {
                return None;
            }

            let mut sorted: Vec<Duration> = self.measurements.iter().copied().collect();
            sorted.sort();

            let index = ((sorted.len() as f64 - 1.0) * p).round() as usize;
            sorted.get(index).copied()
        }

        /// Get the maximum latency from all recorded measurements
        ///
        /// # Returns
        /// * `Some(Duration)` - The maximum latency if measurements exist
        ///
        /// * `None` - If no measurements have been recorded
        pub fn max(&self) -> Option<Duration> {
            self.measurements.iter().max().copied()
        }

        /// Get the minimum latency from all recorded measurements
        ///
        /// # Returns
        /// * `Some(Duration)` - The minimum latency if measurements exist
        ///
        /// * `None` - If no measurements have been recorded
        pub fn min(&self) -> Option<Duration> {
            self.measurements.iter().min().copied()
        }
    }

    /// Measure operation latency with safety checks
    ///
    /// Executes an operation and measures its execution time with proper error handling.
    ///
    /// # Arguments
    /// * `operation` - The async operation to measure
    ///
    /// * `context` - Description of the operation for error reporting
    ///
    /// # Returns
    /// * `Ok((T, Duration))` - The operation result and its execution time
    ///
    /// * `Err(TestError)` - If the operation fails
    pub async fn measure_latency<F, T, Fut>(
        operation: F,
        context: &str,
    ) -> TestResult<(T, Duration)>
    where
        F: FnOnce() -> Fut,
        Fut: std::future::Future<Output = TestResult<T>>,
    {
        let start = Instant::now();
        let result = operation().await?;
        let latency = start.elapsed();

        println!("Operation '{}' completed in {:?}", context, latency);
        Ok((result, latency))
    }

    /// Validate HFT latency requirements (sub-microsecond)
    ///
    /// Checks if a measured latency meets HFT performance requirements.
    ///
    /// # Arguments
    /// * `latency` - The measured latency to validate
    ///
    /// * `max_allowed` - Maximum allowed latency for HFT operations
    /// * `context` - Description of the operation for error reporting
    ///
    /// # Returns
    /// * `Ok(())` - If latency meets requirements
    ///
    /// * `Err(TestError)` - If latency exceeds maximum allowed
    pub fn validate_hft_latency(
        latency: Duration,
        max_allowed: Duration,
        context: &str,
    ) -> TestResult<()> {
        if latency > max_allowed {
            return Err(TestError::assertion(format!(
                "{}: Latency {:?} exceeds HFT requirement of {:?}",
                context, latency, max_allowed
            )));
        }
        Ok(())
    }

    /// Benchmark operation with warmup and multiple iterations
    ///
    /// Performs a comprehensive benchmark with warmup phase and statistical measurement.
    ///
    /// # Arguments
    /// * `operation` - The operation to benchmark (must be cloneable for multiple runs)
    ///
    /// * `warmup_iterations` - Number of warmup iterations to run before measurement
    /// * `measurement_iterations` - Number of iterations to measure for statistics
    ///
    /// * `context` - Description of the operation for error reporting
    ///
    /// # Returns
    /// * `Ok(LatencyMeasurement)` - Statistical analysis of the benchmark results
    ///
    /// * `Err(TestError)` - If any iteration fails
    pub async fn benchmark_operation<F, T, Fut>(
        operation: F,
        warmup_iterations: usize,
        measurement_iterations: usize,
        context: &str,
    ) -> TestResult<LatencyMeasurement>
    where
        F: Fn() -> Fut + Clone,
        Fut: std::future::Future<Output = TestResult<T>>,
    {
        // Warmup phase
        for _ in 0..warmup_iterations {
            operation().await.map_err(|e| {
                TestError::setup(format!("Benchmark warmup failed for {}: {}", context, e))
            })?;
        }

        // Measurement phase
        let mut measurements = LatencyMeasurement::new(measurement_iterations);
        for _ in 0..measurement_iterations {
            let (_, latency) = measure_latency(operation.clone(), context).await?;
            measurements.record(latency);
        }

        Ok(measurements)
    }
}

/// Financial accuracy testing utilities
pub mod financial {
    use super::*;

    /// Precision threshold for financial calculations
    ///
    /// This constant defines the maximum acceptable difference between
    /// two financial amounts for them to be considered equal.
    pub const FINANCIAL_PRECISION: f64 = 1e-8;

    /// Safe comparison of financial amounts with precision handling
    ///
    /// Compares two Decimal values for equality within the financial precision threshold.
    ///
    /// This is essential for testing financial calculations where floating-point precision
    /// errors can cause exact equality comparisons to fail.
    ///
    /// # Arguments
    /// * `left` - First decimal value to compare
    ///
    /// * `right` - Second decimal value to compare
    /// * `context` - Description of the comparison for error reporting
    ///
    /// # Returns
    /// * `Ok(())` - If values are equal within precision
    ///
    /// * `Err(TestError)` - If values differ beyond acceptable precision
    pub fn assert_decimal_eq(left: Decimal, right: Decimal, context: &str) -> TestResult<()> {
        let diff = (left - right).abs();
        let precision_decimal = Decimal::try_from(FINANCIAL_PRECISION)
            .map_err(|_| TestError::setup("Failed to create precision decimal"))?;

        if diff > precision_decimal {
            return Err(TestError::assertion(format!(
                "{}: Financial amounts not equal within precision\n  left: {}\n  right: {}\n  diff: {}\n  max_allowed: {}",
                context, left, right, diff, precision_decimal
            )));
        }
        Ok(())
    }

    /// Generate test prices with realistic market behavior
    ///
    /// Creates a sequence of realistic price movements for testing market data processing.
    ///
    /// Uses random walk with specified volatility to simulate real market conditions.
    ///
    /// # Arguments
    /// * `base_price` - Starting price for the sequence
    ///
    /// * `count` - Number of price points to generate
    /// * `volatility` - Volatility as a percentage (e.g., 0.02 for 2% volatility)
    ///
    /// # Returns
    /// * `Ok(Vec<Decimal>)` - Sequence of generated prices
    ///
    /// * `Err(TestError)` - If price generation fails
    pub fn generate_realistic_prices(
        base_price: Decimal,
        count: usize,
        volatility: f64,
    ) -> TestResult<Vec<Decimal>> {
        use rand::Rng;
        let mut rng = rand::thread_rng();
        let mut prices = Vec::with_capacity(count);
        let mut current_price = base_price;

        for _ in 0..count {
            let change_pct = rng.gen_range(-volatility..volatility);
            let change_decimal = Decimal::try_from(change_pct / 100.0)
                .map_err(|_| TestError::setup("Failed to create change decimal"))?;
            let change = current_price * change_decimal;
            current_price =
                (current_price + change).max(Decimal::try_from(0.01).unwrap_or_default());
            prices.push(current_price);
        }

        Ok(prices)
    }
}

/// Market data testing utilities
pub mod market_data {
    use super::*;

    /// Mock market data tick for testing
    ///
    /// Represents a single market data tick with price, volume, and optional bid/ask spread.
    ///
    /// Used for testing market data processing and trading algorithms.
    #[derive(Debug, Clone)]
    pub struct TestTick {
        /// Trading symbol (e.g., "BTCUSD", "AAPL")
        pub symbol: String,
        /// UTC timestamp of the tick
        pub timestamp: DateTime<Utc>,
        /// Last traded price
        pub price: Decimal,
        /// Volume traded at this price
        pub volume: Decimal,
        /// Best bid price (optional)
        pub bid: Option<Decimal>,
        /// Best ask price (optional)
        pub ask: Option<Decimal>,
    }

    impl TestTick {
        /// Create a new test tick with basic price and volume
        ///
        /// # Arguments
        /// * `symbol` - Trading symbol for this tick
        ///
        /// * `price` - Last traded price
        /// * `volume` - Volume traded
        pub fn new(symbol: &str, price: Decimal, volume: Decimal) -> Self {
            Self {
                symbol: symbol.to_string(),
                timestamp: Utc::now(),
                price,
                volume,
                bid: None,
                ask: None,
            }
        }

        /// Add bid/ask spread to the tick
        ///
        /// # Arguments
        /// * `bid` - Best bid price
        ///
        /// * `ask` - Best ask price
        ///
        /// # Returns
        ///
        /// Self with bid/ask prices set
        pub fn with_spread(mut self, bid: Decimal, ask: Decimal) -> Self {
            self.bid = Some(bid);
            self.ask = Some(ask);
            self
        }
    }

    /// Generate realistic market data stream for testing
    ///
    /// Provides a configurable market data generator that produces realistic
    /// price movements and tick patterns for testing trading algorithms.
    #[derive(Debug)]
    pub struct TestMarketDataStream {
        /// List of symbols to generate data for
        symbols: Vec<String>,
        /// Base prices for each symbol
        base_prices: std::collections::HashMap<String, Decimal>,
        /// Tick generation rate in Hz
        tick_rate_hz: u64,
    }

    impl TestMarketDataStream {
        /// Create a new market data stream generator
        ///
        /// # Arguments
        /// * `symbols` - List of trading symbols to generate data for
        ///
        /// * `tick_rate_hz` - Rate of tick generation in Hz
        pub fn new(symbols: Vec<String>, tick_rate_hz: u64) -> Self {
            let mut base_prices = std::collections::HashMap::new();
            for symbol in &symbols {
                // Set realistic base prices for different asset types
                let base_price = match symbol.as_str() {
                    s if s.contains("USD") => Decimal::try_from(1.2).unwrap_or_default(),
                    s if s.starts_with("BTC") => Decimal::try_from(45000.0).unwrap_or_default(),
                    _ => Decimal::try_from(100.0).unwrap_or_default(), // Default stock price
                };
                base_prices.insert(symbol.clone(), base_price);
            }

            Self {
                symbols,
                base_prices,
                tick_rate_hz,
            }
        }

        /// Generate market data ticks for the specified duration
        ///
        /// Creates realistic market data with small price variations and consistent timing.
        ///
        /// # Arguments
        /// * `duration` - How long to generate data for
        ///
        /// # Returns
        /// * `Ok(Vec<TestTick>)` - Generated market data ticks
        ///
        /// * `Err(TestError)` - If tick generation fails
        pub async fn generate_ticks(&self, duration: Duration) -> TestResult<Vec<TestTick>> {
            let total_ticks = (duration.as_secs_f64() * self.tick_rate_hz as f64) as usize;
            let mut ticks = Vec::with_capacity(total_ticks);
            let tick_interval = Duration::from_nanos(1_000_000_000 / self.tick_rate_hz);

            for i in 0..total_ticks {
                for symbol in &self.symbols {
                    let base_price = self.base_prices.get(symbol).ok_or_else(|| {
                        TestError::setup(format!("No base price for symbol {}", symbol))
                    })?;

                    // Add small random variation
                    let variation = (i as f64 * 0.001).sin() * 0.001; // Small deterministic variation
                    let variation_decimal = Decimal::try_from(variation).unwrap_or_default();
                    let price = *base_price * (Decimal::ONE + variation_decimal);

                    let volume = Decimal::from((100 + i) % 1000);
                    let tick = TestTick::new(symbol, price, volume);
                    ticks.push(tick);
                }

                // Simulate real-time tick generation
                if i % 100 == 0 {
                    tokio::time::sleep(tick_interval).await;
                }
            }

            Ok(ticks)
        }
    }

    /// Validate market data consistency
    ///
    /// Performs comprehensive validation of a tick sequence including:
    /// - Timestamp ordering
    ///
    /// - Realistic price movements (no more than 50% jumps)
    /// - Data integrity checks
    ///
    /// # Arguments
    /// * `ticks` - Sequence of ticks to validate
    ///
    /// * `context` - Description for error reporting
    ///
    /// # Returns
    /// * `Ok(())` - If all validation checks pass
    ///
    /// * `Err(TestError)` - If any validation fails
    pub fn validate_tick_sequence(ticks: &[TestTick], context: &str) -> TestResult<()> {
        if ticks.is_empty() {
            return Err(TestError::assertion(format!(
                "{}: Empty tick sequence",
                context
            )));
        }

        // Check timestamp ordering
        for window in ticks.windows(2) {
            if window[1].timestamp < window[0].timestamp {
                return Err(TestError::assertion(format!(
                    "{}: Timestamps out of order: {} -> {}",
                    context, window[0].timestamp, window[1].timestamp
                )));
            }
        }

        // Check for unrealistic price movements (>50% in single tick)
        for window in ticks.windows(2) {
            if window[0].symbol == window[1].symbol {
                let price_change = ((window[1].price - window[0].price) / window[0].price).abs();
                let fifty_percent = Decimal::try_from(0.5).unwrap_or_default();
                if price_change > fifty_percent {
                    return Err(TestError::assertion(format!(
                        "{}: Unrealistic price movement in {}: {} -> {} ({:.2}%)",
                        context,
                        window[0].symbol,
                        window[0].price,
                        window[1].price,
                        price_change * Decimal::from(100)
                    )));
                }
            }
        }

        Ok(())
    }
}

/// Order management testing utilities
pub mod orders {
    use super::*;

    // Import order types from common crate
    pub use common::trading::{OrderSide, OrderType};
    pub use common::types::OrderStatus;

    /// Mock order for testing
    ///
    /// Represents a trading order with full lifecycle tracking including
    /// partial fills, status transitions, and average fill price calculation.
    #[derive(Debug, Clone)]
    pub struct TestOrder {
        /// Unique order identifier
        pub id: String,
        /// Trading symbol (e.g., "AAPL", "BTCUSD")
        pub symbol: String,
        /// Order side (Buy or Sell)
        pub side: OrderSide,
        /// Total order quantity
        pub quantity: Decimal,
        /// Limit price (None for market orders)
        pub price: Option<Decimal>,
        /// Order type (Market, Limit, etc.)
        pub order_type: OrderType,
        /// Current order status
        pub status: OrderStatus,
        /// Timestamp when order was created
        pub created_at: DateTime<Utc>,
        /// Quantity that has been filled
        pub filled_quantity: Decimal,
        /// Volume-weighted average fill price
        pub avg_fill_price: Option<Decimal>,
    }

    impl TestOrder {
        /// Create a new market order
        ///
        /// # Arguments
        /// * `symbol` - Trading symbol for the order
        ///
        /// * `side` - Order side (Buy or Sell)
        /// * `quantity` - Order quantity
        ///
        /// # Returns
        ///
        /// A new market order with New status
        pub fn new_market_order(symbol: &str, side: OrderSide, quantity: Decimal) -> Self {
            Self {
                id: uuid::Uuid::new_v4().to_string(),
                symbol: symbol.to_string(),
                side,
                quantity,
                price: None,
                order_type: OrderType::Market,
                status: OrderStatus::New,
                created_at: Utc::now(),
                filled_quantity: Decimal::ZERO,
                avg_fill_price: None,
            }
        }

        /// Create a new limit order
        ///
        /// # Arguments
        /// * `symbol` - Trading symbol for the order
        ///
        /// * `side` - Order side (Buy or Sell)
        /// * `quantity` - Order quantity
        ///
        /// * `price` - Limit price
        ///
        /// # Returns
        ///
        /// A new limit order with New status
        pub fn new_limit_order(
            symbol: &str,
            side: OrderSide,
            quantity: Decimal,
            price: Decimal,
        ) -> Self {
            Self {
                id: uuid::Uuid::new_v4().to_string(),
                symbol: symbol.to_string(),
                side,
                quantity,
                price: Some(price),
                order_type: OrderType::Limit,
                status: OrderStatus::New,
                created_at: Utc::now(),
                filled_quantity: Decimal::ZERO,
                avg_fill_price: None,
            }
        }

        /// Simulate a fill event for this order
        ///
        /// Updates the filled quantity, average fill price, and order status.
        ///
        /// Validates that fills don't exceed the order quantity.
        ///
        /// # Arguments
        /// * `fill_quantity` - Quantity being filled
        ///
        /// * `fill_price` - Price at which the fill occurred
        ///
        /// # Returns
        /// * `Ok(())` - If the fill is valid and processed
        ///
        /// * `Err(TestError)` - If the fill would exceed order quantity or is invalid
        pub fn simulate_fill(
            &mut self,
            fill_quantity: Decimal,
            fill_price: Decimal,
        ) -> TestResult<()> {
            if fill_quantity <= Decimal::ZERO {
                return Err(TestError::assertion("Fill quantity must be positive"));
            }

            if self.filled_quantity + fill_quantity > self.quantity {
                return Err(TestError::assertion(format!(
                    "Fill quantity {} would exceed order quantity {}",
                    fill_quantity, self.quantity
                )));
            }

            // Update average fill price
            if self.filled_quantity == Decimal::ZERO {
                self.avg_fill_price = Some(fill_price);
            } else {
                let current_avg = self.avg_fill_price.unwrap_or_default();
                let total_value = current_avg * self.filled_quantity + fill_price * fill_quantity;
                let new_total_quantity = self.filled_quantity + fill_quantity;
                self.avg_fill_price = Some(total_value / new_total_quantity);
            }

            self.filled_quantity += fill_quantity;

            // Update status
            if self.filled_quantity == self.quantity {
                self.status = OrderStatus::Filled;
            } else {
                self.status = OrderStatus::PartiallyFilled;
            }

            Ok(())
        }
    }

    /// Validate order lifecycle transitions
    ///
    /// Performs comprehensive validation of order state consistency:
    /// - Filled quantity doesn't exceed order quantity
    ///
    /// - Order status matches fill state
    /// - Average fill prices are reasonable
    ///
    /// # Arguments
    /// * `orders` - Collection of orders to validate
    ///
    /// * `context` - Description for error reporting
    ///
    /// # Returns
    /// * `Ok(())` - If all orders pass validation
    ///
    /// * `Err(TestError)` - If any order has inconsistent state
    pub fn validate_order_lifecycle(orders: &[TestOrder], context: &str) -> TestResult<()> {
        for order in orders {
            // Validate filled quantity doesn't exceed order quantity
            if order.filled_quantity > order.quantity {
                return Err(TestError::assertion(format!(
                    "{}: Order {} filled quantity {} exceeds order quantity {}",
                    context, order.id, order.filled_quantity, order.quantity
                )));
            }

            // Validate status consistency
            match order.status {
                OrderStatus::Filled if order.filled_quantity != order.quantity => {
                    return Err(TestError::assertion(format!(
                        "{}: Order {} marked as filled but quantities don't match: {} != {}",
                        context, order.id, order.filled_quantity, order.quantity
                    )));
                },
                OrderStatus::PartiallyFilled if order.filled_quantity == Decimal::ZERO => {
                    return Err(TestError::assertion(format!(
                        "{}: Order {} marked as partially filled but no quantity filled",
                        context, order.id
                    )));
                },
                _ => {},
            }
        }

        Ok(())
    }
}

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

    #[tokio::test]
    async fn test_latency_measurement() -> TestResult<()> {
        let mut measurement = performance::LatencyMeasurement::new(100);

        for i in 1..=10 {
            measurement.record(Duration::from_nanos(i * 1000));
        }

        let avg = measurement.average().expect("Should have average");
        assert!(avg > Duration::from_nanos(5000));
        assert!(avg < Duration::from_nanos(6000));

        Ok(())
    }

    #[tokio::test]
    async fn test_financial_precision() -> TestResult<()> {
        let price1 = Decimal::try_from(100.12345678).unwrap_or_default();
        let price2 = Decimal::try_from(100.12345679).unwrap_or_default();

        // Should pass within precision
        financial::assert_decimal_eq(price1, price2, "price comparison")?;

        Ok(())
    }

    #[tokio::test]
    async fn test_market_data_generation() -> TestResult<()> {
        let stream = market_data::TestMarketDataStream::new(
            vec!["AAPL".to_string(), "GOOGL".to_string()],
            1000, // 1000 Hz
        );

        let ticks = stream.generate_ticks(Duration::from_millis(10)).await?;
        assert!(!ticks.is_empty());

        market_data::validate_tick_sequence(&ticks, "test market data")?;

        Ok(())
    }

    #[tokio::test]
    async fn test_order_simulation() -> TestResult<()> {
        let mut order = orders::TestOrder::new_limit_order(
            "AAPL",
            orders::OrderSide::Buy,
            Decimal::from(100),
            Decimal::try_from(150.0).unwrap_or_default(),
        );

        // Simulate partial fill
        order.simulate_fill(
            Decimal::from(50),
            Decimal::try_from(149.5).unwrap_or_default(),
        )?;

        assert_eq!(order.status, orders::OrderStatus::PartiallyFilled);
        assert_eq!(order.filled_quantity, Decimal::from(50));

        // Complete the fill
        order.simulate_fill(
            Decimal::from(50),
            Decimal::try_from(150.5).unwrap_or_default(),
        )?;

        assert_eq!(order.status, orders::OrderStatus::Filled);
        assert_eq!(order.filled_quantity, Decimal::from(100));

        Ok(())
    }
}
