//! KMS Caching System
//!
//! Provides intelligent caching with TTL, offline fallback, and multiple
//! backend support including in-memory, Redis, and local file-based caching.

use std::collections::HashMap;
use std::sync::Arc;

use async_trait::async_trait;
use chrono::{DateTime, Utc};
use redis::{aio::ConnectionManager, AsyncCommands, Client as RedisClient};
use serde::{Deserialize, Serialize};
use tokio::sync::RwLock;

use crate::kms::error::KmsError;
use crate::kms::traits::CacheBackend as CacheBackendTrait;
use crate::kms::types::{CacheBackend, CacheConfig, HealthStatus};

/// Cache entry with metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheEntry {
    pub data: Vec<u8>,
    pub created_at: DateTime<Utc>,
    pub expires_at: DateTime<Utc>,
    pub access_count: u64,
    pub last_accessed: DateTime<Utc>,
    pub metadata: HashMap<String, String>,
}

impl CacheEntry {
    pub fn new(data: Vec<u8>, ttl_seconds: u64) -> Self {
        let now = Utc::now();
        Self {
            data,
            created_at: now,
            expires_at: now + chrono::Duration::seconds(ttl_seconds as i64),
            access_count: 0,
            last_accessed: now,
            metadata: HashMap::new(),
        }
    }

    pub fn is_expired(&self) -> bool {
        Utc::now() > self.expires_at
    }

    pub fn access(&mut self) {
        self.access_count += 1;
        self.last_accessed = Utc::now();
    }

    pub fn extend_ttl(&mut self, additional_seconds: u64) {
        self.expires_at += chrono::Duration::seconds(additional_seconds as i64);
    }
}

/// Multi-backend cache manager with intelligent failover
pub struct CacheManager {
    primary: Box<dyn CacheBackendTrait>,
    fallback: Option<Box<dyn CacheBackendTrait>>,
    config: CacheConfig,
    cache_stats: Arc<RwLock<CacheStats>>,
}

/// Cache statistics
#[derive(Debug, Clone, Default)]
pub struct CacheStats {
    pub hits: u64,
    pub misses: u64,
    pub evictions: u64,
    pub errors: u64,
    pub total_size_bytes: u64,
    pub entry_count: u64,
    pub last_cleanup: Option<DateTime<Utc>>,
}

impl CacheStats {
    pub fn hit_rate(&self) -> f64 {
        if self.hits + self.misses == 0 {
            0.0
        } else {
            self.hits as f64 / (self.hits + self.misses) as f64
        }
    }
}

impl CacheManager {
    /// Create a new cache manager with primary and optional fallback backends
    pub async fn new(config: CacheConfig) -> Result<Self, KmsError> {
        let primary = Self::create_backend(&config.backend, &config).await?;

        // Create local file fallback for remote backends
        let fallback = match config.backend {
            CacheBackend::Redis => {
                let local_config = CacheConfig {
                    backend: CacheBackend::Local,
                    local_dir: config.local_dir.clone(),
                    ..config.clone()
                };
                Some(Self::create_backend(&CacheBackend::Local, &local_config).await?)
            }
            _ => None,
        };

        Ok(Self {
            primary,
            fallback,
            config,
            cache_stats: Arc::new(RwLock::new(CacheStats::default())),
        })
    }

    /// Create a cache backend based on configuration
    async fn create_backend(
        backend_type: &CacheBackend,
        config: &CacheConfig,
    ) -> Result<Box<dyn CacheBackendTrait>, KmsError> {
        match backend_type {
            CacheBackend::Memory => Ok(Box::new(MemoryCache::new(config.clone())?)),
            CacheBackend::Redis => Ok(Box::new(RedisCache::new(config.clone()).await?)),
            CacheBackend::Local => Ok(Box::new(LocalFileCache::new(config.clone()).await?)),
        }
    }

    /// Get cache statistics
    pub async fn get_stats(&self) -> CacheStats {
        self.cache_stats.read().await.clone()
    }

    /// Cleanup expired entries
    pub async fn cleanup(&self) -> Result<u64, KmsError> {
        let cleaned = self
            .primary
            .stats()
            .await?
            .get("expired_entries")
            .copied()
            .unwrap_or(0) as u64;

        if let Some(fallback) = &self.fallback {
            let _ = fallback.stats().await; // Trigger cleanup on fallback
        }

        let mut cache_stats = self.cache_stats.write().await;
        cache_stats.last_cleanup = Some(Utc::now());
        cache_stats.evictions += cleaned;

        Ok(cleaned)
    }
}

#[async_trait]
impl CacheBackendTrait for CacheManager {
    async fn set(&self, key: &str, value: &[u8], ttl_seconds: u64) -> Result<(), KmsError> {
        match self.primary.set(key, value, ttl_seconds).await {
            Ok(_) => {
                let mut cache_stats = self.cache_stats.write().await;
                cache_stats.entry_count += 1;
                cache_stats.total_size_bytes += value.len() as u64;
                Ok(())
            }
            Err(e) => {
                let mut cache_stats = self.cache_stats.write().await;
                cache_stats.errors += 1;

                // Try fallback if available
                if let Some(fallback) = &self.fallback {
                    fallback.set(key, value, ttl_seconds).await
                } else {
                    Err(e)
                }
            }
        }
    }

    async fn get(&self, key: &str) -> Result<Option<Vec<u8>>, KmsError> {
        match self.primary.get(key).await {
            Ok(Some(value)) => {
                let mut cache_stats = self.cache_stats.write().await;
                cache_stats.hits += 1;
                Ok(Some(value))
            }
            Ok(None) => {
                // Try fallback if available
                match &self.fallback {
                    Some(fallback) => {
                        match fallback.get(key).await {
                            Ok(Some(value)) => {
                                // Restore to primary cache
                                let _ = self
                                    .primary
                                    .set(key, &value, self.config.default_ttl_seconds)
                                    .await;
                                let mut cache_stats = self.cache_stats.write().await;
                                cache_stats.hits += 1;
                                Ok(Some(value))
                            }
                            Ok(None) => {
                                let mut cache_stats = self.cache_stats.write().await;
                                cache_stats.misses += 1;
                                Ok(None)
                            }
                            Err(_) => {
                                let mut cache_stats = self.cache_stats.write().await;
                                cache_stats.misses += 1;
                                Ok(None)
                            }
                        }
                    }
                    None => {
                        let mut cache_stats = self.cache_stats.write().await;
                        cache_stats.misses += 1;
                        Ok(None)
                    }
                }
            }
            Err(e) => {
                let mut cache_stats = self.cache_stats.write().await;
                cache_stats.errors += 1;

                // Try fallback if available
                if let Some(fallback) = &self.fallback {
                    fallback.get(key).await
                } else {
                    Err(e)
                }
            }
        }
    }

    async fn delete(&self, key: &str) -> Result<bool, KmsError> {
        let primary_result = self.primary.delete(key).await.unwrap_or(false);
        let fallback_result = if let Some(fallback) = &self.fallback {
            fallback.delete(key).await.unwrap_or(false)
        } else {
            false
        };

        Ok(primary_result || fallback_result)
    }

    async fn exists(&self, key: &str) -> Result<bool, KmsError> {
        match self.primary.exists(key).await {
            Ok(true) => Ok(true),
            _ => {
                if let Some(fallback) = &self.fallback {
                    fallback.exists(key).await
                } else {
                    Ok(false)
                }
            }
        }
    }

    async fn expire(&self, key: &str, ttl_seconds: u64) -> Result<bool, KmsError> {
        let primary_result = self.primary.expire(key, ttl_seconds).await.unwrap_or(false);
        let fallback_result = if let Some(fallback) = &self.fallback {
            fallback.expire(key, ttl_seconds).await.unwrap_or(false)
        } else {
            false
        };

        Ok(primary_result || fallback_result)
    }

    async fn clear(&self) -> Result<(), KmsError> {
        let _ = self.primary.clear().await;
        if let Some(fallback) = &self.fallback {
            let _ = fallback.clear().await;
        }

        let mut cache_stats = self.cache_stats.write().await;
        cache_stats.entry_count = 0;
        cache_stats.total_size_bytes = 0;

        Ok(())
    }

    async fn stats(&self) -> Result<HashMap<String, i64>, KmsError> {
        let cache_stats_data = self.cache_stats.read().await;
        let mut stats = HashMap::new();

        stats.insert("hits".to_string(), cache_stats_data.hits as i64);
        stats.insert("misses".to_string(), cache_stats_data.misses as i64);
        stats.insert(
            "hit_rate".to_string(),
            (cache_stats_data.hit_rate() * 100.0) as i64,
        );
        stats.insert("evictions".to_string(), cache_stats_data.evictions as i64);
        stats.insert("errors".to_string(), cache_stats_data.errors as i64);
        stats.insert(
            "entry_count".to_string(),
            cache_stats_data.entry_count as i64,
        );
        stats.insert(
            "total_size_bytes".to_string(),
            cache_stats_data.total_size_bytes as i64,
        );

        Ok(stats)
    }

    async fn health_check(&self) -> Result<HealthStatus, KmsError> {
        let primary_health = self.primary.health_check().await?;
        let fallback_healthy = if let Some(fallback) = &self.fallback {
            fallback.health_check().await?.healthy
        } else {
            true
        };

        let stats = self.stats().await?;
        Ok(HealthStatus {
            healthy: primary_health.healthy || fallback_healthy,
            message: format!(
                "Cache manager - Primary: {}, Fallback available: {}",
                primary_health.healthy,
                self.fallback.is_some()
            ),
            last_check: Utc::now(),
            metrics: stats.into_iter().map(|(k, v)| (k, v as f64)).collect(),
        })
    }
}

/// In-memory cache implementation with LRU eviction
pub struct MemoryCache {
    entries: Arc<RwLock<HashMap<String, CacheEntry>>>,
    config: CacheConfig,
}

impl MemoryCache {
    pub fn new(config: CacheConfig) -> Result<Self, KmsError> {
        Ok(Self {
            entries: Arc::new(RwLock::new(HashMap::new())),
            config,
        })
    }

    async fn evict_expired(&self) -> u64 {
        let mut entries = self.entries.write().await;
        let initial_count = entries.len();
        entries.retain(|_, entry| !entry.is_expired());
        (initial_count - entries.len()) as u64
    }

    async fn evict_lru(&self) -> u64 {
        let mut entries = self.entries.write().await;
        let current_size: u64 = entries.values().map(|e| e.data.len() as u64).sum();

        if current_size <= self.config.max_size_bytes {
            return 0;
        }

        // Sort by last accessed time and remove oldest
        let mut sorted_keys: Vec<_> = entries
            .iter()
            .map(|(k, v)| (k.clone(), v.last_accessed))
            .collect();
        sorted_keys.sort_by(|a, b| a.1.cmp(&b.1));

        let mut evicted = 0;
        let mut remaining_size = current_size;

        for (key, _) in sorted_keys {
            if remaining_size <= self.config.max_size_bytes {
                break;
            }

            if let Some(entry) = entries.remove(&key) {
                remaining_size -= entry.data.len() as u64;
                evicted += 1;
            }
        }

        evicted
    }
}

#[async_trait]
impl CacheBackendTrait for MemoryCache {
    async fn set(&self, key: &str, value: &[u8], ttl_seconds: u64) -> Result<(), KmsError> {
        // Evict expired entries
        self.evict_expired().await;

        let entry = CacheEntry::new(value.to_vec(), ttl_seconds);
        self.entries.write().await.insert(key.to_string(), entry);

        // Evict LRU if over size limit
        self.evict_lru().await;

        Ok(())
    }

    async fn get(&self, key: &str) -> Result<Option<Vec<u8>>, KmsError> {
        let mut entries = self.entries.write().await;

        if let Some(entry) = entries.get_mut(key) {
            if entry.is_expired() {
                entries.remove(key);
                Ok(None)
            } else {
                entry.access();
                Ok(Some(entry.data.clone()))
            }
        } else {
            Ok(None)
        }
    }

    async fn delete(&self, key: &str) -> Result<bool, KmsError> {
        Ok(self.entries.write().await.remove(key).is_some())
    }

    async fn exists(&self, key: &str) -> Result<bool, KmsError> {
        let entries = self.entries.read().await;
        match entries.get(key) {
            Some(entry) => Ok(!entry.is_expired()),
            None => Ok(false),
        }
    }

    async fn expire(&self, key: &str, ttl_seconds: u64) -> Result<bool, KmsError> {
        let mut entries = self.entries.write().await;
        match entries.get_mut(key) {
            Some(entry) => {
                entry.expires_at = Utc::now() + chrono::Duration::seconds(ttl_seconds as i64);
                Ok(true)
            }
            None => Ok(false),
        }
    }

    async fn clear(&self) -> Result<(), KmsError> {
        self.entries.write().await.clear();
        Ok(())
    }

    async fn stats(&self) -> Result<HashMap<String, i64>, KmsError> {
        let entries = self.entries.read().await;
        let mut stats = HashMap::new();

        stats.insert("entry_count".to_string(), entries.len() as i64);
        stats.insert(
            "total_size_bytes".to_string(),
            entries.values().map(|e| e.data.len() as i64).sum(),
        );

        let expired = entries.values().filter(|e| e.is_expired()).count() as i64;
        stats.insert("expired_entries".to_string(), expired);

        Ok(stats)
    }

    async fn health_check(&self) -> Result<HealthStatus, KmsError> {
        let stats = self.stats().await?;
        Ok(HealthStatus::healthy("Memory cache operational")
            .with_metrics(stats.into_iter().map(|(k, v)| (k, v as f64)).collect()))
    }
}

/// Redis cache implementation
pub struct RedisCache {
    connection: ConnectionManager,
    /// Configuration for TTL and cache behavior
    #[allow(dead_code)]
    config: CacheConfig,
}

impl RedisCache {
    pub async fn new(config: CacheConfig) -> Result<Self, KmsError> {
        let redis_url = config
            .redis_url
            .as_ref()
            .ok_or_else(|| KmsError::config("Redis URL required for Redis cache"))?;

        let client = RedisClient::open(redis_url.as_str()).map_err(|e| {
            KmsError::cache(
                "redis_connect",
                format!("Failed to create Redis client: {}", e),
            )
        })?;

        let connection = ConnectionManager::new(client).await.map_err(|e| {
            KmsError::cache(
                "redis_connect",
                format!("Failed to connect to Redis: {}", e),
            )
        })?;

        Ok(Self { connection, config })
    }

    fn cache_key(&self, key: &str) -> String {
        format!("kms:cache:{}", key)
    }
}

#[async_trait]
impl CacheBackendTrait for RedisCache {
    async fn set(&self, key: &str, value: &[u8], ttl_seconds: u64) -> Result<(), KmsError> {
        let mut conn = self.connection.clone();
        let cache_key = self.cache_key(key);

        // Use set_ex instead of setex for async redis
        redis::cmd("SETEX")
            .arg(&cache_key)
            .arg(ttl_seconds)
            .arg(value)
            .query_async::<()>(&mut conn)
            .await
            .map_err(|e| {
                KmsError::cache("redis_set", format!("Failed to set cache entry: {}", e))
            })?;

        Ok(())
    }

    async fn get(&self, key: &str) -> Result<Option<Vec<u8>>, KmsError> {
        let mut conn = self.connection.clone();
        let cache_key = self.cache_key(key);

        let result: Option<Vec<u8>> = conn.get(&cache_key).await.map_err(|e| {
            KmsError::cache("redis_get", format!("Failed to get cache entry: {}", e))
        })?;

        Ok(result)
    }

    async fn delete(&self, key: &str) -> Result<bool, KmsError> {
        let mut conn = self.connection.clone();
        let cache_key = self.cache_key(key);

        let deleted: u32 = conn.del(&cache_key).await.map_err(|e| {
            KmsError::cache(
                "redis_delete",
                format!("Failed to delete cache entry: {}", e),
            )
        })?;

        Ok(deleted > 0)
    }

    async fn exists(&self, key: &str) -> Result<bool, KmsError> {
        let mut conn = self.connection.clone();
        let cache_key = self.cache_key(key);

        let exists: bool = conn.exists(&cache_key).await.map_err(|e| {
            KmsError::cache(
                "redis_exists",
                format!("Failed to check cache entry: {}", e),
            )
        })?;

        Ok(exists)
    }

    async fn expire(&self, key: &str, ttl_seconds: u64) -> Result<bool, KmsError> {
        let mut conn = self.connection.clone();
        let cache_key = self.cache_key(key);

        // Use cmd instead of direct expire call
        let result: i32 = redis::cmd("EXPIRE")
            .arg(&cache_key)
            .arg(ttl_seconds as i64)
            .query_async(&mut conn)
            .await
            .map_err(|e| {
                KmsError::cache("redis_expire", format!("Failed to set expiration: {}", e))
            })?;

        Ok(result == 1)
    }

    async fn clear(&self) -> Result<(), KmsError> {
        let mut conn = self.connection.clone();

        // Get all cache keys
        let pattern = self.cache_key("*");
        let keys: Vec<String> = conn.keys(&pattern).await.map_err(|e| {
            KmsError::cache("redis_clear", format!("Failed to get cache keys: {}", e))
        })?;

        if !keys.is_empty() {
            let _: () = conn.del(&keys).await.map_err(|e| {
                KmsError::cache("redis_clear", format!("Failed to delete cache keys: {}", e))
            })?;
        }

        Ok(())
    }

    async fn stats(&self) -> Result<HashMap<String, i64>, KmsError> {
        let mut conn = self.connection.clone();
        let mut stats = HashMap::new();

        // Get Redis info using cmd
        let info: String = redis::cmd("INFO")
            .arg("memory")
            .query_async(&mut conn)
            .await
            .map_err(|e| {
                KmsError::cache("redis_stats", format!("Failed to get Redis info: {}", e))
            })?;

        // Parse memory usage (simplified)
        for line in info.lines() {
            if let Some((key, value)) = line.split_once(':') {
                match key {
                    "used_memory" => {
                        if let Ok(mem) = value.trim().parse::<i64>() {
                            stats.insert("used_memory_bytes".to_string(), mem);
                        }
                    }
                    "used_memory_peak" => {
                        if let Ok(mem) = value.trim().parse::<i64>() {
                            stats.insert("peak_memory_bytes".to_string(), mem);
                        }
                    }
                    _ => {}
                }
            }
        }

        // Count cache entries
        let pattern = self.cache_key("*");
        let keys: Vec<String> = redis::cmd("KEYS")
            .arg(&pattern)
            .query_async(&mut conn)
            .await
            .map_err(|e| KmsError::cache("redis_stats", format!("Failed to count keys: {}", e)))?;

        stats.insert("entry_count".to_string(), keys.len() as i64);

        Ok(stats)
    }

    async fn health_check(&self) -> Result<HealthStatus, KmsError> {
        let mut conn = self.connection.clone();
        let start = std::time::Instant::now();

        // Test with a ping using cmd
        let result: String = redis::cmd("PING")
            .query_async(&mut conn)
            .await
            .map_err(|e| KmsError::cache("redis_health", format!("Redis ping failed: {}", e)))?;

        let elapsed = start.elapsed();
        let mut metrics = HashMap::new();
        metrics.insert("ping_time_ms".to_string(), elapsed.as_millis() as f64);

        if result == "PONG" {
            Ok(HealthStatus {
                healthy: true,
                message: "Redis cache operational".to_string(),
                last_check: Utc::now(),
                metrics,
            })
        } else {
            Ok(HealthStatus::unhealthy(
                "Redis ping returned unexpected response",
            ))
        }
    }
}

/// Local file-based cache implementation
pub struct LocalFileCache {
    cache_dir: std::path::PathBuf,
    /// Configuration for TTL and cache behavior
    #[allow(dead_code)]
    config: CacheConfig,
}

impl LocalFileCache {
    pub async fn new(config: CacheConfig) -> Result<Self, KmsError> {
        let cache_dir = std::path::PathBuf::from(
            config
                .local_dir
                .as_ref()
                .ok_or_else(|| KmsError::config("Local cache directory required"))?,
        );

        // Create cache directory if it doesn't exist
        tokio::fs::create_dir_all(&cache_dir)
            .await
            .map_err(|e| KmsError::io(format!("Failed to create cache directory: {}", e)))?;

        Ok(Self { cache_dir, config })
    }

    fn entry_path(&self, key: &str) -> std::path::PathBuf {
        // Use a hash of the key to avoid filesystem issues with special characters
        use std::collections::hash_map::DefaultHasher;
        use std::hash::{Hash, Hasher};

        let mut hasher = DefaultHasher::new();
        key.hash(&mut hasher);
        let hash = hasher.finish();

        self.cache_dir.join(format!("{:016x}.cache", hash))
    }
}

#[async_trait]
impl CacheBackendTrait for LocalFileCache {
    async fn set(&self, key: &str, value: &[u8], ttl_seconds: u64) -> Result<(), KmsError> {
        let entry = CacheEntry::new(value.to_vec(), ttl_seconds);
        let entry_path = self.entry_path(key);

        let serialized = serde_json::to_vec(&entry).map_err(|e| {
            KmsError::serialization(format!("Failed to serialize cache entry: {}", e))
        })?;

        tokio::fs::write(&entry_path, serialized)
            .await
            .map_err(|e| KmsError::io(format!("Failed to write cache entry: {}", e)))?;

        Ok(())
    }

    async fn get(&self, key: &str) -> Result<Option<Vec<u8>>, KmsError> {
        let entry_path = self.entry_path(key);

        match tokio::fs::read(&entry_path).await {
            Ok(data) => {
                let mut entry: CacheEntry = serde_json::from_slice(&data).map_err(|e| {
                    KmsError::serialization(format!("Failed to deserialize cache entry: {}", e))
                })?;

                if entry.is_expired() {
                    // Remove expired entry
                    let _ = tokio::fs::remove_file(&entry_path).await;
                    Ok(None)
                } else {
                    entry.access();
                    // Update access statistics
                    let serialized = serde_json::to_vec(&entry).map_err(|e| {
                        KmsError::serialization(format!("Failed to serialize cache entry: {}", e))
                    })?;
                    let _ = tokio::fs::write(&entry_path, serialized).await;

                    Ok(Some(entry.data))
                }
            }
            Err(_) => Ok(None),
        }
    }

    async fn delete(&self, key: &str) -> Result<bool, KmsError> {
        let entry_path = self.entry_path(key);
        match tokio::fs::remove_file(&entry_path).await {
            Ok(_) => Ok(true),
            Err(_) => Ok(false),
        }
    }

    async fn exists(&self, key: &str) -> Result<bool, KmsError> {
        let entry_path = self.entry_path(key);
        Ok(entry_path.exists())
    }

    async fn expire(&self, key: &str, ttl_seconds: u64) -> Result<bool, KmsError> {
        let entry_path = self.entry_path(key);

        match tokio::fs::read(&entry_path).await {
            Ok(data) => match serde_json::from_slice::<CacheEntry>(&data) {
                Ok(mut entry) => {
                    entry.extend_ttl(ttl_seconds);
                    let serialized = serde_json::to_vec(&entry).map_err(|e| {
                        KmsError::serialization(format!("Failed to serialize cache entry: {}", e))
                    })?;
                    tokio::fs::write(&entry_path, serialized)
                        .await
                        .map_err(|e| {
                            KmsError::io(format!("Failed to update cache entry: {}", e))
                        })?;
                    Ok(true)
                }
                Err(_) => Ok(false),
            },
            Err(_) => Ok(false),
        }
    }

    async fn clear(&self) -> Result<(), KmsError> {
        let mut entries = tokio::fs::read_dir(&self.cache_dir)
            .await
            .map_err(|e| KmsError::io(format!("Failed to read cache directory: {}", e)))?;

        while let Some(entry) = entries
            .next_entry()
            .await
            .map_err(|e| KmsError::io(format!("Failed to read directory entry: {}", e)))?
        {
            if let Some(ext) = entry.path().extension() {
                if ext == "cache" {
                    let _ = tokio::fs::remove_file(entry.path()).await;
                }
            }
        }

        Ok(())
    }

    async fn stats(&self) -> Result<HashMap<String, i64>, KmsError> {
        let mut stats = HashMap::new();
        let mut entry_count = 0i64;
        let mut total_size = 0i64;
        let mut expired_count = 0i64;

        let mut entries = tokio::fs::read_dir(&self.cache_dir)
            .await
            .map_err(|e| KmsError::io(format!("Failed to read cache directory: {}", e)))?;

        while let Some(entry) = entries
            .next_entry()
            .await
            .map_err(|e| KmsError::io(format!("Failed to read directory entry: {}", e)))?
        {
            if let Some(ext) = entry.path().extension() {
                if ext == "cache" {
                    entry_count += 1;

                    if let Ok(data) = tokio::fs::read(entry.path()).await {
                        total_size += data.len() as i64;

                        if let Ok(cache_entry) = serde_json::from_slice::<CacheEntry>(&data) {
                            if cache_entry.is_expired() {
                                expired_count += 1;
                            }
                        }
                    }
                }
            }
        }

        stats.insert("entry_count".to_string(), entry_count);
        stats.insert("total_size_bytes".to_string(), total_size);
        stats.insert("expired_entries".to_string(), expired_count);

        Ok(stats)
    }

    async fn health_check(&self) -> Result<HealthStatus, KmsError> {
        // Check if cache directory is accessible
        match tokio::fs::read_dir(&self.cache_dir).await {
            Ok(_) => {
                let stats = self.stats().await?;
                Ok(HealthStatus {
                    healthy: true,
                    message: "Local file cache operational".to_string(),
                    last_check: Utc::now(),
                    metrics: stats.into_iter().map(|(k, v)| (k, v as f64)).collect(),
                })
            }
            Err(e) => Ok(HealthStatus::unhealthy(format!(
                "Cache directory inaccessible: {}",
                e
            ))),
        }
    }
}
