#![allow(dead_code)]
//! Cache Performance Benchmark
//!
//! Measures caching layer performance for:
//! - JWT validation cache (decoded tokens)
//! - RBAC permission cache
//! - Revocation list cache
//! - User session cache
//!
//! Targets:
//! - Cache hit: <100ns
//! - Cache miss: <1μs (with lookup)

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

/// Simple LRU cache implementation
struct LruCache<K: Clone + Eq + std::hash::Hash, V: Clone> {
    cache: HashMap<K, (V, Instant)>,
    capacity: usize,
    ttl: Duration,
}

impl<K: Clone + Eq + std::hash::Hash, V: Clone> LruCache<K, V> {
    fn new(capacity: usize, ttl: Duration) -> Self {
        Self {
            cache: HashMap::with_capacity(capacity),
            capacity,
            ttl,
        }
    }

    fn get(&mut self, key: &K) -> Option<V> {
        if let Some((value, timestamp)) = self.cache.get(key) {
            // Check TTL
            if timestamp.elapsed() < self.ttl {
                return Some(value.clone());
            } else {
                // Expired
                self.cache.remove(key);
            }
        }
        None
    }

    fn put(&mut self, key: K, value: V) {
        // Evict if at capacity
        if self.cache.len() >= self.capacity {
            // Simple eviction: remove first entry
            if let Some(k) = self.cache.keys().next().cloned() {
                self.cache.remove(&k);
            }
        }
        self.cache.insert(key, (value, Instant::now()));
    }
}

/// Thread-safe cache wrapper
struct ThreadSafeCache<K: Clone + Eq + std::hash::Hash, V: Clone> {
    cache: Arc<RwLock<LruCache<K, V>>>,
}

impl<K: Clone + Eq + std::hash::Hash, V: Clone> ThreadSafeCache<K, V> {
    fn new(capacity: usize, ttl: Duration) -> Self {
        Self {
            cache: Arc::new(RwLock::new(LruCache::new(capacity, ttl))),
        }
    }

    fn get(&self, key: &K) -> Option<V> {
        self.cache.write().expect("INVARIANT: RwLock should not be poisoned").get(key)
    }

    fn put(&self, key: K, value: V) {
        self.cache.write().expect("INVARIANT: RwLock should not be poisoned").put(key, value);
    }
}

/// JWT cache entry
#[derive(Clone, Debug)]
struct JwtCacheEntry {
    user_id: String,
    roles: Vec<String>,
    permissions: Vec<String>,
    exp: u64,
}

/// RBAC cache entry
#[derive(Clone, Debug)]
struct RbacCacheEntry {
    permissions: Vec<String>,
}

/// Benchmark 1: JWT cache hit (TARGET: <100ns)
fn bench_jwt_cache_hit(c: &mut Criterion) {
    let mut cache = LruCache::new(10_000, Duration::from_secs(300));

    // Prepopulate cache
    for i in 0..1000 {
        let entry = JwtCacheEntry {
            user_id: format!("user{}", i),
            roles: vec!["trader".to_string()],
            permissions: vec!["trade:read".to_string(), "trade:write".to_string()],
            exp: 1234567890,
        };
        cache.put(format!("jwt_token_{}", i), entry);
    }

    c.bench_function("jwt_cache_hit", |b| {
        b.iter(|| {
            let key = format!("jwt_token_{}", black_box(500));
            let entry = cache.get(&key);
            black_box(entry);
        });
    });
}

/// Benchmark 2: JWT cache miss (with decode simulation)
fn bench_jwt_cache_miss(c: &mut Criterion) {
    let mut cache = LruCache::new(10_000, Duration::from_secs(300));

    c.bench_function("jwt_cache_miss_with_decode", |b| {
        b.iter(|| {
            let key = format!("jwt_token_{}", black_box(9999));
            let entry = cache.get(&key);

            if entry.is_none() {
                // Simulate JWT decode (expensive operation)
                let decoded = JwtCacheEntry {
                    user_id: "user9999".to_string(),
                    roles: vec!["trader".to_string()],
                    permissions: vec!["trade:read".to_string()],
                    exp: 1234567890,
                };
                cache.put(key.clone(), decoded.clone());
                black_box(decoded);
            }
        });
    });
}

/// Benchmark 3: RBAC cache hit (TARGET: <100ns)
fn bench_rbac_cache_hit(c: &mut Criterion) {
    let mut cache = LruCache::new(10_000, Duration::from_secs(300));

    // Prepopulate with permissions
    for i in 0..1000 {
        let entry = RbacCacheEntry {
            permissions: vec![
                "trade:read".to_string(),
                "trade:write".to_string(),
                "portfolio:read".to_string(),
            ],
        };
        cache.put(format!("user{}", i), entry);
    }

    c.bench_function("rbac_cache_hit", |b| {
        b.iter(|| {
            let key = format!("user{}", black_box(500));
            let entry = cache.get(&key);
            black_box(entry);
        });
    });
}

/// Benchmark 4: Different cache sizes
fn bench_cache_sizes(c: &mut Criterion) {
    let mut group = c.benchmark_group("cache_size_impact");

    for size in &[100, 1_000, 10_000, 100_000] {
        let mut cache = LruCache::new(*size, Duration::from_secs(300));

        // Prepopulate to capacity
        for i in 0..*size {
            cache.put(format!("key{}", i), format!("value{}", i));
        }

        group.bench_with_input(BenchmarkId::new("cache_lookup", size), size, |b, &n| {
            b.iter(|| {
                let key = format!("key{}", black_box(n / 2));
                let value = cache.get(&key);
                black_box(value);
            });
        });
    }

    group.finish();
}

/// Benchmark 5: Cache eviction overhead
fn bench_cache_eviction(c: &mut Criterion) {
    let mut cache = LruCache::new(100, Duration::from_secs(300));

    // Fill to capacity
    for i in 0..100 {
        cache.put(format!("key{}", i), format!("value{}", i));
    }

    c.bench_function("cache_eviction_on_insert", |b| {
        let mut counter = 100;
        b.iter(|| {
            cache.put(
                format!("key{}", black_box(counter)),
                format!("value{}", counter),
            );
            counter += 1;
        });
    });
}

/// Benchmark 6: TTL expiration check overhead
fn bench_ttl_expiration(c: &mut Criterion) {
    let mut cache_short = LruCache::new(1000, Duration::from_millis(1));
    let mut cache_long = LruCache::new(1000, Duration::from_secs(300));

    // Prepopulate
    for i in 0..100 {
        cache_short.put(format!("key{}", i), format!("value{}", i));
        cache_long.put(format!("key{}", i), format!("value{}", i));
    }

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

    group.bench_function("short_ttl_1ms", |b| {
        b.iter(|| {
            let key = format!("key{}", black_box(50));
            let value = cache_short.get(&key);
            black_box(value);
        });
    });

    group.bench_function("long_ttl_300s", |b| {
        b.iter(|| {
            let key = format!("key{}", black_box(50));
            let value = cache_long.get(&key);
            black_box(value);
        });
    });

    group.finish();
}

/// Benchmark 7: Thread-safe cache with RwLock
fn bench_thread_safe_cache(c: &mut Criterion) {
    let cache = ThreadSafeCache::new(10_000, Duration::from_secs(300));

    // Prepopulate
    for i in 0..1000 {
        cache.put(format!("key{}", i), format!("value{}", i));
    }

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

    group.bench_function("read_lock_cache_hit", |b| {
        b.iter(|| {
            let key = format!("key{}", black_box(500));
            let value = cache.get(&key);
            black_box(value);
        });
    });

    group.bench_function("write_lock_cache_miss", |b| {
        let mut counter = 1000;
        b.iter(|| {
            let key = format!("key{}", black_box(counter));
            let value = cache.get(&key);
            if value.is_none() {
                cache.put(key.clone(), format!("value{}", counter));
            }
            counter += 1;
        });
    });

    group.finish();
}

/// Benchmark 8: Hot/cold cache patterns
fn bench_hot_cold_patterns(c: &mut Criterion) {
    let mut group = c.benchmark_group("hot_cold_patterns");

    // Hot cache: Small working set
    let mut hot_cache = LruCache::new(10_000, Duration::from_secs(300));
    for i in 0..10 {
        hot_cache.put(format!("hot_key{}", i), format!("value{}", i));
    }

    group.bench_function("hot_cache_10_keys", |b| {
        b.iter(|| {
            let key = format!("hot_key{}", black_box(5));
            let value = hot_cache.get(&key);
            black_box(value);
        });
    });

    // Cold cache: Large working set with misses
    let mut cold_cache = LruCache::new(100, Duration::from_secs(300));
    for i in 0..100 {
        cold_cache.put(format!("cold_key{}", i), format!("value{}", i));
    }

    let mut counter = 0;
    group.bench_function("cold_cache_100_keys_rotating", |b| {
        b.iter(|| {
            counter += 1;
            let key = format!("cold_key{}", black_box(counter % 150));
            let value = cold_cache.get(&key);
            if value.is_none() {
                cold_cache.put(key.clone(), format!("value{}", counter));
            }
            black_box(value);
        });
    });

    group.finish();
}

/// Benchmark 9: Multi-tier cache (L1 + L2)
fn bench_multi_tier_cache(c: &mut Criterion) {
    let l1_cache = Arc::new(RwLock::new(LruCache::<String, String>::new(
        100,
        Duration::from_secs(60),
    )));
    let l2_cache = Arc::new(RwLock::new(LruCache::<String, String>::new(
        10_000,
        Duration::from_secs(300),
    )));

    // Prepopulate L2
    for i in 0..1000 {
        l2_cache
            .write()
            .unwrap()
            .put(format!("key{}", i), format!("value{}", i));
    }

    // Prepopulate L1 with hot keys
    for i in 0..50 {
        l1_cache
            .write()
            .unwrap()
            .put(format!("key{}", i), format!("value{}", i));
    }

    c.bench_function("multi_tier_cache_l1_hit", |b| {
        b.iter(|| {
            let key = format!("key{}", black_box(25));

            // Check L1
            let value = l1_cache.write().expect("INVARIANT: RwLock should not be poisoned").get(&key);
            if value.is_none() {
                // Check L2
                if let Some(v) = l2_cache.write().expect("INVARIANT: RwLock should not be poisoned").get(&key) {
                    l1_cache.write().expect("INVARIANT: RwLock should not be poisoned").put(key, v.clone());
                    black_box(v);
                }
            } else {
                black_box(value);
            }
        });
    });
}

/// Benchmark 10: Revocation list lookup
fn bench_revocation_list(c: &mut Criterion) {
    // Simulate revocation list as HashSet
    let mut revoked_tokens = std::collections::HashSet::new();
    for i in 0..10000 {
        revoked_tokens.insert(format!("revoked_token_{}", i));
    }

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

    group.bench_function("revoked_token_hit", |b| {
        b.iter(|| {
            let jti = format!("revoked_token_{}", black_box(5000));
            let is_revoked = revoked_tokens.contains(&jti);
            black_box(is_revoked);
        });
    });

    group.bench_function("revoked_token_miss", |b| {
        b.iter(|| {
            let jti = format!("valid_token_{}", black_box(5000));
            let is_revoked = revoked_tokens.contains(&jti);
            black_box(is_revoked);
        });
    });

    group.finish();
}

criterion_group!(
    cache_benches,
    bench_jwt_cache_hit,
    bench_jwt_cache_miss,
    bench_rbac_cache_hit,
    bench_cache_sizes,
    bench_cache_eviction,
    bench_ttl_expiration,
    bench_thread_safe_cache,
    bench_hot_cold_patterns,
    bench_multi_tier_cache,
    bench_revocation_list
);

criterion_main!(cache_benches);
