//! Performance benchmarks for market data replay engine
//!
//! Measures throughput and latency characteristics of the backtesting system
//! under various data loads and configurations.

// Explicit alias to avoid core crate shadowing std::core for async_trait
extern crate std as stdlib;

use async_trait::async_trait;
use chrono::{TimeDelta, Utc};
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use std::io::Write;
use tempfile::NamedTempFile;
use tokio::runtime::Runtime;

use rust_decimal::Decimal;
use rust_decimal_macros::dec;

use backtesting::{
    replay_engine::{DataFormat, DataSource, MarketReplay, ReplayConfig, SourceType},
    BacktestConfig, BacktestEngine,
};
use common::{Order, Position};
use trading_engine::types::events::MarketEvent;

/// Benchmark market data replay throughput
fn bench_replay_throughput(c: &mut Criterion) {
    let rt = Runtime::new().unwrap();

    let mut group = c.benchmark_group("replay_throughput");

    for event_count in &[1_000, 10_000, 100_000] {
        group.throughput(Throughput::Elements(*event_count));
        group.bench_with_input(
            BenchmarkId::new("events", event_count),
            event_count,
            |b, &event_count| {
                b.iter(|| {
                    rt.block_on(async {
                        let data_file = create_benchmark_data(event_count as usize).await.unwrap();

                        let config = ReplayConfig {
                            data_sources: vec![DataSource {
                                source_type: SourceType::CsvFile,
                                path: data_file.clone(),
                                format: DataFormat::OhlcvTicks,
                                priority: 1,
                            }],
                            speed_multiplier: 0.0, // Maximum speed
                            tick_by_tick: true,
                            buffer_size: 50000,
                            ..Default::default()
                        };

                        let replay = MarketReplay::new(config);
                        let mut receiver = replay.take_receiver().await.unwrap();

                        // Start replay
                        let replay_handle =
                            tokio::spawn(async move { replay.start_replay().await });

                        // Count events
                        let mut count = 0;
                        let start = std::time::Instant::now();

                        while let Some(_event) = receiver.recv().await {
                            count += 1;
                            black_box(count);
                        }

                        replay_handle.await.unwrap().unwrap();

                        // Clean up
                        std::fs::remove_file(&data_file).ok();

                        let duration = start.elapsed();
                        black_box((count, duration));
                    })
                });
            },
        );
    }

    group.finish();
}

/// Benchmark event processing latency
fn bench_event_latency(c: &mut Criterion) {
    let rt = Runtime::new().unwrap();

    c.bench_function("event_latency", |b| {
        b.iter(|| {
            rt.block_on(async {
                let data_file = create_benchmark_data(1000).await.unwrap();

                let config = ReplayConfig {
                    data_sources: vec![DataSource {
                        source_type: SourceType::CsvFile,
                        path: data_file.clone(),
                        format: DataFormat::OhlcvTicks,
                        priority: 1,
                    }],
                    speed_multiplier: 0.0,
                    tick_by_tick: true,
                    buffer_size: 10000,
                    ..Default::default()
                };

                let replay = MarketReplay::new(config);
                let mut receiver = replay.take_receiver().await.unwrap();

                let replay_handle = tokio::spawn(async move { replay.start_replay().await });

                // Measure latency of first 100 events
                let mut latencies = Vec::new();
                for _ in 0..100 {
                    let start = std::time::Instant::now();
                    if let Some(_event) = receiver.recv().await {
                        let latency = start.elapsed();
                        latencies.push(latency);
                    }
                }

                // Drain remaining events
                while receiver.recv().await.is_some() {}

                replay_handle.await.unwrap().unwrap();
                std::fs::remove_file(&data_file).ok();

                black_box(latencies);
            })
        });
    });
}

/// Benchmark memory usage under load
fn bench_memory_usage(c: &mut Criterion) {
    let rt = Runtime::new().unwrap();

    let mut group = c.benchmark_group("memory_usage");

    for buffer_size in &[1_000, 10_000, 50_000] {
        group.bench_with_input(
            BenchmarkId::new("buffer_size", buffer_size),
            buffer_size,
            |b, &buffer_size| {
                b.iter(|| {
                    rt.block_on(async {
                        let data_file = create_benchmark_data(50000).await.unwrap();

                        let config = ReplayConfig {
                            data_sources: vec![DataSource {
                                source_type: SourceType::CsvFile,
                                path: data_file.clone(),
                                format: DataFormat::OhlcvTicks,
                                priority: 1,
                            }],
                            speed_multiplier: 0.0,
                            tick_by_tick: true,
                            buffer_size,
                            ..Default::default()
                        };

                        let replay = MarketReplay::new(config);
                        let mut receiver = replay.take_receiver().await.unwrap();

                        let replay_handle =
                            tokio::spawn(async move { replay.start_replay().await });

                        // Process all events
                        let mut count = 0;
                        while let Some(_event) = receiver.recv().await {
                            count += 1;

                            // Simulate some processing work
                            if count % 1000 == 0 {
                                tokio::task::yield_now().await;
                            }
                        }

                        replay_handle.await.unwrap().unwrap();
                        std::fs::remove_file(&data_file).ok();

                        black_box(count);
                    })
                });
            },
        );
    }

    group.finish();
}

/// Benchmark complete backtesting engine
fn bench_full_backtest(c: &mut Criterion) {
    let rt = Runtime::new().unwrap();

    c.bench_function("full_backtest", |b| {
        b.iter(|| {
            rt.block_on(async {
                let data_file = create_benchmark_data(10000).await.unwrap();

                let config = BacktestConfig {
                    initial_capital: dec!(100000),
                    replay_config: ReplayConfig {
                        data_sources: vec![DataSource {
                            source_type: SourceType::CsvFile,
                            path: data_file.clone(),
                            format: DataFormat::OhlcvTicks,
                            priority: 1,
                        }],
                        speed_multiplier: 0.0,
                        tick_by_tick: true,
                        buffer_size: 20000,
                        ..Default::default()
                    },
                    enable_logging: false, // Disable logging for benchmarks
                    snapshot_interval: 3600,
                    max_memory_usage: 256 * 1024 * 1024,
                    ..Default::default()
                };

                let mut engine = BacktestEngine::new(config).await.unwrap();

                // Use a simple buy-and-hold strategy for benchmarking
                let strategy = Box::new(BenchmarkStrategy::new());
                engine.set_strategy(strategy).await.unwrap();

                let result = engine.run().await.unwrap();

                std::fs::remove_file(&data_file).ok();

                black_box(result);
            })
        });
    });
}

/// Benchmark strategy execution overhead
fn bench_strategy_execution(c: &mut Criterion) {
    let rt = Runtime::new().unwrap();

    let mut group = c.benchmark_group("strategy_execution");

    for complexity in &["simple", "medium", "complex"] {
        group.bench_with_input(
            BenchmarkId::new("strategy", complexity),
            complexity,
            |b, &complexity| {
                b.iter(|| {
                    rt.block_on(async {
                        let data_file = create_benchmark_data(5000).await.unwrap();

                        let config = BacktestConfig {
                            initial_capital: dec!(100000),
                            replay_config: ReplayConfig {
                                data_sources: vec![DataSource {
                                    source_type: SourceType::CsvFile,
                                    path: data_file.clone(),
                                    format: DataFormat::OhlcvTicks,
                                    priority: 1,
                                }],
                                speed_multiplier: 0.0,
                                tick_by_tick: true,
                                buffer_size: 10000,
                                ..Default::default()
                            },
                            enable_logging: false,
                            snapshot_interval: 3600,
                            max_memory_usage: 128 * 1024 * 1024,
                            ..Default::default()
                        };

                        let mut engine = BacktestEngine::new(config).await.unwrap();

                        let strategy: Box<dyn backtesting::Strategy> = match complexity {
                            "simple" => Box::new(SimpleStrategy::new()),
                            "medium" => Box::new(MediumStrategy::new()),
                            "complex" => Box::new(ComplexStrategy::new()),
                            _ => Box::new(BenchmarkStrategy::new()),
                        };

                        engine.set_strategy(strategy).await.unwrap();
                        let result = engine.run().await.unwrap();

                        std::fs::remove_file(&data_file).ok();

                        black_box(result);
                    })
                });
            },
        );
    }

    group.finish();
}

/// Create benchmark data file
async fn create_benchmark_data(event_count: usize) -> Result<String, Box<dyn std::error::Error>> {
    let mut temp_file = NamedTempFile::new()?;

    writeln!(temp_file, "timestamp,symbol,open,high,low,close,volume")?;

    let base_time = Utc::now() - TimeDelta::days(1);
    let mut price = dec!(50000.0);

    for i in 0..event_count {
        let timestamp = base_time + TimeDelta::seconds(i as i64);

        // Simple price movement
        price += Decimal::from_f64_retain((i as f64 * 0.01).sin() * 10.0).unwrap_or_default();

        let open = price;
        let high = price + dec!(50);
        let low = price - dec!(50);
        let close =
            price + Decimal::from_f64_retain((i as f64 * 0.1).cos() * 25.0).unwrap_or_default();
        let volume = dec!(1000);

        writeln!(
            temp_file,
            "{},BTCUSD,{},{},{},{},{}",
            timestamp.timestamp_millis(),
            open,
            high,
            low,
            close,
            volume
        )?;

        price = close;
    }

    let path = temp_file.path().to_string_lossy().to_string();
    temp_file.keep()?;

    Ok(path)
}

// Benchmark strategies with different complexity levels

/// Simple strategy for benchmarking
struct BenchmarkStrategy;

impl BenchmarkStrategy {
    fn new() -> Self {
        Self
    }
}

#[async_trait(?Send)]
impl backtesting::Strategy for BenchmarkStrategy {
    fn name(&self) -> &str {
        "benchmark_strategy"
    }

    async fn initialize(
        &mut self,
        _initial_capital: Decimal,
        _config: backtesting::StrategyConfig,
    ) -> anyhow::Result<()> {
        Ok(())
    }

    async fn on_market_event(
        &mut self,
        _event: &MarketEvent,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<Vec<backtesting::TradingSignal>> {
        Ok(vec![])
    }

    async fn on_order_update(
        &mut self,
        _order: &Order,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<()> {
        Ok(())
    }

    async fn on_position_update(
        &mut self,
        _position: &Position,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<()> {
        Ok(())
    }

    async fn finalize(
        &mut self,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<backtesting::StrategyResult> {
        Ok(backtesting::StrategyResult {
            strategy_name: "benchmark_strategy".to_string(),
            total_return: dec!(0.05),
            annualized_return: dec!(0.05),
            max_drawdown: dec!(0.02),
            sharpe_ratio: dec!(1.0),
            total_trades: 10,
            win_rate: dec!(0.6),
            avg_trade_return: dec!(0.005),
            final_value: dec!(105000),
            trades: vec![],
            performance_timeline: vec![],
        })
    }

    async fn get_state(&self) -> anyhow::Result<serde_json::Value> {
        Ok(serde_json::json!({"name": "benchmark_strategy"}))
    }
}

/// Simple strategy with minimal computation
struct SimpleStrategy;

impl SimpleStrategy {
    fn new() -> Self {
        Self
    }
}

#[async_trait(?Send)]
impl backtesting::Strategy for SimpleStrategy {
    fn name(&self) -> &str {
        "simple_strategy"
    }

    async fn initialize(
        &mut self,
        _initial_capital: Decimal,
        _config: backtesting::StrategyConfig,
    ) -> anyhow::Result<()> {
        Ok(())
    }

    async fn on_market_event(
        &mut self,
        event: &MarketEvent,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<Vec<backtesting::TradingSignal>> {
        // Simple logic: check if price changed
        if let MarketEvent::Trade { price, .. } = event {
            if price.to_decimal().unwrap_or_default() > dec!(50000) {
                // Some minimal computation
                let _ = price.to_decimal().unwrap_or_default() * dec!(1.01);
            }
        }
        Ok(vec![])
    }

    async fn on_order_update(
        &mut self,
        _order: &Order,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<()> {
        Ok(())
    }

    async fn on_position_update(
        &mut self,
        _position: &Position,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<()> {
        Ok(())
    }

    async fn finalize(
        &mut self,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<backtesting::StrategyResult> {
        Ok(backtesting::StrategyResult {
            strategy_name: "simple_strategy".to_string(),
            total_return: dec!(0.03),
            annualized_return: dec!(0.03),
            max_drawdown: dec!(0.01),
            sharpe_ratio: dec!(0.8),
            total_trades: 5,
            win_rate: dec!(0.6),
            avg_trade_return: dec!(0.006),
            final_value: dec!(103000),
            trades: vec![],
            performance_timeline: vec![],
        })
    }

    async fn get_state(&self) -> anyhow::Result<serde_json::Value> {
        Ok(serde_json::json!({"name": "simple_strategy"}))
    }
}

/// Medium complexity strategy
struct MediumStrategy {
    price_history: std::collections::VecDeque<Decimal>,
}

impl MediumStrategy {
    fn new() -> Self {
        Self {
            price_history: std::collections::VecDeque::new(),
        }
    }
}

#[async_trait(?Send)]
impl backtesting::Strategy for MediumStrategy {
    fn name(&self) -> &str {
        "medium_strategy"
    }

    async fn initialize(
        &mut self,
        _initial_capital: Decimal,
        _config: backtesting::StrategyConfig,
    ) -> anyhow::Result<()> {
        self.price_history.clear();
        Ok(())
    }

    async fn on_market_event(
        &mut self,
        event: &MarketEvent,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<Vec<backtesting::TradingSignal>> {
        if let MarketEvent::Trade { price, .. } = event {
            self.price_history
                .push_back(price.to_decimal().unwrap_or_default());
            if self.price_history.len() > 20 {
                self.price_history.pop_front();
            }

            // Calculate simple moving average
            if self.price_history.len() >= 10 {
                let sum: Decimal = self.price_history.iter().rev().take(10).sum();
                let _avg = sum / dec!(10);
                // Some medium computation
            }
        }
        Ok(vec![])
    }

    async fn on_order_update(
        &mut self,
        _order: &Order,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<()> {
        Ok(())
    }

    async fn on_position_update(
        &mut self,
        _position: &Position,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<()> {
        Ok(())
    }

    async fn finalize(
        &mut self,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<backtesting::StrategyResult> {
        Ok(backtesting::StrategyResult {
            strategy_name: "medium_strategy".to_string(),
            total_return: dec!(0.07),
            annualized_return: dec!(0.07),
            max_drawdown: dec!(0.03),
            sharpe_ratio: dec!(1.2),
            total_trades: 15,
            win_rate: dec!(0.65),
            avg_trade_return: dec!(0.0047),
            final_value: dec!(107000),
            trades: vec![],
            performance_timeline: vec![],
        })
    }

    async fn get_state(&self) -> anyhow::Result<serde_json::Value> {
        Ok(serde_json::json!({
            "name": "medium_strategy",
            "price_history_length": self.price_history.len()
        }))
    }
}

/// Complex strategy with heavy computation
struct ComplexStrategy {
    price_history: std::collections::VecDeque<Decimal>,
    indicators: std::collections::HashMap<String, Decimal>,
}

impl ComplexStrategy {
    fn new() -> Self {
        Self {
            price_history: std::collections::VecDeque::new(),
            indicators: std::collections::HashMap::new(),
        }
    }
}

#[async_trait(?Send)]
impl backtesting::Strategy for ComplexStrategy {
    fn name(&self) -> &str {
        "complex_strategy"
    }

    async fn initialize(
        &mut self,
        _initial_capital: Decimal,
        _config: backtesting::StrategyConfig,
    ) -> anyhow::Result<()> {
        self.price_history.clear();
        self.indicators.clear();
        Ok(())
    }

    async fn on_market_event(
        &mut self,
        event: &MarketEvent,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<Vec<backtesting::TradingSignal>> {
        if let MarketEvent::Trade { price, .. } = event {
            self.price_history
                .push_back(price.to_decimal().unwrap_or_default());
            if self.price_history.len() > 100 {
                self.price_history.pop_front();
            }

            // Calculate multiple indicators (complex computation)
            if self.price_history.len() >= 20 {
                // SMA 20
                let sma20: Decimal =
                    self.price_history.iter().rev().take(20).sum::<Decimal>() / dec!(20);
                self.indicators.insert("sma20".to_string(), sma20);

                // SMA 50
                if self.price_history.len() >= 50 {
                    let sma50: Decimal =
                        self.price_history.iter().rev().take(50).sum::<Decimal>() / dec!(50);
                    self.indicators.insert("sma50".to_string(), sma50);
                }

                // Standard deviation calculation
                let prices: Vec<Decimal> =
                    self.price_history.iter().rev().take(20).cloned().collect();
                let mean = sma20;
                let variance: Decimal = prices
                    .iter()
                    .map(|p| (*p - mean) * (*p - mean))
                    .sum::<Decimal>()
                    / dec!(20);

                self.indicators.insert("std_dev".to_string(), variance);
            }
        }
        Ok(vec![])
    }

    async fn on_order_update(
        &mut self,
        _order: &Order,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<()> {
        Ok(())
    }

    async fn on_position_update(
        &mut self,
        _position: &Position,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<()> {
        Ok(())
    }

    async fn finalize(
        &mut self,
        _context: &backtesting::StrategyContext,
    ) -> anyhow::Result<backtesting::StrategyResult> {
        Ok(backtesting::StrategyResult {
            strategy_name: "complex_strategy".to_string(),
            total_return: dec!(0.10),
            annualized_return: dec!(0.10),
            max_drawdown: dec!(0.04),
            sharpe_ratio: dec!(1.5),
            total_trades: 25,
            win_rate: dec!(0.70),
            avg_trade_return: dec!(0.004),
            final_value: dec!(110000),
            trades: vec![],
            performance_timeline: vec![],
        })
    }

    async fn get_state(&self) -> anyhow::Result<serde_json::Value> {
        Ok(serde_json::json!({
            "name": "complex_strategy",
            "price_history_length": self.price_history.len(),
            "indicators": self.indicators
        }))
    }
}

criterion_group!(
    benches,
    bench_replay_throughput,
    bench_event_latency,
    bench_memory_usage,
    bench_full_backtest,
    bench_strategy_execution
);

criterion_main!(benches);
