use std::sync::Arc;

use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use tracing::{debug, error, info};

use super::config::{DeploymentMode, SecretumVaultConfig};
/// SecretumVault KMS client with support for both embedded and service modes
/// Uses HTTP-based communication (valid for both embedded library mode and
/// standalone service)
use crate::types::{DataKey, EncryptionContext, KeySpec, KmsError, Result};

/// SecretumVault KMS client supporting both embedded and service modes
#[derive(Clone)]
pub struct SecretumVaultClient {
    config: SecretumVaultConfig,
    http_client: Arc<Client>,
}

#[derive(Debug, Serialize, Deserialize)]
struct TransitEncryptRequest {
    plaintext: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct TransitEncryptResponse {
    request_id: String,
    lease_id: String,
    renewable: bool,
    lease_duration: i32,
    data: EncryptedData,
    warnings: Option<Vec<String>>,
    auth: Option<serde_json::Value>,
}

#[derive(Debug, Serialize, Deserialize)]
struct EncryptedData {
    ciphertext: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct TransitDecryptRequest {
    ciphertext: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct DecryptedData {
    plaintext: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct TransitDecryptResponse {
    request_id: String,
    lease_id: String,
    renewable: bool,
    lease_duration: i32,
    data: DecryptedData,
    warnings: Option<Vec<String>>,
    auth: Option<serde_json::Value>,
}

#[derive(Debug, Serialize, Deserialize)]
struct TransitDataKeyRequest {
    bits: u32,
}

#[derive(Debug, Serialize, Deserialize)]
struct DataKeyResponse {
    request_id: String,
    lease_id: String,
    renewable: bool,
    lease_duration: i32,
    data: GeneratedKeyData,
    warnings: Option<Vec<String>>,
    auth: Option<serde_json::Value>,
}

#[derive(Debug, Serialize, Deserialize)]
struct GeneratedKeyData {
    plaintext: String,
    ciphertext: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct HealthResponse {
    initialized: bool,
    sealed: bool,
    standby: bool,
    performance_standby: bool,
    replication_performance_mode: String,
    replication_dr_mode: String,
    server_time_utc: u64,
    version: String,
}

impl SecretumVaultClient {
    /// Create a new SecretumVault client
    /// Supports both embedded library mode (dev) and standalone service mode
    /// (prod)
    pub async fn new(config: SecretumVaultConfig) -> Result<Self> {
        config.validate().map_err(KmsError::SecretumVaultError)?;

        info!(
            "Initializing SecretumVault KMS backend ({:?} mode) at {}",
            config.deployment_mode, config.server_url
        );

        let http_client = Client::builder()
            .danger_accept_invalid_certs(!config.tls_verify)
            .build()
            .map_err(|e| {
                KmsError::SecretumVaultError(format!("Failed to create HTTP client: {}", e))
            })?;

        Ok(Self {
            config,
            http_client: Arc::new(http_client),
        })
    }

    /// Get deployment mode
    pub fn deployment_mode(&self) -> DeploymentMode {
        self.config.deployment_mode
    }

    /// Encrypt data using Transit engine
    pub async fn encrypt(&self, plaintext: &[u8], _context: &EncryptionContext) -> Result<Vec<u8>> {
        // Encode plaintext to base64 for API
        let plaintext_b64 = BASE64.encode(plaintext);

        let request_url = format!(
            "{}/v1/{}/encrypt/{}",
            self.config.server_url, self.config.mount_point, self.config.key_name
        );

        let request_body = TransitEncryptRequest {
            plaintext: plaintext_b64,
        };

        debug!("Encrypting data with SecretumVault Transit engine");

        let response = self
            .http_client
            .post(&request_url)
            .header(
                "X-Vault-Token",
                self.config.auth_token.clone().unwrap_or_default(),
            )
            .json(&request_body)
            .send()
            .await
            .map_err(|e| {
                KmsError::SecretumVaultError(format!("Encryption request failed: {}", e))
            })?;

        if !response.status().is_success() {
            let status = response.status();
            let body = response.text().await.unwrap_or_default();
            return Err(KmsError::EncryptionError(format!(
                "SecretumVault returned {}: {}",
                status, body
            )));
        }

        let resp: TransitEncryptResponse = response.json().await.map_err(|e| {
            KmsError::SecretumVaultError(format!("Failed to parse response: {}", e))
        })?;

        // Ciphertext is already in vault format: vault:v1:base64data
        // Return as bytes for consistency
        Ok(resp.data.ciphertext.into_bytes())
    }

    /// Decrypt data using Transit engine
    pub async fn decrypt(
        &self,
        ciphertext: &[u8],
        _context: &EncryptionContext,
    ) -> Result<Vec<u8>> {
        // Ciphertext should be in vault format
        let ciphertext_str = String::from_utf8(ciphertext.to_vec()).map_err(|e| {
            KmsError::DecryptionError(format!("Invalid UTF-8 in ciphertext: {}", e))
        })?;

        let request_url = format!(
            "{}/v1/{}/decrypt/{}",
            self.config.server_url, self.config.mount_point, self.config.key_name
        );

        let request_body = TransitDecryptRequest {
            ciphertext: ciphertext_str,
        };

        debug!("Decrypting data with SecretumVault Transit engine");

        let response = self
            .http_client
            .post(&request_url)
            .header(
                "X-Vault-Token",
                self.config.auth_token.clone().unwrap_or_default(),
            )
            .json(&request_body)
            .send()
            .await
            .map_err(|e| {
                KmsError::SecretumVaultError(format!("Decryption request failed: {}", e))
            })?;

        if !response.status().is_success() {
            let status = response.status();
            let body = response.text().await.unwrap_or_default();
            return Err(KmsError::DecryptionError(format!(
                "SecretumVault returned {}: {}",
                status, body
            )));
        }

        let resp: TransitDecryptResponse = response.json().await.map_err(|e| {
            KmsError::SecretumVaultError(format!("Failed to parse response: {}", e))
        })?;

        // Plaintext is base64 encoded, decode it
        BASE64
            .decode(&resp.data.plaintext)
            .map_err(|e| KmsError::DecryptionError(format!("Failed to decode plaintext: {}", e)))
    }

    /// Generate a data key for envelope encryption
    pub async fn generate_data_key(&self, key_spec: &KeySpec) -> Result<DataKey> {
        let bits = match key_spec {
            KeySpec::Aes128 => 128,
            KeySpec::Aes256 => 256,
            KeySpec::Rsa2048 => 2048,
            KeySpec::Rsa4096 => 4096,
        };

        let request_url = format!(
            "{}/v1/{}/datakey/plaintext/{}",
            self.config.server_url, self.config.mount_point, self.config.key_name
        );

        let request_body = TransitDataKeyRequest { bits };

        debug!("Generating {} bit data key with SecretumVault", bits);

        let response = self
            .http_client
            .post(&request_url)
            .header(
                "X-Vault-Token",
                self.config.auth_token.clone().unwrap_or_default(),
            )
            .json(&request_body)
            .send()
            .await
            .map_err(|e| {
                KmsError::SecretumVaultError(format!("Data key generation request failed: {}", e))
            })?;

        if !response.status().is_success() {
            let status = response.status();
            let body = response.text().await.unwrap_or_default();
            return Err(KmsError::SecretumVaultError(format!(
                "SecretumVault returned {}: {}",
                status, body
            )));
        }

        let resp: DataKeyResponse = response.json().await.map_err(|e| {
            KmsError::SecretumVaultError(format!("Failed to parse response: {}", e))
        })?;

        // Decode plaintext from base64
        let plaintext = BASE64.decode(&resp.data.plaintext).map_err(|e| {
            KmsError::SecretumVaultError(format!("Failed to decode plaintext: {}", e))
        })?;

        Ok(DataKey {
            plaintext,
            ciphertext: resp.data.ciphertext.into_bytes(),
            key_id: self.config.key_name.clone(),
        })
    }

    /// Health check for SecretumVault
    pub async fn health_check(&self) -> Result<bool> {
        let health_url = format!("{}/v1/sys/health", self.config.server_url);

        debug!("Checking SecretumVault health");

        match self.http_client.get(&health_url).send().await {
            Ok(response) => {
                let status = response.status();
                // Vault returns 200 for OK, 429 for unsealed, 501 for not initialized, 503 for
                // sealed
                Ok(status.is_success())
            }
            Err(e) => {
                error!("Health check failed: {}", e);
                Err(KmsError::SecretumVaultError(format!(
                    "Health check failed: {}",
                    e
                )))
            }
        }
    }

    /// Get SecretumVault version info
    pub async fn get_version(&self) -> Result<String> {
        let health_url = format!("{}/v1/sys/health", self.config.server_url);

        match self.http_client.get(&health_url).send().await {
            Ok(response) => {
                if let Ok(health) = response.json::<HealthResponse>().await {
                    Ok(health.version)
                } else {
                    Ok("unknown".to_string())
                }
            }
            Err(_e) => Ok("unknown".to_string()),
        }
    }
}

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

    #[tokio::test]
    async fn test_config_validation_on_create() {
        let config = SecretumVaultConfig {
            server_url: "http://localhost:8200".to_string(),
            storage_backend: "invalid".to_string(),
            auth_token: None,
            mount_point: "transit".to_string(),
            key_name: "test-key".to_string(),
            tls_verify: false,
            deployment_mode: DeploymentMode::Service,
        };

        let result = SecretumVaultClient::new(config).await;
        assert!(result.is_err());
    }

    #[test]
    fn test_key_spec_bits() {
        assert_eq!(
            match KeySpec::Aes128 {
                KeySpec::Aes128 => 128,
                KeySpec::Aes256 => 256,
                _ => 0,
            },
            128
        );
    }
}
