//! Shared slot hash cache accessible from both server and interpreter
//!
//! This module provides a global cache for slot hashes that is populated
//! by the gRPC stream and accessed by computed field resolvers.

use std::collections::BTreeMap;
use std::sync::{Arc, RwLock};

/// Global slot hash cache
static SLOT_HASH_CACHE: once_cell::sync::Lazy<Arc<RwLock<BTreeMap<u64, String>>>> =
    once_cell::sync::Lazy::new(|| Arc::new(RwLock::new(BTreeMap::new())));

/// Maximum number of slot hashes to keep in cache (prevent unbounded growth)
const MAX_CACHE_SIZE: usize = 1000;

/// Record a slot hash in the global cache
pub fn record_slot_hash(slot: u64, slot_hash: String) {
    let mut cache = SLOT_HASH_CACHE.write().expect("RwLock poisoned");
    cache.insert(slot, slot_hash);

    // Prune old entries if cache is too large
    if cache.len() > MAX_CACHE_SIZE {
        // Remove oldest 25% of entries using pop_first for O(log n) per removal
        let target_size = cache.len() - cache.len() / 4;
        while cache.len() > target_size {
            cache.pop_first();
        }
    }
}

/// Get a slot hash from the global cache
pub fn get_slot_hash(slot: u64) -> Option<String> {
    let cache = SLOT_HASH_CACHE.read().expect("RwLock poisoned");
    cache.get(&slot).cloned()
}

/// Check if a slot hash is in the cache
pub fn has_slot_hash(slot: u64) -> bool {
    let cache = SLOT_HASH_CACHE.read().expect("RwLock poisoned");
    cache.contains_key(&slot)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_slot_hash_cache() {
        record_slot_hash(100, "test_hash".to_string());
        assert_eq!(get_slot_hash(100), Some("test_hash".to_string()));
        assert_eq!(get_slot_hash(101), None);
    }
}
