//! Temporal SSH Key Manager
//!
//! Handles automatic expiration and cleanup of SSH keys with TTL tracking.

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

use anyhow::Result;
use chrono::{DateTime, Duration, Utc};
use tokio::sync::RwLock;
use tracing::{debug, error, info, warn};

use super::key_deployer::KeyDeployer;
use super::TemporalSshKey;

/// Temporal manager for SSH keys
pub struct TemporalManager {
    /// Shared key storage
    key_storage: Arc<RwLock<HashMap<String, TemporalSshKey>>>,
    /// Key deployer for cleanup
    key_deployer: Arc<KeyDeployer>,
    /// Cleanup interval
    cleanup_interval: Duration,
    /// Last cleanup statistics
    last_cleanup_stats: Arc<RwLock<(usize, Option<DateTime<Utc>>)>>,
}

impl TemporalManager {
    /// Create new temporal manager
    pub fn new(
        key_storage: Arc<RwLock<HashMap<String, TemporalSshKey>>>,
        key_deployer: Arc<KeyDeployer>,
        cleanup_interval: Duration,
    ) -> Self {
        Self {
            key_storage,
            key_deployer,
            cleanup_interval,
            last_cleanup_stats: Arc::new(RwLock::new((0, None))),
        }
    }

    /// Start background cleanup task
    pub async fn start_cleanup_task(&self) {
        info!(
            "Starting SSH key cleanup task with interval: {} seconds",
            self.cleanup_interval.num_seconds()
        );

        loop {
            // Sleep for cleanup interval
            let sleep_duration =
                std::time::Duration::from_secs(self.cleanup_interval.num_seconds().max(1) as u64);
            tokio::time::sleep(sleep_duration).await;

            // Run cleanup
            match self.cleanup_expired_keys().await {
                Ok(cleaned_keys) => {
                    if !cleaned_keys.is_empty() {
                        info!("Cleaned up {} expired SSH keys", cleaned_keys.len());
                        debug!("Cleaned key IDs: {:?}", cleaned_keys);
                    }
                }
                Err(e) => {
                    error!("Failed to cleanup expired SSH keys: {}", e);
                }
            }
        }
    }

    /// Cleanup expired SSH keys
    pub async fn cleanup_expired_keys(&self) -> Result<Vec<String>> {
        let now = Utc::now();
        let mut cleaned_keys = Vec::new();

        // Find expired keys
        let expired_keys: Vec<TemporalSshKey> = {
            let storage = self.key_storage.read().await;
            storage
                .values()
                .filter(|k| k.expires_at <= now)
                .cloned()
                .collect()
        };

        if expired_keys.is_empty() {
            debug!("No expired SSH keys to cleanup");
            return Ok(cleaned_keys);
        }

        info!("Found {} expired SSH keys to cleanup", expired_keys.len());

        // Remove keys from servers
        for key in &expired_keys {
            match self.remove_expired_key(key).await {
                Ok(_) => {
                    cleaned_keys.push(key.id.clone());
                    info!(
                        "Successfully removed expired key {} from server {}",
                        key.id, key.target_server
                    );
                }
                Err(e) => {
                    warn!(
                        "Failed to remove expired key {} from server {}: {}",
                        key.id, key.target_server, e
                    );
                }
            }
        }

        // Remove from storage
        {
            let mut storage = self.key_storage.write().await;
            for key_id in &cleaned_keys {
                storage.remove(key_id);
            }
        }

        // Update statistics
        {
            let mut stats = self.last_cleanup_stats.write().await;
            *stats = (cleaned_keys.len(), Some(now));
        }

        Ok(cleaned_keys)
    }

    /// Remove a single expired key
    async fn remove_expired_key(&self, key: &TemporalSshKey) -> Result<()> {
        if !key.deployed {
            debug!("Key {} was never deployed, skipping removal", key.id);
            return Ok(());
        }

        // Remove from server
        self.key_deployer.remove(key).await?;

        Ok(())
    }

    /// Get keys expiring within duration
    pub async fn get_expiring_keys(&self, within: Duration) -> Result<Vec<TemporalSshKey>> {
        let now = Utc::now();
        let threshold = now + within;

        let storage = self.key_storage.read().await;
        let expiring_keys: Vec<TemporalSshKey> = storage
            .values()
            .filter(|k| k.expires_at > now && k.expires_at <= threshold)
            .cloned()
            .collect();

        Ok(expiring_keys)
    }

    /// Get last cleanup statistics
    pub async fn get_last_cleanup_stats(&self) -> (usize, Option<DateTime<Utc>>) {
        let stats = self.last_cleanup_stats.read().await;
        *stats
    }

    /// Force cleanup of specific key
    pub async fn force_cleanup(&self, key_id: &str) -> Result<()> {
        let key = {
            let mut storage = self.key_storage.write().await;
            storage
                .remove(key_id)
                .ok_or_else(|| anyhow::anyhow!("Key not found"))?
        };

        if key.deployed {
            self.key_deployer.remove(&key).await?;
        }

        info!("Force cleaned up key {}", key_id);
        Ok(())
    }

    /// Get total keys managed
    pub async fn get_total_keys(&self) -> usize {
        let storage = self.key_storage.read().await;
        storage.len()
    }

    /// Get expired keys count
    pub async fn get_expired_count(&self) -> usize {
        let now = Utc::now();
        let storage = self.key_storage.read().await;
        storage.values().filter(|k| k.expires_at <= now).count()
    }

    /// Get active keys count
    pub async fn get_active_count(&self) -> usize {
        let now = Utc::now();
        let storage = self.key_storage.read().await;
        storage.values().filter(|k| k.expires_at > now).count()
    }
}

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

    fn create_test_key(id: &str, expires_in_seconds: i64) -> TemporalSshKey {
        let now = Utc::now();
        TemporalSshKey {
            id: id.to_string(),
            key_type: SshKeyType::DynamicKeyPair,
            public_key: "ssh-ed25519 AAAA... test@example.com".to_string(),
            private_key: None,
            fingerprint: "SHA256:test".to_string(),
            user: "root".to_string(),
            target_server: "test.example.com".to_string(),
            created_at: now,
            expires_at: now + Duration::seconds(expires_in_seconds),
            ttl: Duration::seconds(expires_in_seconds),
            used: false,
            deployed: false,
            vault_lease_id: None,
        }
    }

    #[tokio::test]
    async fn test_temporal_manager_creation() {
        let storage = Arc::new(RwLock::new(HashMap::new()));
        let deployer = Arc::new(
            KeyDeployer::new("http://localhost:8080").expect("Failed to create KeyDeployer"),
        );
        let manager = TemporalManager::new(storage, deployer, Duration::minutes(5));

        assert_eq!(manager.cleanup_interval, Duration::minutes(5));
    }

    #[tokio::test]
    async fn test_get_expiring_keys() {
        let storage = Arc::new(RwLock::new(HashMap::new()));
        let deployer = Arc::new(
            KeyDeployer::new("http://localhost:8080").expect("Failed to create KeyDeployer"),
        );
        let manager = TemporalManager::new(Arc::clone(&storage), deployer, Duration::minutes(5));

        // Add test keys
        {
            let mut store = storage.write().await;
            store.insert("key1".to_string(), create_test_key("key1", 60)); // Expires in 1 min
            store.insert("key2".to_string(), create_test_key("key2", 300)); // Expires in 5 min
            store.insert("key3".to_string(), create_test_key("key3", 7200)); // Expires in 2 hours
        }

        // Get keys expiring within 10 minutes
        let expiring = manager
            .get_expiring_keys(Duration::minutes(10))
            .await
            .unwrap();

        assert_eq!(expiring.len(), 2); // key1 and key2
    }

    #[tokio::test]
    async fn test_get_active_and_expired_counts() {
        let storage = Arc::new(RwLock::new(HashMap::new()));
        let deployer = Arc::new(
            KeyDeployer::new("http://localhost:8080").expect("Failed to create KeyDeployer"),
        );
        let manager = TemporalManager::new(Arc::clone(&storage), deployer, Duration::minutes(5));

        // Add test keys
        {
            let mut store = storage.write().await;
            store.insert("key1".to_string(), create_test_key("key1", 3600)); // Active
            store.insert("key2".to_string(), create_test_key("key2", -60)); // Expired
            store.insert("key3".to_string(), create_test_key("key3", 7200)); // Active
        }

        let active = manager.get_active_count().await;
        let expired = manager.get_expired_count().await;

        assert_eq!(active, 2);
        assert_eq!(expired, 1);
    }

    #[tokio::test]
    async fn test_cleanup_expired_keys() {
        let storage = Arc::new(RwLock::new(HashMap::new()));
        let deployer = Arc::new(
            KeyDeployer::new("http://localhost:8080").expect("Failed to create KeyDeployer"),
        );
        let manager = TemporalManager::new(Arc::clone(&storage), deployer, Duration::minutes(5));

        // Add expired key (not deployed, so cleanup won't fail)
        {
            let mut store = storage.write().await;
            store.insert("expired".to_string(), create_test_key("expired", -60));
            store.insert("active".to_string(), create_test_key("active", 3600));
        }

        let cleaned = manager.cleanup_expired_keys().await.unwrap();

        assert_eq!(cleaned.len(), 1);
        assert_eq!(cleaned[0], "expired");

        // Verify storage
        let remaining = manager.get_total_keys().await;
        assert_eq!(remaining, 1);
    }
}
