// Performance benchmarks for open deviation bar processing
//
// Target: 1M ticks < 100ms, 1B ticks < 30 seconds

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use opendeviationbar::{AggTrade, FixedPoint, OpenDeviationBar, OpenDeviationBarProcessor};

fn create_test_trades(count: usize, base_price: f64, volatility: f64) -> Vec<AggTrade> {
    let mut trades = Vec::with_capacity(count);
    let mut price = base_price;
    let mut rng = 0x1234_5678_u64; // Simple deterministic RNG

    for i in 0..count {
        // Simple LCG for deterministic "random" price movements
        rng = rng.wrapping_mul(1_103_515_245).wrapping_add(12345);
        let random = (rng >> 16) as f64 / 65536.0; // [0, 1)
        let price_change = (random - 0.5) * volatility * 2.0;
        price += price_change;

        // Alternate buy/sell pressure for realistic microstructure testing
        let is_buyer_maker = (i % 2) == 0; // 50/50 split for balanced microstructure

        trades.push(AggTrade {
            agg_trade_id: i as i64,
            price: FixedPoint::from_str(&format!("{:.8}", price)).unwrap(),
            volume: FixedPoint::from_str("1.0").unwrap(),
            first_trade_id: i as i64,
            last_trade_id: i as i64,
            timestamp: 1_640_995_200_000 + (i as i64 * 100), // 100ms intervals
            is_buyer_maker,                                  // PHASE 0: Market microstructure field
            is_best_match: None,
        });
    }

    trades
}

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

    // Test different scales
    for size in &[1_000, 10_000, 100_000, 1_000_000] {
        let trades = create_test_trades(*size, 50000.0, 100.0); // BTC-like volatility

        group.bench_with_input(BenchmarkId::new("process_trades", size), size, |b, _| {
            b.iter(|| {
                let mut proc = OpenDeviationBarProcessor::new(8000).unwrap();
                let bars = proc.process_agg_trade_records(black_box(&trades)).unwrap();
                black_box(bars);
            });
        });
    }

    group.finish();
}

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

    let price = FixedPoint::from_str("50000.12345678").unwrap();

    group.bench_function("compute_thresholds", |b| {
        b.iter(|| {
            let (upper, lower) = price.compute_range_thresholds(black_box(8000));
            black_box((upper, lower));
        });
    });

    group.finish();
}

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

    let trade = AggTrade {
        agg_trade_id: 1,
        price: FixedPoint::from_str("50000.0").unwrap(),
        volume: FixedPoint::from_str("1.0").unwrap(),
        first_trade_id: 1,
        last_trade_id: 1,
        timestamp: 1_640_995_200_000,
        is_buyer_maker: false, // Buy pressure for testing
        is_best_match: None,
    };

    let bar = OpenDeviationBar::new(&trade);
    let (upper_threshold, lower_threshold) = trade.price.compute_range_thresholds(8000);
    let test_price = FixedPoint::from_str("50400.0").unwrap(); // Breach price

    group.bench_function("is_breach", |b| {
        b.iter(|| {
            let result = bar.is_breach(
                black_box(test_price),
                black_box(upper_threshold),
                black_box(lower_threshold),
            );
            black_box(result);
        });
    });

    group.finish();
}

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

    // Test memory allocation patterns for different batch sizes
    for batch_size in &[1000, 10_000, 100_000] {
        group.bench_with_input(
            BenchmarkId::new("batch_processing", batch_size),
            batch_size,
            |b, &size| {
                let trades = create_test_trades(size, 50000.0, 100.0);

                b.iter(|| {
                    let mut processor = OpenDeviationBarProcessor::new(8000).unwrap();
                    // Process in batches to simulate real-world streaming
                    let batch_size = 1000;
                    let mut total_bars = 0;

                    for chunk in trades.chunks(batch_size) {
                        let bars = processor
                            .process_agg_trade_records(black_box(chunk))
                            .unwrap();
                        total_bars += bars.len();
                    }

                    black_box(total_bars);
                });
            },
        );
    }

    group.finish();
}

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

    // High volatility scenario (many open deviation bar completions)
    let high_volatility_trades = create_test_trades(10000, 50000.0, 500.0); // Very volatile

    group.bench_function("high_volatility", |b| {
        b.iter(|| {
            let mut processor = OpenDeviationBarProcessor::new(8000).unwrap();
            let bars = processor
                .process_agg_trade_records(black_box(&high_volatility_trades))
                .unwrap();
            black_box(bars);
        });
    });

    // Low volatility scenario (few open deviation bar completions)
    let low_volatility_trades = create_test_trades(10000, 50000.0, 10.0); // Very stable

    group.bench_function("low_volatility", |b| {
        b.iter(|| {
            let mut processor = OpenDeviationBarProcessor::new(8000).unwrap();
            let bars = processor
                .process_agg_trade_records(black_box(&low_volatility_trades))
                .unwrap();
            black_box(bars);
        });
    });

    group.finish();
}

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

    // High volatility = many bar closes = R1/R2 take() exercised heavily
    let trades_10k = create_test_trades(10000, 50000.0, 500.0);
    let trades_100k = create_test_trades(100_000, 50000.0, 500.0);

    // Single-trade path (R1): process one trade at a time
    group.bench_function("single_trade_path_10k", |b| {
        b.iter(|| {
            let mut processor = OpenDeviationBarProcessor::new(250).unwrap();
            let mut bar_count = 0_usize;
            for trade in &trades_10k {
                if processor
                    .process_single_trade(black_box(trade))
                    .unwrap()
                    .is_some()
                {
                    bar_count += 1;
                }
            }
            black_box(bar_count);
        });
    });

    // Batch path (R2): process all trades in one call
    group.bench_function("batch_path_10k", |b| {
        b.iter(|| {
            let mut processor = OpenDeviationBarProcessor::new(250).unwrap();
            let bars = processor
                .process_agg_trade_records(black_box(&trades_10k))
                .unwrap();
            black_box(bars.len());
        });
    });

    // Batch path at scale (R2)
    group.bench_function("batch_path_100k", |b| {
        b.iter(|| {
            let mut processor = OpenDeviationBarProcessor::new(250).unwrap();
            let bars = processor
                .process_agg_trade_records(black_box(&trades_100k))
                .unwrap();
            black_box(bars.len());
        });
    });

    group.finish();
}

/// Dedicated Hurst DFA profiling to validate O(n²) complexity
/// This isolates the bottleneck identified in Issue #96
fn bench_hurst_rescaled_range_scaling(c: &mut Criterion) {
    use opendeviationbar_core::interbar_math::compute_hurst_rescaled_range;

    let mut group = c.benchmark_group("hurst_rescaled_range_scaling");
    group.sample_size(50); // Reduce samples due to longer compute times

    // Test different series lengths to confirm O(n²) scaling
    for series_len in &[64, 128, 256, 512, 1024] {
        // Generate synthetic price series
        let mut prices = Vec::with_capacity(*series_len);
        let mut price = 50000.0;
        for i in 0..*series_len {
            let noise = ((i * 7919) % 1000) as f64 / 1000.0 - 0.5;
            price += noise * 10.0;
            prices.push(price);
        }

        group.bench_with_input(
            BenchmarkId::new("compute_hurst_rescaled_range", series_len),
            series_len,
            |b, _| {
                b.iter(|| {
                    let h = compute_hurst_rescaled_range(black_box(&prices));
                    black_box(h);
                });
            },
        );
    }

    group.finish();
}

/// Compare our optimized Hurst DFA across different use cases
/// Issue #96: Memory Efficiency - Performance enhancement survey Phase 3
/// Benchmark: DFA performance analysis (Phase 3a Research)
/// Phase 3a: Crate Integration Research - evrom/hurst evaluation
///
/// NOTE: hurst crate API exploration needed before full integration.
/// Next steps:
/// 1. Evaluate hurst crate API (check actual exports)
/// 2. Add rescaled_range benchmark once API is confirmed
/// 3. Measure speedup: target 1.5-3x for O(n log n) vs O(n²)
/// 4. Validate oracle accuracy (cross-check 1000 bars vs ClickHouse baseline)
///
/// References: .claude/phase3-decision-matrix.md, docs/development/PERFORMANCE.md
fn bench_hurst_crate_comparison(c: &mut Criterion) {
    use opendeviationbar_core::interbar_math::compute_hurst_rescaled_range;

    let mut group = c.benchmark_group("hurst_crate_comparison");
    group.sample_size(30);

    // Test case 1: Typical trading window (trending + noise)
    let mut prices_512 = Vec::with_capacity(512);
    let mut price = 50000.0;
    for i in 0..512 {
        let trend = (i as f64 / 512.0) * 100.0;
        let noise = ((i * 7919) % 1000) as f64 / 1000.0 - 0.5;
        price += trend / 512.0 + noise * 10.0;
        prices_512.push(price);
    }

    // Test case 2: Longer series (full hour lookback)
    let mut prices_1024 = Vec::with_capacity(1024);
    price = 50000.0;
    for i in 0..1024 {
        let trend = (i as f64 / 1024.0) * 200.0;
        let noise = ((i * 7919) % 1000) as f64 / 1000.0 - 0.5;
        price += trend / 1024.0 + noise * 10.0;
        prices_1024.push(price);
    }

    // Benchmark our optimized DFA (baseline O(n²))
    group.bench_function("baseline_dfa_512", |b| {
        b.iter(|| {
            let h = compute_hurst_rescaled_range(black_box(&prices_512));
            black_box(h);
        });
    });

    group.bench_function("baseline_dfa_1024", |b| {
        b.iter(|| {
            let h = compute_hurst_rescaled_range(black_box(&prices_1024));
            black_box(h);
        });
    });

    // opendeviationbar-hurst R/S Analysis — O(n log n) vs DFA O(n²)
    // Issue #96: Phase 3a - crate comparison for Hurst bottleneck optimization
    // rssimple: uncorrected R/S (faster), rs_corrected: Anis-Okon corrected R/S
    group.bench_function("hurst_rssimple_512", |b| {
        b.iter(|| {
            let h = opendeviationbar_hurst::rssimple(black_box(&prices_512));
            black_box(h);
        });
    });

    group.bench_function("hurst_rssimple_1024", |b| {
        b.iter(|| {
            let h = opendeviationbar_hurst::rssimple(black_box(&prices_1024));
            black_box(h);
        });
    });

    group.bench_function("hurst_rs_corrected_512", |b| {
        b.iter(|| {
            let h = opendeviationbar_hurst::rs_corrected(black_box(prices_512.clone()));
            black_box(h);
        });
    });

    group.bench_function("hurst_rs_corrected_1024", |b| {
        b.iter(|| {
            let h = opendeviationbar_hurst::rs_corrected(black_box(prices_1024.clone()));
            black_box(h);
        });
    });

    group.finish();
}

/// Garman-Klass volatility optimization benchmark
/// Phase 4a: Coefficient pre-computation optimization
/// Issue #96: Performance optimization for inter-bar features
fn bench_garman_klass(c: &mut Criterion) {
    use opendeviationbar_core::interbar_math::compute_garman_klass;
    use opendeviationbar_core::interbar_types::TradeSnapshot;

    let mut group = c.benchmark_group("garman_klass_optimization");
    group.sample_size(100);

    // Generate synthetic trades for different lookback sizes
    for lookback_size in &[10, 50, 100, 256] {
        let mut trades = Vec::with_capacity(*lookback_size);
        let mut price = 50000.0;

        for i in 0..*lookback_size {
            let noise = ((i * 7919) % 1000) as f64 / 1000.0 - 0.5;
            price += noise * 10.0;

            trades.push(AggTrade {
                agg_trade_id: i as i64,
                price: FixedPoint::from_str(&format!("{:.8}", price)).unwrap(),
                volume: FixedPoint::from_str("1.0").unwrap(),
                first_trade_id: i as i64,
                last_trade_id: i as i64,
                timestamp: 1_640_995_200_000 + (i as i64 * 100),
                is_buyer_maker: (i % 2) == 0,
                is_best_match: None,
            });
        }

        group.bench_with_input(
            BenchmarkId::new("compute_garman_klass", lookback_size),
            lookback_size,
            |b, _| {
                let snapshots: Vec<TradeSnapshot> =
                    trades.iter().map(std::convert::Into::into).collect();
                let snapshot_refs: Vec<&TradeSnapshot> = snapshots.iter().collect();
                b.iter(|| {
                    let vol = compute_garman_klass(black_box(&snapshot_refs));
                    black_box(vol);
                });
            },
        );
    }

    group.finish();
}

criterion_group!(
    benches,
    bench_open_deviation_bar_processing,
    bench_threshold_calculation,
    bench_breach_detection,
    bench_memory_efficiency,
    bench_extreme_cases,
    bench_bar_close_take,
    bench_hurst_rescaled_range_scaling,
    bench_hurst_crate_comparison,
    bench_garman_klass,
    bench_permutation_entropy
);
criterion_main!(benches);

/// Permutation entropy optimization benchmark
/// Phase 4b: Identify optimization opportunities for pattern counting
/// Issue #96: Inter-bar feature performance optimization
fn bench_permutation_entropy(c: &mut Criterion) {
    use opendeviationbar_core::interbar_math::compute_permutation_entropy;

    let mut group = c.benchmark_group("permutation_entropy_optimization");
    group.sample_size(50);

    // Test different series lengths
    for series_len in &[64, 128, 256, 512] {
        let mut prices = Vec::with_capacity(*series_len);
        let mut price = 50000.0;

        for i in 0..*series_len {
            let noise = ((i * 7919) % 1000) as f64 / 1000.0 - 0.5;
            price += noise * 10.0;
            prices.push(price);
        }

        group.bench_with_input(
            BenchmarkId::new("compute_permutation_entropy", series_len),
            series_len,
            |b, _| {
                b.iter(|| {
                    let entropy = compute_permutation_entropy(black_box(&prices));
                    black_box(entropy);
                });
            },
        );
    }

    group.finish();
}
