//! Metrics Collection Overhead Benchmarks
//!
//! Validates metrics performance targets:
//! - Observation overhead: <5us per metric
//! - Registry size impact: O(1) lookup
//! - Cardinality performance: >1000 unique labels
//! - Aggregation overhead: <100us per aggregation
//!
//! Critical for ensuring observability doesn't impact trading latency.

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::Duration;

/// Mock metrics registry
struct MetricsRegistry {
    counters: Arc<Mutex<HashMap<String, f64>>>,
    gauges: Arc<Mutex<HashMap<String, f64>>>,
    histograms: Arc<Mutex<HashMap<String, Vec<f64>>>>,
}

impl MetricsRegistry {
    fn new() -> Self {
        Self {
            counters: Arc::new(Mutex::new(HashMap::new())),
            gauges: Arc::new(Mutex::new(HashMap::new())),
            histograms: Arc::new(Mutex::new(HashMap::new())),
        }
    }

    fn observe_counter(&self, name: String, value: f64) {
        let mut counters = self.counters.lock().unwrap();
        *counters.entry(name).or_insert(0.0) += value;
    }

    fn observe_gauge(&self, name: String, value: f64) {
        let mut gauges = self.gauges.lock().unwrap();
        gauges.insert(name, value);
    }

    fn observe_histogram(&self, name: String, value: f64) {
        let mut histograms = self.histograms.lock().unwrap();
        histograms.entry(name).or_default().push(value);
    }
}

/// Benchmark metric observation overhead
fn bench_observation_overhead(c: &mut Criterion) {
    let mut group = c.benchmark_group("observation_overhead");
    group.throughput(Throughput::Elements(1));

    let registry = MetricsRegistry::new();

    group.bench_function("counter_increment", |b| {
        b.iter(|| {
            registry.observe_counter("requests_total".to_string(), 1.0);
            black_box(&registry)
        });
    });

    group.bench_function("gauge_set", |b| {
        b.iter(|| {
            registry.observe_gauge("queue_size".to_string(), 42.0);
            black_box(&registry)
        });
    });

    group.bench_function("histogram_observe", |b| {
        b.iter(|| {
            registry.observe_histogram("request_duration_ms".to_string(), 15.5);
            black_box(&registry)
        });
    });

    group.finish();
}

/// Benchmark registry lookup performance
fn bench_registry_lookup(c: &mut Criterion) {
    let mut group = c.benchmark_group("registry_lookup");

    for num_metrics in &[10, 100, 1000, 10000] {
        group.bench_with_input(
            BenchmarkId::new("metrics", num_metrics),
            num_metrics,
            |b, &count| {
                let registry = MetricsRegistry::new();

                // Pre-populate registry
                for i in 0..count {
                    registry.observe_counter(format!("metric_{}", i), 1.0);
                }

                b.iter(|| {
                    // Lookup random metric
                    let metric_name = format!("metric_{}", count / 2);
                    registry.observe_counter(metric_name, 1.0);
                    black_box(&registry)
                });
            },
        );
    }

    group.finish();
}

/// Benchmark label cardinality impact
fn bench_label_cardinality(c: &mut Criterion) {
    let mut group = c.benchmark_group("label_cardinality");

    for num_labels in &[1, 5, 10, 20] {
        group.bench_with_input(
            BenchmarkId::new("labels", num_labels),
            num_labels,
            |b, &labels| {
                b.iter(|| {
                    let mut label_map = HashMap::new();
                    for i in 0..labels {
                        label_map.insert(format!("label_{}", i), format!("value_{}", i));
                    }

                    black_box(("request_latency", label_map, 42.0_f64, 0_u64))
                });
            },
        );
    }

    group.finish();
}

/// Benchmark metric aggregation
fn bench_aggregation(c: &mut Criterion) {
    let mut group = c.benchmark_group("metric_aggregation");

    for sample_count in &[100, 1000, 10000] {
        group.throughput(Throughput::Elements(*sample_count as u64));
        group.bench_with_input(
            BenchmarkId::new("samples", sample_count),
            sample_count,
            |b, &count| {
                b.iter_batched(
                    || {
                        // Generate sample data
                        (0..count).map(|i| i as f64).collect::<Vec<_>>()
                    },
                    |samples| {
                        // Calculate percentiles
                        let mut sorted = samples.clone();
                        sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());

                        let p50 = sorted[count / 2];
                        let p95 = sorted[(count * 95) / 100];
                        let p99 = sorted[(count * 99) / 100];

                        black_box((p50, p95, p99))
                    },
                    criterion::BatchSize::SmallInput,
                );
            },
        );
    }

    group.finish();
}

/// Benchmark concurrent metric updates
fn bench_concurrent_updates(c: &mut Criterion) {
    let mut group = c.benchmark_group("concurrent_updates");

    for num_threads in &[1, 2, 4, 8] {
        group.bench_with_input(
            BenchmarkId::new("threads", num_threads),
            num_threads,
            |b, &threads| {
                b.iter(|| {
                    let registry = Arc::new(MetricsRegistry::new());
                    let mut handles = vec![];

                    for t in 0..threads {
                        let reg = Arc::clone(&registry);
                        let handle = std::thread::spawn(move || {
                            for i in 0..100 {
                                reg.observe_counter(format!("counter_{}_{}", t, i), 1.0);
                            }
                        });
                        handles.push(handle);
                    }

                    for handle in handles {
                        handle.join().unwrap();
                    }

                    black_box(registry)
                });
            },
        );
    }

    group.finish();
}

/// Benchmark histogram bucket operations
fn bench_histogram_buckets(c: &mut Criterion) {
    let mut group = c.benchmark_group("histogram_buckets");

    for num_buckets in &[10, 50, 100] {
        group.bench_with_input(
            BenchmarkId::new("buckets", num_buckets),
            num_buckets,
            |b, &buckets| {
                b.iter(|| {
                    // Simulate finding appropriate bucket
                    let value = 42.5;
                    let bucket_boundaries: Vec<f64> =
                        (0..buckets).map(|i| (i as f64) * 10.0).collect();

                    let bucket = bucket_boundaries
                        .iter()
                        .position(|&b| value < b)
                        .unwrap_or(buckets - 1);

                    black_box(bucket)
                });
            },
        );
    }

    group.finish();
}

criterion_group! {
    name = metrics_benchmarks;
    config = Criterion::default()
        .measurement_time(Duration::from_secs(10))
        .sample_size(1000)
        .warm_up_time(Duration::from_secs(2))
        .with_plots();
    targets =
        bench_observation_overhead,
        bench_registry_lookup,
        bench_label_cardinality,
        bench_aggregation,
        bench_concurrent_updates,
        bench_histogram_buckets
}

criterion_main!(metrics_benchmarks);

#[cfg(test)]
mod metrics_validation {
    #[allow(unused_imports)]
    use super::*;
    #[allow(unused_imports)]
    use std::time::{Duration, Instant};

    #[test]
    fn validate_observation_overhead() {
        let registry = MetricsRegistry::new();
        let iterations = 100_000_u128;

        let start = Instant::now();
        for i in 0..iterations {
            registry.observe_counter("test_counter".to_string(), i as f64);
        }
        let elapsed = start.elapsed();

        let avg_overhead_ns = elapsed.as_nanos() / iterations;
        let avg_overhead_us = avg_overhead_ns / 1000;

        println!(
            "Average observation overhead: {}ns ({}us)",
            avg_overhead_ns, avg_overhead_us
        );

        // Target: <5us = 5000ns
        assert!(
            avg_overhead_ns < 5000,
            "Observation overhead exceeds 5us target: {}ns",
            avg_overhead_ns
        );
    }

    #[test]
    fn validate_registry_scalability() {
        let registry = MetricsRegistry::new();

        // Add many metrics
        for i in 0..10000 {
            registry.observe_counter(format!("metric_{}", i), 1.0);
        }

        // Measure lookup time with large registry
        let start = Instant::now();
        for _ in 0..1000 {
            registry.observe_counter("metric_5000".to_string(), 1.0);
        }
        let elapsed = start.elapsed();

        let avg_lookup_ns = elapsed.as_nanos() / 1000;

        let metric_count = registry.counters.lock().unwrap().len()
            + registry.gauges.lock().unwrap().len()
            + registry.histograms.lock().unwrap().len();

        println!(
            "Registry with {} metrics, avg lookup: {}ns",
            metric_count, avg_lookup_ns
        );

        // Should maintain O(1) performance
        assert!(
            avg_lookup_ns < 10000,
            "Registry lookup degraded with size: {}ns",
            avg_lookup_ns
        );
    }

    #[test]
    fn validate_label_cardinality() {
        let max_labels = 20;
        let iterations = 10_000_u128;

        let start = Instant::now();
        for _ in 0..iterations {
            let mut labels = HashMap::new();
            for i in 0..max_labels {
                labels.insert(format!("label_{}", i), format!("value_{}", i));
            }

            black_box(labels);
        }
        let elapsed = start.elapsed();

        let avg_time_ns = elapsed.as_nanos() / iterations;

        println!(
            "{} labels per metric, avg creation time: {}ns",
            max_labels, avg_time_ns
        );

        // Should handle high cardinality efficiently
        assert!(
            avg_time_ns < 50000,
            "Label processing too slow: {}ns",
            avg_time_ns
        );
    }

    #[test]
    fn validate_aggregation_performance() {
        let sample_count = 10000;
        let samples: Vec<f64> = (0..sample_count).map(|i| i as f64).collect();

        let start = Instant::now();

        let mut sorted = samples.clone();
        sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());

        let _p50 = sorted[sample_count / 2];
        let _p95 = sorted[(sample_count * 95) / 100];
        let _p99 = sorted[(sample_count * 99) / 100];

        let elapsed = start.elapsed();

        println!("Aggregated {} samples in {:?}", sample_count, elapsed);

        // Target: <100us for aggregation
        assert!(
            elapsed < Duration::from_micros(100),
            "Aggregation too slow: {:?}",
            elapsed
        );
    }
}
