use std::fmt;
use std::str::FromStr;

use serde::{Deserialize, Serialize};
use thiserror::Error;

/// KMS service errors
#[derive(Error, Debug)]
pub enum KmsError {
    #[error("Age encryption error: {0}")]
    AgeError(String),

    #[error("Cosmian KMS error: {0}")]
    CosmianError(String),

    #[error("RustyVault error: {0}")]
    RustyVaultError(String),

    #[error("SecretumVault error: {0}")]
    SecretumVaultError(String),

    #[error("Encryption error: {0}")]
    EncryptionError(String),

    #[error("Decryption error: {0}")]
    DecryptionError(String),

    #[error("Key not found: {0}")]
    KeyNotFound(String),

    #[error("Authentication failed: {0}")]
    AuthenticationFailed(String),

    #[error("Configuration error: {0}")]
    ConfigurationError(String),

    #[error("HTTP error: {0}")]
    HttpError(#[from] reqwest::Error),

    #[error("JSON error: {0}")]
    JsonError(#[from] serde_json::Error),

    #[error("Base64 error: {0}")]
    Base64Error(#[from] base64::DecodeError),

    #[error("IO error: {0}")]
    IoError(#[from] std::io::Error),

    #[error("Invalid backend: {0}")]
    InvalidBackend(String),
}

pub type Result<T> = std::result::Result<T, KmsError>;

/// Key specification for data key generation
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum KeySpec {
    Aes128,
    Aes256,
    Rsa2048,
    Rsa4096,
}

impl KeySpec {
    pub fn key_size(&self) -> usize {
        match self {
            KeySpec::Aes128 => 16,
            KeySpec::Aes256 => 32,
            KeySpec::Rsa2048 => 256,
            KeySpec::Rsa4096 => 512,
        }
    }
}

/// Generated data key with plaintext and encrypted versions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataKey {
    /// Plaintext data key (use and discard immediately)
    pub plaintext: Vec<u8>,
    /// Encrypted data key (store persistently)
    pub ciphertext: Vec<u8>,
    /// Key ID used for encryption
    pub key_id: String,
}

/// Encryption context for additional authenticated data (AAD)
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EncryptionContext {
    pub context: std::collections::HashMap<String, String>,
}

impl EncryptionContext {
    pub fn new() -> Self {
        Self {
            context: std::collections::HashMap::new(),
        }
    }

    pub fn add(&mut self, key: impl Into<String>, value: impl Into<String>) {
        self.context.insert(key.into(), value.into());
    }
}

impl FromStr for EncryptionContext {
    type Err = KmsError;

    fn from_str(context_str: &str) -> std::result::Result<Self, Self::Err> {
        let mut ctx = Self::new();
        for pair in context_str.split(',') {
            let parts: Vec<&str> = pair.split('=').collect();
            if parts.len() == 2 {
                ctx.add(parts[0].trim(), parts[1].trim());
            }
        }
        Ok(ctx)
    }
}

impl fmt::Display for EncryptionContext {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let s = self
            .context
            .iter()
            .map(|(k, v)| format!("{}={}", k, v))
            .collect::<Vec<_>>()
            .join(",");
        write!(f, "{}", s)
    }
}

/// KMS backend configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum KmsBackendConfig {
    Age {
        public_key_path: String,
        private_key_path: String,
    },
    Cosmian {
        server_url: String,
        api_key: String,
        default_key_id: String,
        tls_verify: bool,
    },
    Rustyvault {
        server_url: String,
        token: Option<String>,
        mount_point: String,
        key_name: String,
        tls_verify: bool,
    },
    Secretumvault {
        server_url: String,
        storage_backend: String,
        auth_token: Option<String>,
        mount_point: String,
        key_name: String,
        tls_verify: bool,
    },
}

/// KMS operation request
#[derive(Debug, Serialize, Deserialize)]
pub struct EncryptRequest {
    /// Data to encrypt (base64 encoded)
    pub plaintext: String,
    /// Optional encryption context
    pub context: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct EncryptResponse {
    /// Encrypted data (base64 encoded)
    pub ciphertext: String,
    /// Key ID used
    pub key_id: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct DecryptRequest {
    /// Data to decrypt (base64 encoded)
    pub ciphertext: String,
    /// Optional encryption context
    pub context: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct DecryptResponse {
    /// Decrypted data (base64 encoded)
    pub plaintext: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct GenerateKeyRequest {
    /// Key specification
    pub key_spec: KeySpec,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct GenerateKeyResponse {
    /// Plaintext key (base64 encoded)
    pub plaintext: String,
    /// Encrypted key (base64 encoded)
    pub ciphertext: String,
    /// Key ID used
    pub key_id: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct RotateKeyRequest {
    /// Key ID to rotate
    pub key_id: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct RotateKeyResponse {
    /// New key ID
    pub new_key_id: String,
    /// Success status
    pub success: bool,
}

/// KMS service status
#[derive(Debug, Serialize, Deserialize)]
pub struct KmsStatus {
    pub backend: String,
    pub healthy: bool,
    pub version: String,
    pub uptime: String,
    pub operations_count: u64,
}

/// Health check response
#[derive(Debug, Serialize, Deserialize)]
pub struct HealthResponse {
    pub status: String,
    pub backend: String,
    pub timestamp: String,
}
