//! Market Data Event Processing Latency Benchmarks
//!
//! Measures ultra-low latency market data processing:
//! - Event parsing: <1μs target
//! - Event processing: <10μs target
//! - Order book update: <5μs target
//! - Feature extraction: <20μs target
//! - Full pipeline: <50μs target
//!
//! Critical for HFT signal generation and strategy execution

#![allow(
    clippy::assertions_on_constants,
    clippy::assertions_on_result_states,
    clippy::clone_on_copy,
    clippy::doc_markdown,
    clippy::else_if_without_else,
    clippy::expect_used,
    clippy::indexing_slicing,
    clippy::integer_division,
    clippy::missing_const_for_fn,
    clippy::modulo_arithmetic,
    clippy::non_ascii_literal,
    clippy::str_to_string,
    clippy::unnecessary_cast,
    clippy::unseparated_literal_suffix,
    clippy::unwrap_used,
    dead_code,
    unused_imports,
    unused_variables
)]

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use hdrhistogram::Histogram;
use rust_decimal::Decimal;
use std::collections::VecDeque;
use std::time::{Duration, Instant};

/// Latency metrics
struct LatencyMetrics {
    histogram: Histogram<u64>,
    samples: Vec<u64>,
}

impl LatencyMetrics {
    fn new() -> Self {
        Self {
            histogram: Histogram::<u64>::new(5).unwrap(),
            samples: Vec::new(),
        }
    }

    fn record_nanos(&mut self, nanos: u64) {
        self.histogram.record(nanos / 1000).ok();
        self.samples.push(nanos);
    }

    fn report(&self, label: &str, target_us: f64) {
        let p50 = self.histogram.value_at_percentile(50.0) as f64;
        let p95 = self.histogram.value_at_percentile(95.0) as f64;
        let p99 = self.histogram.value_at_percentile(99.0) as f64;

        println!("\n=== {} ===", label);
        println!("P50:  {:.3}μs", p50);
        println!("P95:  {:.3}μs", p95);
        println!("P99:  {:.3}μs", p99);

        if p99 < target_us {
            println!("✅ P99 {:.3}μs < {:.1}μs", p99, target_us);
        } else {
            println!("❌ P99 {:.3}μs >= {:.1}μs", p99, target_us);
        }
    }
}

/// Market data event types
#[derive(Clone)]
enum MarketEvent {
    Trade {
        symbol: String,
        price: Decimal,
        quantity: Decimal,
        timestamp_ns: u64,
    },
    Quote {
        symbol: String,
        bid: Decimal,
        ask: Decimal,
        bid_size: Decimal,
        ask_size: Decimal,
        timestamp_ns: u64,
    },
    Level2 {
        symbol: String,
        side: Side,
        price: Decimal,
        quantity: Decimal,
        timestamp_ns: u64,
    },
}

#[derive(Clone)]
enum Side {
    Bid,
    Ask,
}

/// Order book level
#[derive(Clone)]
struct Level {
    price: Decimal,
    quantity: Decimal,
}

/// High-performance order book
struct OrderBook {
    bids: VecDeque<Level>,
    asks: VecDeque<Level>,
    last_update_ns: u64,
}

impl OrderBook {
    fn new() -> Self {
        let mut bids = VecDeque::new();
        let mut asks = VecDeque::new();

        // Pre-populate with 10 levels
        for i in 0..10 {
            bids.push_back(Level {
                price: Decimal::new(65000 - i * 10, 0),
                quantity: Decimal::new(1, 1),
            });
            asks.push_back(Level {
                price: Decimal::new(65100 + i * 10, 0),
                quantity: Decimal::new(1, 1),
            });
        }

        Self {
            bids,
            asks,
            last_update_ns: 0,
        }
    }

    fn update_level(&mut self, side: Side, price: Decimal, quantity: Decimal, timestamp_ns: u64) {
        let levels = match side {
            Side::Bid => &mut self.bids,
            Side::Ask => &mut self.asks,
        };

        // Find and update level
        if let Some(level) = levels.iter_mut().find(|l| l.price == price) {
            if quantity == Decimal::ZERO {
                // Remove level
                levels.retain(|l| l.price != price);
            } else {
                level.quantity = quantity;
            }
        } else if quantity > Decimal::ZERO {
            // Add new level
            levels.push_back(Level { price, quantity });
        }

        self.last_update_ns = timestamp_ns;
    }

    fn get_mid_price(&self) -> Option<Decimal> {
        let bid = self.bids.front()?.price;
        let ask = self.asks.front()?.price;
        Some((bid + ask) / Decimal::TWO)
    }

    fn get_spread(&self) -> Option<Decimal> {
        let bid = self.bids.front()?.price;
        let ask = self.asks.front()?.price;
        Some(ask - bid)
    }
}

/// Feature extractor for ML
struct FeatureExtractor {
    price_history: VecDeque<Decimal>,
    volume_history: VecDeque<Decimal>,
}

impl FeatureExtractor {
    fn new() -> Self {
        Self {
            price_history: VecDeque::with_capacity(100),
            volume_history: VecDeque::with_capacity(100),
        }
    }

    fn extract_features(&mut self, price: Decimal, volume: Decimal) -> Vec<f64> {
        // Update history
        self.price_history.push_back(price);
        self.volume_history.push_back(volume);

        if self.price_history.len() > 100 {
            self.price_history.pop_front();
            self.volume_history.pop_front();
        }

        // Calculate features
        let mut features = Vec::with_capacity(5);

        // Price momentum
        if self.price_history.len() >= 2 {
            let current = self.price_history.back().unwrap();
            let prev = self.price_history.front().unwrap();
            features.push(
                ((current - prev) / prev)
                    .to_string()
                    .parse::<f64>()
                    .unwrap_or(0.0),
            );
        }

        // Volume ratio
        if self.volume_history.len() >= 10 {
            let recent_vol: Decimal = self.volume_history.iter().rev().take(10).sum();
            let avg_vol: Decimal = self.volume_history.iter().sum::<Decimal>()
                / Decimal::new(self.volume_history.len() as i64, 0);
            features.push(
                (recent_vol / avg_vol / Decimal::new(10, 0))
                    .to_string()
                    .parse::<f64>()
                    .unwrap_or(0.0),
            );
        }

        // Volatility (simplified)
        if self.price_history.len() >= 20 {
            let prices: Vec<_> = self.price_history.iter().collect();
            let mean =
                prices.iter().map(|&&p| p).sum::<Decimal>() / Decimal::new(prices.len() as i64, 0);
            let variance: Decimal = prices
                .iter()
                .map(|&&p| (p - mean) * (p - mean))
                .sum::<Decimal>()
                / Decimal::new(prices.len() as i64, 0);
            // rust_decimal doesn't have sqrt, convert to f64 first
            let variance_f64 = {
                use num_traits::ToPrimitive;
                variance.to_f64().unwrap_or(0.0)
            };
            let volatility = variance_f64.sqrt();
            features.push(volatility);
        }

        features
    }
}

//
// ==================== BENCHMARK 1: Event Parsing (<1μs) ====================
//

fn bench_event_parsing(c: &mut Criterion) {
    c.bench_function("market_event_parsing", |b| {
        b.iter_custom(|iters| {
            let mut metrics = LatencyMetrics::new();

            for i in 0..iters {
                let raw_data = (
                    "BTC-USD".to_string(),
                    65000 + (i % 100) as i64,
                    100 + (i % 10) as i64,
                    1234567890000u64 + i,
                );

                let start = Instant::now();

                let event = MarketEvent::Trade {
                    symbol: raw_data.0,
                    price: Decimal::new(raw_data.1, 0),
                    quantity: Decimal::new(raw_data.2, 2),
                    timestamp_ns: raw_data.3,
                };

                black_box(event);
                metrics.record_nanos(start.elapsed().as_nanos() as u64);
            }

            metrics.report("Market Event Parsing", 1.0);
            Duration::from_nanos((metrics.samples.iter().sum::<u64>() / iters.max(1)) as u64)
        });
    });
}

//
// ==================== BENCHMARK 2: Order Book Update (<5μs) ====================
//

fn bench_orderbook_update(c: &mut Criterion) {
    c.bench_function("orderbook_level_update", |b| {
        b.iter_custom(|iters| {
            let mut metrics = LatencyMetrics::new();
            let mut ob = OrderBook::new();

            for i in 0..iters {
                let price = Decimal::new(65000 + (i % 100) as i64, 0);
                let quantity = Decimal::new(1 + (i % 10) as i64, 1);
                let side = if i % 2 == 0 { Side::Bid } else { Side::Ask };

                let start = Instant::now();
                ob.update_level(side, price, quantity, i);
                metrics.record_nanos(start.elapsed().as_nanos() as u64);
            }

            metrics.report("Order Book Level Update", 5.0);
            Duration::from_nanos((metrics.samples.iter().sum::<u64>() / iters.max(1)) as u64)
        });
    });
}

//
// ==================== BENCHMARK 3: Mid-Price Calculation (<1μs) ====================
//

fn bench_mid_price_calc(c: &mut Criterion) {
    let ob = OrderBook::new();

    c.bench_function("mid_price_calculation", |b| {
        b.iter_custom(|iters| {
            let mut metrics = LatencyMetrics::new();

            for _ in 0..iters {
                let start = Instant::now();
                black_box(ob.get_mid_price());
                metrics.record_nanos(start.elapsed().as_nanos() as u64);
            }

            metrics.report("Mid-Price Calculation", 1.0);
            Duration::from_nanos((metrics.samples.iter().sum::<u64>() / iters.max(1)) as u64)
        });
    });
}

//
// ==================== BENCHMARK 4: Feature Extraction (<20μs) ====================
//

fn bench_feature_extraction(c: &mut Criterion) {
    c.bench_function("feature_extraction", |b| {
        b.iter_custom(|iters| {
            let mut metrics = LatencyMetrics::new();
            let mut extractor = FeatureExtractor::new();

            for i in 0..iters {
                let price = Decimal::new(65000 + (i % 100) as i64, 0);
                let volume = Decimal::new(100 + (i % 50) as i64, 2);

                let start = Instant::now();
                black_box(extractor.extract_features(price, volume));
                metrics.record_nanos(start.elapsed().as_nanos() as u64);
            }

            metrics.report("Feature Extraction", 20.0);
            Duration::from_nanos((metrics.samples.iter().sum::<u64>() / iters.max(1)) as u64)
        });
    });
}

//
// ==================== BENCHMARK 5: Full Pipeline (<50μs) ====================
//

fn bench_full_pipeline(c: &mut Criterion) {
    c.bench_function("full_market_data_pipeline", |b| {
        b.iter_custom(|iters| {
            let mut metrics = LatencyMetrics::new();
            let mut ob = OrderBook::new();
            let mut extractor = FeatureExtractor::new();

            for i in 0..iters {
                let start = Instant::now();

                // Step 1: Parse event
                let price = Decimal::new(65000 + (i % 100) as i64, 0);
                let quantity = Decimal::new(1 + (i % 10) as i64, 1);

                // Step 2: Update order book
                ob.update_level(Side::Bid, price, quantity, i);

                // Step 3: Calculate mid-price
                let mid_price = ob.get_mid_price().unwrap_or(Decimal::ZERO);

                // Step 4: Extract features
                let features = extractor.extract_features(mid_price, quantity);

                black_box(features);
                metrics.record_nanos(start.elapsed().as_nanos() as u64);
            }

            metrics.report("Full Market Data Pipeline", 50.0);
            Duration::from_nanos((metrics.samples.iter().sum::<u64>() / iters.max(1)) as u64)
        });
    });
}

//
// ==================== BENCHMARK 6: Throughput Test ====================
//

fn bench_event_throughput(c: &mut Criterion) {
    let mut group = c.benchmark_group("market_data_throughput");

    for events_per_sec in &[1000, 10000, 100000] {
        group.throughput(Throughput::Elements(*events_per_sec as u64));
        group.bench_with_input(
            BenchmarkId::from_parameter(events_per_sec),
            events_per_sec,
            |b, &n| {
                b.iter_custom(|_iters| {
                    let mut ob = OrderBook::new();
                    let start = Instant::now();

                    for i in 0..n {
                        let price = Decimal::new(65000 + (i % 100) as i64, 0);
                        let quantity = Decimal::new(1, 1);
                        ob.update_level(Side::Bid, price, quantity, i as u64);
                    }

                    start.elapsed()
                });
            },
        );
    }

    group.finish();
}

criterion_group!(
    market_data_benches,
    bench_event_parsing,
    bench_orderbook_update,
    bench_mid_price_calc,
    bench_feature_extraction,
    bench_full_pipeline,
    bench_event_throughput,
);

criterion_main!(market_data_benches);
