use std::path::PathBuf;
use std::sync::Arc;
use std::time::Instant;

use tokio::sync::RwLock;
use tracing::info;

use crate::age::AgeClient;
use crate::cosmian::CosmianClient;
use crate::rustyvault::RustyVaultClient;
use crate::secretumvault::SecretumVaultClient;
use crate::types::{DataKey, EncryptionContext, KeySpec, KmsBackendConfig, KmsError, Result};

/// KMS backend enum supporting Age (dev), Cosmian (prod), RustyVault
/// (self-hosted), and SecretumVault (enterprise)
#[derive(Clone)]
pub enum KmsBackend {
    Age(AgeClient),
    Cosmian(CosmianClient),
    RustyVault(RustyVaultClient),
    SecretumVault(SecretumVaultClient),
}

/// Unified KMS service
pub struct KmsService {
    backend: KmsBackend,
    operations_count: Arc<RwLock<u64>>,
    start_time: Instant,
}

impl KmsService {
    /// Create a new KMS service from configuration
    pub async fn new(config: KmsBackendConfig) -> Result<Self> {
        let backend = match config {
            KmsBackendConfig::Age {
                public_key_path,
                private_key_path,
            } => {
                info!("Initializing Age KMS backend (development)");
                let client = AgeClient::new(
                    PathBuf::from(public_key_path),
                    PathBuf::from(private_key_path),
                )?;
                KmsBackend::Age(client)
            }
            KmsBackendConfig::Cosmian {
                server_url,
                api_key,
                default_key_id,
                tls_verify,
            } => {
                info!(
                    "Initializing Cosmian KMS backend (production) at {}",
                    server_url
                );
                let client = CosmianClient::new(server_url, api_key, default_key_id, tls_verify)?;
                KmsBackend::Cosmian(client)
            }
            KmsBackendConfig::Rustyvault {
                server_url,
                token,
                mount_point,
                key_name,
                tls_verify,
            } => {
                info!(
                    "Initializing RustyVault KMS backend (self-hosted) at {}",
                    server_url
                );
                let client =
                    RustyVaultClient::new(server_url, token, mount_point, key_name, tls_verify)?;
                KmsBackend::RustyVault(client)
            }
            KmsBackendConfig::Secretumvault {
                server_url,
                storage_backend,
                auth_token,
                mount_point,
                key_name,
                tls_verify,
            } => {
                info!("Initializing SecretumVault KMS backend at {}", server_url);
                let config = crate::secretumvault::SecretumVaultConfig {
                    server_url,
                    storage_backend,
                    auth_token,
                    mount_point,
                    key_name,
                    tls_verify,
                    deployment_mode: Default::default(),
                };
                let client = SecretumVaultClient::new(config).await?;
                KmsBackend::SecretumVault(client)
            }
        };

        Ok(Self {
            backend,
            operations_count: Arc::new(RwLock::new(0)),
            start_time: Instant::now(),
        })
    }

    /// Increment operations counter
    async fn increment_operations(&self) {
        let mut count = self.operations_count.write().await;
        *count += 1;
    }

    /// Get operations count
    pub async fn get_operations_count(&self) -> u64 {
        *self.operations_count.read().await
    }

    /// Get uptime in seconds
    pub fn get_uptime(&self) -> u64 {
        self.start_time.elapsed().as_secs()
    }

    /// Get backend name
    pub fn get_backend_name(&self) -> String {
        match &self.backend {
            KmsBackend::Age(_) => "age".to_string(),
            KmsBackend::Cosmian(_) => "cosmian".to_string(),
            KmsBackend::RustyVault(_) => "rustyvault".to_string(),
            KmsBackend::SecretumVault(_) => "secretumvault".to_string(),
        }
    }

    /// Encrypt data using the configured backend
    pub async fn encrypt(&self, plaintext: &[u8], context: &EncryptionContext) -> Result<Vec<u8>> {
        self.increment_operations().await;

        match &self.backend {
            KmsBackend::Age(client) => client.encrypt(plaintext, context),
            KmsBackend::Cosmian(client) => client.encrypt(plaintext, context).await,
            KmsBackend::RustyVault(client) => client.encrypt(plaintext, context).await,
            KmsBackend::SecretumVault(client) => client.encrypt(plaintext, context).await,
        }
    }

    /// Decrypt data using the configured backend
    pub async fn decrypt(&self, ciphertext: &[u8], context: &EncryptionContext) -> Result<Vec<u8>> {
        self.increment_operations().await;

        match &self.backend {
            KmsBackend::Age(client) => client.decrypt(ciphertext, context),
            KmsBackend::Cosmian(client) => client.decrypt(ciphertext, context).await,
            KmsBackend::RustyVault(client) => client.decrypt(ciphertext, context).await,
            KmsBackend::SecretumVault(client) => client.decrypt(ciphertext, context).await,
        }
    }

    /// Generate a data key for envelope encryption
    /// Note: Supported with Cosmian, RustyVault, and SecretumVault backends
    pub async fn generate_data_key(&self, key_spec: &KeySpec) -> Result<DataKey> {
        self.increment_operations().await;

        match &self.backend {
            KmsBackend::Age(_) => Err(KmsError::ConfigurationError(
                "Data key generation not supported with Age backend (use for development only)"
                    .to_string(),
            )),
            KmsBackend::Cosmian(client) => client.generate_data_key(key_spec).await,
            KmsBackend::RustyVault(client) => client.generate_data_key(key_spec).await,
            KmsBackend::SecretumVault(client) => client.generate_data_key(key_spec).await,
        }
    }

    /// Rotate the encryption key
    /// Note: Age doesn't support key rotation (manual key replacement required)
    /// Cosmian, RustyVault, and SecretumVault handle key rotation server-side
    pub async fn rotate_key(&self, _key_id: &str) -> Result<String> {
        self.increment_operations().await;

        match &self.backend {
            KmsBackend::Age(_) => Err(KmsError::ConfigurationError(
                "Key rotation not supported with Age backend (generate new keys manually)"
                    .to_string(),
            )),
            KmsBackend::Cosmian(_) => Err(KmsError::ConfigurationError(
                "Key rotation handled server-side by Cosmian KMS (configure rotation policy in \
                 KMS)"
                    .to_string(),
            )),
            KmsBackend::RustyVault(_) => Err(KmsError::ConfigurationError(
                "Key rotation handled server-side by RustyVault (use: rustyvault write -f \
                 transit/keys/<key>/rotate)"
                    .to_string(),
            )),
            KmsBackend::SecretumVault(_) => Err(KmsError::ConfigurationError(
                "Key rotation handled server-side by SecretumVault (configure rotation policy in \
                 KMS)"
                    .to_string(),
            )),
        }
    }

    /// Health check for the KMS backend
    pub async fn health_check(&self) -> Result<bool> {
        match &self.backend {
            KmsBackend::Age(client) => client.health_check(),
            KmsBackend::Cosmian(client) => client.health_check().await,
            KmsBackend::RustyVault(client) => client.health_check().await,
            KmsBackend::SecretumVault(client) => client.health_check().await,
        }
    }

    /// Get backend version/info
    pub async fn get_version(&self) -> Result<String> {
        match &self.backend {
            KmsBackend::Age(client) => Ok(client.get_version()),
            KmsBackend::Cosmian(client) => client.get_version().await,
            KmsBackend::RustyVault(client) => client.get_version().await,
            KmsBackend::SecretumVault(client) => client.get_version().await,
        }
    }

    /// Get the backend instance for advanced operations
    pub fn get_backend(&self) -> &KmsBackend {
        &self.backend
    }
}

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

    #[test]
    fn test_backend_name_logic() {
        // Test backend name logic (actual initialization requires real configs)
        let age_name = "age";
        let cosmian_name = "cosmian";

        assert_eq!(age_name, "age");
        assert_eq!(cosmian_name, "cosmian");
    }

    #[test]
    fn test_encryption_context() {
        let mut ctx = EncryptionContext::new();
        ctx.add("environment", "production");
        ctx.add("service", "api-gateway");

        assert_eq!(ctx.context.len(), 2);
        assert_eq!(ctx.context.get("environment").unwrap(), "production");
    }
}
