#![allow(dead_code, unused_variables, clippy::manual_range_contains, clippy::clone_on_copy)]
//! Authorization Service Performance Benchmark
//!
//! Compares performance between:
//! - RwLock<HashMap> (baseline)
//! - DashMap (optimized)
//!
//! Target: <8ns per RBAC check (12x improvement from ~100ns baseline)

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use dashmap::DashMap;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::time::Instant;
use tokio::sync::RwLock;
use uuid::Uuid;

/// User permissions structure
#[derive(Clone, Debug)]
struct UserPermissions {
    user_id: Uuid,
    permissions: HashSet<String>,
    loaded_at: Instant,
}

/// RwLock-based cache (baseline)
struct RwLockAuthzCache {
    cache: Arc<RwLock<HashMap<Uuid, UserPermissions>>>,
}

impl RwLockAuthzCache {
    fn new() -> Self {
        Self {
            cache: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    async fn check_permission(&self, user_id: &Uuid, endpoint: &str) -> bool {
        let cache = self.cache.read().await;
        if let Some(user_perms) = cache.get(user_id) {
            user_perms.permissions.contains(endpoint)
        } else {
            false
        }
    }

    async fn insert(&self, user_id: Uuid, perms: UserPermissions) {
        let mut cache = self.cache.write().await;
        cache.insert(user_id, perms);
    }
}

/// DashMap-based cache (optimized)
struct DashMapAuthzCache {
    cache: Arc<DashMap<Uuid, UserPermissions>>,
}

impl DashMapAuthzCache {
    fn new() -> Self {
        Self {
            cache: Arc::new(DashMap::new()),
        }
    }

    fn check_permission(&self, user_id: &Uuid, endpoint: &str) -> bool {
        if let Some(user_perms_ref) = self.cache.get(user_id) {
            user_perms_ref.permissions.contains(endpoint)
        } else {
            false
        }
    }

    fn insert(&self, user_id: Uuid, perms: UserPermissions) {
        self.cache.insert(user_id, perms);
    }
}

/// Benchmark: RwLock cache read (baseline)
fn bench_rwlock_read(c: &mut Criterion) {
    let rt = tokio::runtime::Runtime::new().unwrap();
    let cache = RwLockAuthzCache::new();

    // Prepopulate with 1000 users
    rt.block_on(async {
        for i in 0..1000 {
            let user_id = Uuid::new_v4();
            let perms = UserPermissions {
                user_id,
                permissions: vec![
                    "/api/trade".to_string(),
                    "/api/portfolio".to_string(),
                    "/api/risk".to_string(),
                ]
                .into_iter()
                .collect(),
                loaded_at: Instant::now(),
            };
            cache.insert(user_id, perms).await;
        }
    });

    // Get a user ID for benchmarking
    let test_user_id = rt.block_on(async {
        let cache_guard = cache.cache.read().await;
        cache_guard.keys().next().copied().unwrap()
    });

    c.bench_function("rwlock_permission_check", |b| {
        b.iter_custom(|iters| {
            let start = Instant::now();
            rt.block_on(async {
                for _ in 0..iters {
                    let result = cache
                        .check_permission(black_box(&test_user_id), black_box("/api/trade"))
                        .await;
                    black_box(result);
                }
            });
            start.elapsed()
        })
    });
}

/// Benchmark: DashMap cache read (optimized)
fn bench_dashmap_read(c: &mut Criterion) {
    let cache = DashMapAuthzCache::new();

    // Prepopulate with 1000 users
    for i in 0..1000 {
        let user_id = Uuid::new_v4();
        let perms = UserPermissions {
            user_id,
            permissions: vec![
                "/api/trade".to_string(),
                "/api/portfolio".to_string(),
                "/api/risk".to_string(),
            ]
            .into_iter()
            .collect(),
            loaded_at: Instant::now(),
        };
        cache.insert(user_id, perms);
    }

    // Get a user ID for benchmarking
    let test_user_id = cache.cache.iter().next().expect("INVARIANT: Iterator should have next element").key().clone();

    c.bench_function("dashmap_permission_check", |b| {
        b.iter(|| {
            let result = cache.check_permission(black_box(&test_user_id), black_box("/api/trade"));
            black_box(result);
        });
    });
}

/// Benchmark: Compare different cache sizes
fn bench_cache_sizes(c: &mut Criterion) {
    let mut group = c.benchmark_group("authz_cache_sizes");

    for size in &[100, 1_000, 10_000, 100_000] {
        // DashMap benchmark
        let dashmap_cache = DashMapAuthzCache::new();
        let mut test_user_ids = Vec::new();

        for i in 0..*size {
            let user_id = Uuid::new_v4();
            test_user_ids.push(user_id);
            let perms = UserPermissions {
                user_id,
                permissions: vec!["/api/trade".to_string(), "/api/portfolio".to_string()]
                    .into_iter()
                    .collect(),
                loaded_at: Instant::now(),
            };
            dashmap_cache.insert(user_id, perms);
        }

        let mid_user = test_user_ids[size / 2];

        group.bench_with_input(BenchmarkId::new("dashmap", size), size, |b, _| {
            b.iter(|| {
                let result =
                    dashmap_cache.check_permission(black_box(&mid_user), black_box("/api/trade"));
                black_box(result);
            });
        });
    }

    group.finish();
}

/// Benchmark: Concurrent reads
fn bench_concurrent_reads(c: &mut Criterion) {
    let mut group = c.benchmark_group("concurrent_reads");

    // DashMap - lock-free concurrent reads
    let dashmap_cache = Arc::new(DashMapAuthzCache::new());
    let mut test_user_ids = Vec::new();

    for i in 0..1000 {
        let user_id = Uuid::new_v4();
        test_user_ids.push(user_id);
        let perms = UserPermissions {
            user_id,
            permissions: vec!["/api/trade".to_string()].into_iter().collect(),
            loaded_at: Instant::now(),
        };
        dashmap_cache.insert(user_id, perms);
    }

    group.bench_function("dashmap_concurrent_8_threads", |b| {
        b.iter(|| {
            let rt = tokio::runtime::Runtime::new().unwrap();
            let cache = Arc::clone(&dashmap_cache);

            rt.block_on(async {
                let mut handles = Vec::new();

                for i in 0..8 {
                    let cache_clone = Arc::clone(&cache);
                    let user_id = test_user_ids[i * 100];

                    let handle = tokio::spawn(async move {
                        for _ in 0..100 {
                            let result = cache_clone.check_permission(&user_id, "/api/trade");
                            black_box(result);
                        }
                    });

                    handles.push(handle);
                }

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

    group.finish();
}

/// Benchmark: Hot path - permission check only
fn bench_hot_path(c: &mut Criterion) {
    let dashmap_cache = DashMapAuthzCache::new();

    // Prepopulate with realistic data
    let mut test_users = Vec::new();
    for i in 0..100 {
        let user_id = Uuid::new_v4();
        test_users.push(user_id);
        let perms = UserPermissions {
            user_id,
            permissions: vec![
                "/api/trade".to_string(),
                "/api/portfolio".to_string(),
                "/api/risk".to_string(),
                "/api/market-data".to_string(),
            ]
            .into_iter()
            .collect(),
            loaded_at: Instant::now(),
        };
        dashmap_cache.insert(user_id, perms);
    }

    let hot_user = test_users[50];

    c.bench_function("hot_path_permission_check", |b| {
        b.iter(|| {
            // Simulate typical RBAC check pattern
            let has_trade =
                dashmap_cache.check_permission(black_box(&hot_user), black_box("/api/trade"));
            let has_portfolio =
                dashmap_cache.check_permission(black_box(&hot_user), black_box("/api/portfolio"));
            black_box((has_trade, has_portfolio));
        });
    });
}

/// Benchmark: Cache invalidation
fn bench_cache_invalidation(c: &mut Criterion) {
    let mut group = c.benchmark_group("cache_invalidation");

    // DashMap invalidation
    let dashmap_cache = DashMapAuthzCache::new();

    for i in 0..1000 {
        let user_id = Uuid::new_v4();
        let perms = UserPermissions {
            user_id,
            permissions: vec!["/api/trade".to_string()].into_iter().collect(),
            loaded_at: Instant::now(),
        };
        dashmap_cache.insert(user_id, perms);
    }

    let test_user = dashmap_cache.cache.iter().next().expect("INVARIANT: Iterator should have next element").key().clone();

    group.bench_function("dashmap_remove", |b| {
        b.iter(|| {
            dashmap_cache.cache.remove(black_box(&test_user));
            // Re-insert for next iteration
            let perms = UserPermissions {
                user_id: test_user,
                permissions: vec!["/api/trade".to_string()].into_iter().collect(),
                loaded_at: Instant::now(),
            };
            dashmap_cache.insert(test_user, perms);
        });
    });

    group.bench_function("dashmap_clear_all", |b| {
        b.iter(|| {
            dashmap_cache.cache.clear();
            // Repopulate for next iteration
            for i in 0..100 {
                let user_id = Uuid::new_v4();
                let perms = UserPermissions {
                    user_id,
                    permissions: vec!["/api/trade".to_string()].into_iter().collect(),
                    loaded_at: Instant::now(),
                };
                dashmap_cache.insert(user_id, perms);
            }
        });
    });

    group.finish();
}

criterion_group!(
    authz_benches,
    bench_rwlock_read,
    bench_dashmap_read,
    bench_cache_sizes,
    bench_concurrent_reads,
    bench_hot_path,
    bench_cache_invalidation
);

criterion_main!(authz_benches);
