//! Rate Limiting Performance Benchmark
//!
//! Measures rate limiter performance:
//! - TARGET: <50ns per check
//! - Atomic counter operations
//! - Token bucket algorithm
//! - Sliding window counters
//! - Concurrent access patterns

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

/// Atomic counter-based rate limiter (fastest)
struct AtomicRateLimiter {
    counters: HashMap<String, Arc<AtomicU64>>,
    limit: u64,
}

impl AtomicRateLimiter {
    fn new(limit: u64) -> Self {
        Self {
            counters: HashMap::new(),
            limit,
        }
    }

    fn check(&self, user_id: &str) -> bool {
        if let Some(counter) = self.counters.get(user_id) {
            let count = counter.fetch_add(1, Ordering::Relaxed);
            count < self.limit
        } else {
            true // First request always allowed
        }
    }

    fn with_user(mut self, user_id: &str) -> Self {
        self.counters
            .insert(user_id.to_string(), Arc::new(AtomicU64::new(0)));
        self
    }
}

/// Token bucket rate limiter
struct TokenBucket {
    tokens: Mutex<f64>,
    last_refill: Mutex<Instant>,
    capacity: f64,
    refill_rate: f64, // tokens per second
}

impl TokenBucket {
    fn new(capacity: f64, refill_rate: f64) -> Self {
        Self {
            tokens: Mutex::new(capacity),
            last_refill: Mutex::new(Instant::now()),
            capacity,
            refill_rate,
        }
    }

    fn check(&self) -> bool {
        let mut tokens = self.tokens.lock().expect("INVARIANT: Lock should not be poisoned");
        let mut last_refill = self.last_refill.lock().expect("INVARIANT: Lock should not be poisoned");

        let now = Instant::now();
        let elapsed = now.duration_since(*last_refill).as_secs_f64();

        // Refill tokens
        *tokens = (*tokens + (elapsed * self.refill_rate)).min(self.capacity);
        *last_refill = now;

        if *tokens >= 1.0 {
            *tokens -= 1.0;
            true
        } else {
            false
        }
    }
}

/// Sliding window counter
struct SlidingWindow {
    window: Mutex<Vec<Instant>>,
    window_size: Duration,
    limit: usize,
}

impl SlidingWindow {
    fn new(window_size: Duration, limit: usize) -> Self {
        Self {
            window: Mutex::new(Vec::new()),
            window_size,
            limit,
        }
    }

    fn check(&self) -> bool {
        let mut window = self.window.lock().expect("INVARIANT: Lock should not be poisoned");
        let now = Instant::now();

        // Remove expired entries
        window.retain(|&time| now.duration_since(time) < self.window_size);

        if window.len() < self.limit {
            window.push(now);
            true
        } else {
            false
        }
    }
}

/// Benchmark 1: Atomic counter (TARGET: <50ns)
fn bench_atomic_rate_limiter(c: &mut Criterion) {
    let limiter = AtomicRateLimiter::new(1_000_000).with_user("user123");

    c.bench_function("atomic_rate_limiter", |b| {
        b.iter(|| {
            let allowed = limiter.check(black_box("user123"));
            black_box(allowed);
        });
    });
}

/// Benchmark 2: Token bucket algorithm
fn bench_token_bucket(c: &mut Criterion) {
    let bucket = TokenBucket::new(100.0, 100.0);

    c.bench_function("token_bucket_rate_limiter", |b| {
        b.iter(|| {
            let allowed = bucket.check();
            black_box(allowed);
        });
    });
}

/// Benchmark 3: Sliding window counter
fn bench_sliding_window(c: &mut Criterion) {
    let window = SlidingWindow::new(Duration::from_secs(1), 100);

    c.bench_function("sliding_window_rate_limiter", |b| {
        b.iter(|| {
            let allowed = window.check();
            black_box(allowed);
        });
    });
}

/// Benchmark 4: Different user counts
fn bench_user_scaling(c: &mut Criterion) {
    let mut group = c.benchmark_group("rate_limiter_user_scaling");

    for num_users in &[10, 100, 1_000, 10_000] {
        let mut limiter = AtomicRateLimiter::new(1_000_000);
        for i in 0..*num_users {
            limiter = limiter.with_user(&format!("user{}", i));
        }

        group.bench_with_input(
            BenchmarkId::new("atomic_limiter", num_users),
            num_users,
            |b, &n| {
                b.iter(|| {
                    let user_id = format!("user{}", black_box(n / 2));
                    let allowed = limiter.check(&user_id);
                    black_box(allowed);
                });
            },
        );
    }

    group.finish();
}

/// Benchmark 5: Burst handling
fn bench_burst_handling(c: &mut Criterion) {
    let bucket = TokenBucket::new(100.0, 100.0);

    c.bench_function("burst_100_requests", |b| {
        b.iter(|| {
            let mut allowed_count = 0;
            for _ in 0..100 {
                if bucket.check() {
                    allowed_count += 1;
                }
            }
            black_box(allowed_count);
        });
    });
}

/// Benchmark 6: Rate limiter with refill overhead
fn bench_refill_overhead(c: &mut Criterion) {
    let bucket = TokenBucket::new(1000.0, 1000.0);

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

    // Simulate different request patterns
    group.bench_function("high_frequency_no_wait", |b| {
        b.iter(|| {
            let allowed = bucket.check();
            black_box(allowed);
        });
    });

    group.bench_function("with_small_delay", |b| {
        b.iter(|| {
            std::thread::sleep(Duration::from_micros(1));
            let allowed = bucket.check();
            black_box(allowed);
        });
    });

    group.finish();
}

/// Benchmark 7: Concurrent access to rate limiter
fn bench_concurrent_access(c: &mut Criterion) {
    use std::thread;

    let limiter = Arc::new(AtomicRateLimiter::new(1_000_000).with_user("user123"));

    c.bench_function("concurrent_rate_limiter_4_threads", |b| {
        b.iter(|| {
            let mut handles = vec![];

            for _ in 0..4 {
                let limiter_clone = limiter.clone();
                let handle = thread::spawn(move || {
                    for _ in 0..1000 {
                        black_box(limiter_clone.check("user123"));
                    }
                });
                handles.push(handle);
            }

            for handle in handles {
                handle.join().expect("INVARIANT: Thread should complete successfully");
            }
        });
    });
}

/// Benchmark 8: Deny rate (when limit exceeded)
fn bench_deny_rate(c: &mut Criterion) {
    let limiter = AtomicRateLimiter::new(0).with_user("user123"); // Zero limit

    c.bench_function("rate_limiter_deny_path", |b| {
        b.iter(|| {
            let allowed = limiter.check(black_box("user123"));
            assert!(!allowed); // Should always deny
            black_box(allowed);
        });
    });
}

/// Benchmark 9: Cache hit patterns
fn bench_cache_patterns(c: &mut Criterion) {
    let mut group = c.benchmark_group("cache_hit_patterns");

    // Hot path: Same user repeatedly (cache hit)
    let limiter_hot = AtomicRateLimiter::new(1_000_000).with_user("user123");
    group.bench_function("hot_path_same_user", |b| {
        b.iter(|| {
            let allowed = limiter_hot.check(black_box("user123"));
            black_box(allowed);
        });
    });

    // Cold path: Different users (cache miss simulation)
    let mut limiter_cold = AtomicRateLimiter::new(1_000_000);
    for i in 0..1000 {
        limiter_cold = limiter_cold.with_user(&format!("user{}", i));
    }
    let mut counter = 0;
    group.bench_function("cold_path_different_users", |b| {
        b.iter(|| {
            counter += 1;
            let user_id = format!("user{}", counter % 1000);
            let allowed = limiter_cold.check(&user_id);
            black_box(allowed);
        });
    });

    group.finish();
}

/// Benchmark 10: HFT scenario (100K requests/sec target)
fn bench_hft_scenario(c: &mut Criterion) {
    let limiter = AtomicRateLimiter::new(100_000).with_user("hft_trader");

    c.bench_function("hft_100k_rps_scenario", |b| {
        b.iter_custom(|iters| {
            let start = Instant::now();
            for _ in 0..iters {
                black_box(limiter.check("hft_trader"));
            }
            start.elapsed()
        });
    });
}

criterion_group!(
    rate_limiting_benches,
    bench_atomic_rate_limiter,
    bench_token_bucket,
    bench_sliding_window,
    bench_user_scaling,
    bench_burst_handling,
    bench_refill_overhead,
    bench_concurrent_access,
    bench_deny_rate,
    bench_cache_patterns,
    bench_hft_scenario
);

criterion_main!(rate_limiting_benches);
