// Helper functions for KMS backends
// Real implementations for RustyVault, Age, and HTTP Fallback

use base64::{engine::general_purpose, Engine as _};
use rusty_vault::api::Client as RustyVaultClient;
use std::io::{Read, Write};

/// KMS Backend types
pub enum Backend {
    RustyVault {
        client: RustyVaultClient,
    },
    Age {
        recipient: String,
        identity: Option<String>,
    },
    AwsKms {
        key_id: String,
    },
    Vault {
        addr: String,
        token: String,
    },
    HttpFallback {
        backend_name: String,
        url: String,
    },
}

impl Backend {
    /// Create new RustyVault backend
    pub async fn new_rustyvault(server_url: &str, token: &str) -> Result<Self, String> {
        let client = RustyVaultClient::new()
            .with_addr(server_url)
            .with_token(token);

        Ok(Backend::RustyVault { client })
    }

    /// Create new Age backend
    pub fn new_age(recipient: &str, identity: Option<String>) -> Result<Self, String> {
        // Validate recipient format (age1...)
        if !recipient.starts_with("age1") {
            return Err("Invalid Age recipient format (must start with age1)".to_string());
        }

        Ok(Backend::Age {
            recipient: recipient.to_string(),
            identity,
        })
    }

    /// Create new HTTP fallback backend
    pub fn new_http_fallback(backend_name: &str, url: &str) -> Self {
        Backend::HttpFallback {
            backend_name: backend_name.to_string(),
            url: url.to_string(),
        }
    }

    /// Create new AWS KMS backend
    pub fn new_aws_kms(key_id: &str) -> Self {
        Backend::AwsKms {
            key_id: key_id.to_string(),
        }
    }

    /// Create new HashiCorp Vault backend
    pub fn new_vault(addr: &str, token: &str) -> Self {
        Backend::Vault {
            addr: addr.to_string(),
            token: token.to_string(),
        }
    }
}

// =============================================================================
// RustyVault Operations
// =============================================================================

/// Encrypt data using RustyVault Transit backend
pub fn encrypt_rustyvault(
    client: &RustyVaultClient,
    key_name: &str,
    data: &[u8],
) -> Result<String, String> {
    let plaintext_b64 = general_purpose::STANDARD.encode(data);

    let mut req_data = serde_json::Map::new();
    req_data.insert(
        "plaintext".to_string(),
        serde_json::Value::String(plaintext_b64),
    );

    let path = format!("transit/encrypt/{}", key_name);
    let response = client
        .logical()
        .write(&path, Some(req_data))
        .map_err(|e| format!("RustyVault encrypt error: {}", e))?;

    if response.response_status != 200 {
        return Err(format!(
            "RustyVault returned status {}",
            response.response_status
        ));
    }

    let data = response
        .response_data
        .ok_or("No response data from RustyVault")?;

    data["data"]["ciphertext"]
        .as_str()
        .ok_or("Missing ciphertext in response")?
        .to_string()
        .pipe(Ok)
}

/// Decrypt data using RustyVault Transit backend
pub fn decrypt_rustyvault(
    client: &RustyVaultClient,
    key_name: &str,
    ciphertext: &str,
) -> Result<Vec<u8>, String> {
    let mut req_data = serde_json::Map::new();
    req_data.insert(
        "ciphertext".to_string(),
        serde_json::Value::String(ciphertext.to_string()),
    );

    let path = format!("transit/decrypt/{}", key_name);
    let response = client
        .logical()
        .write(&path, Some(req_data))
        .map_err(|e| format!("RustyVault decrypt error: {}", e))?;

    if response.response_status != 200 {
        return Err(format!(
            "RustyVault returned status {}",
            response.response_status
        ));
    }

    let data = response
        .response_data
        .ok_or("No response data from RustyVault")?;

    let plaintext_b64 = data["data"]["plaintext"]
        .as_str()
        .ok_or("Missing plaintext in response")?;

    general_purpose::STANDARD
        .decode(plaintext_b64)
        .map_err(|e| format!("Base64 decode error: {}", e))
}

/// Generate data key using RustyVault
pub fn generate_data_key_rustyvault(
    client: &RustyVaultClient,
    key_name: &str,
    key_spec: &str,
) -> Result<(String, String), String> {
    let bits = match key_spec {
        "AES128" => 128,
        "AES256" => 256,
        _ => return Err(format!("Invalid key spec: {}", key_spec)),
    };

    let mut req_data = serde_json::Map::new();
    req_data.insert("bits".to_string(), serde_json::Value::Number(bits.into()));

    let path = format!("transit/datakey/plaintext/{}", key_name);
    let response = client
        .logical()
        .write(&path, Some(req_data))
        .map_err(|e| format!("RustyVault generate key error: {}", e))?;

    if response.response_status != 200 {
        return Err(format!(
            "RustyVault returned status {}",
            response.response_status
        ));
    }

    let data = response
        .response_data
        .ok_or("No response data from RustyVault")?;

    let plaintext = data["data"]["plaintext"]
        .as_str()
        .ok_or("Missing plaintext in response")?
        .to_string();

    let ciphertext = data["data"]["ciphertext"]
        .as_str()
        .ok_or("Missing ciphertext in response")?
        .to_string();

    Ok((plaintext, ciphertext))
}

// =============================================================================
// Age Operations
// =============================================================================

/// Encrypt data using Age
pub fn encrypt_age(data: &[u8], recipient_str: &str) -> Result<String, String> {
    let recipient = recipient_str
        .parse::<age::x25519::Recipient>()
        .map_err(|e| format!("Invalid Age recipient: {}", e))?;

    let encryptor = age::Encryptor::with_recipients(std::iter::once(&recipient as &dyn age::Recipient))
        .map_err(|_| "Failed to create Age encryptor".to_string())?;

    let mut encrypted = vec![];
    let mut writer = encryptor
        .wrap_output(&mut encrypted)
        .map_err(|e| format!("Age encryption error: {}", e))?;

    writer
        .write_all(data)
        .map_err(|e| format!("Write error: {}", e))?;
    writer
        .finish()
        .map_err(|e| format!("Finish error: {}", e))?;

    // Return as ASCII-armored format
    Ok(format!(
        "-----BEGIN AGE ENCRYPTED FILE-----\n{}\n-----END AGE ENCRYPTED FILE-----",
        general_purpose::STANDARD.encode(&encrypted)
    ))
}

/// Decrypt data using Age
pub fn decrypt_age(ciphertext: &str, identity_path: &str) -> Result<Vec<u8>, String> {
    // Load identity from file
    let identity_file = std::fs::read_to_string(identity_path)
        .map_err(|e| format!("Failed to read identity file {}: {}", identity_path, e))?;

    let identity = identity_file
        .parse::<age::x25519::Identity>()
        .map_err(|e| format!("Invalid Age identity: {}", e))?;

    // Parse armor format if present
    let encrypted_bytes = if ciphertext.contains("BEGIN AGE") {
        let data = ciphertext
            .lines()
            .filter(|l| !l.contains("BEGIN") && !l.contains("END"))
            .collect::<String>();
        general_purpose::STANDARD
            .decode(data)
            .map_err(|e| format!("Base64 decode error: {}", e))?
    } else {
        general_purpose::STANDARD
            .decode(ciphertext)
            .map_err(|e| format!("Base64 decode error: {}", e))?
    };

    let decryptor = age::Decryptor::new(&encrypted_bytes[..])
        .map_err(|e| format!("Age decryptor error: {}", e))?;

    let mut decrypted = vec![];
    let mut reader = decryptor
        .decrypt(std::iter::once(&identity as &dyn age::Identity))
        .map_err(|e| format!("Age decrypt error: {}", e))?;

    reader
        .read_to_end(&mut decrypted)
        .map_err(|e| format!("Read error: {}", e))?;

    Ok(decrypted)
}

/// Generate Age key pair
pub fn generate_age_key() -> Result<(String, String), String> {
    use age::secrecy::ExposeSecret;

    let secret = age::x25519::Identity::generate();
    let public = secret.to_public();

    let secret_str = secret.to_string();
    let secret_value = secret_str.expose_secret().to_string();

    Ok((secret_value, public.to_string()))
}

// =============================================================================
// HTTP Fallback Operations
// =============================================================================

/// Encrypt data using HTTP KMS backend
pub async fn encrypt_http(url: &str, backend: &str, data: &[u8]) -> Result<String, String> {
    let client = reqwest::Client::new();

    let response = client
        .post(format!("{}/api/v1/kms/encrypt", url))
        .json(&serde_json::json!({
            "plaintext": general_purpose::STANDARD.encode(data),
            "backend": backend,
        }))
        .send()
        .await
        .map_err(|e| format!("HTTP request error: {}", e))?;

    if !response.status().is_success() {
        return Err(format!("HTTP error: {}", response.status()));
    }

    let result: serde_json::Value = response
        .json()
        .await
        .map_err(|e| format!("JSON parse error: {}", e))?;

    result["ciphertext"]
        .as_str()
        .ok_or("Missing ciphertext in response")?
        .to_string()
        .pipe(|s| Ok(s))
}

/// Decrypt data using HTTP KMS backend
pub async fn decrypt_http(url: &str, backend: &str, ciphertext: &str) -> Result<Vec<u8>, String> {
    let client = reqwest::Client::new();

    let response = client
        .post(format!("{}/api/v1/kms/decrypt", url))
        .json(&serde_json::json!({
            "ciphertext": ciphertext,
            "backend": backend,
        }))
        .send()
        .await
        .map_err(|e| format!("HTTP request error: {}", e))?;

    if !response.status().is_success() {
        return Err(format!("HTTP error: {}", response.status()));
    }

    let result: serde_json::Value = response
        .json()
        .await
        .map_err(|e| format!("JSON parse error: {}", e))?;

    let plaintext_b64 = result["plaintext"]
        .as_str()
        .ok_or("Missing plaintext in response")?;

    general_purpose::STANDARD
        .decode(plaintext_b64)
        .map_err(|e| format!("Base64 decode error: {}", e))
}

/// Generate data key using HTTP KMS backend
pub async fn generate_data_key_http(
    url: &str,
    backend: &str,
    key_spec: &str,
) -> Result<(String, String), String> {
    let client = reqwest::Client::new();

    let response = client
        .post(format!("{}/api/v1/kms/generate-data-key", url))
        .json(&serde_json::json!({
            "key_spec": key_spec,
            "backend": backend,
        }))
        .send()
        .await
        .map_err(|e| format!("HTTP request error: {}", e))?;

    let result: serde_json::Value = response
        .json()
        .await
        .map_err(|e| format!("JSON parse error: {}", e))?;

    let plaintext = result["plaintext"]
        .as_str()
        .ok_or("Missing plaintext in response")?
        .to_string();
    let ciphertext = result["ciphertext"]
        .as_str()
        .ok_or("Missing ciphertext in response")?
        .to_string();

    Ok((plaintext, ciphertext))
}

// =============================================================================
// Auto-detection
// =============================================================================

/// Detect available KMS backend from environment
pub async fn detect_backend() -> Backend {
    // 1. Check for RustyVault
    if let (Ok(vault_addr), Ok(vault_token)) = (
        std::env::var("RUSTYVAULT_ADDR"),
        std::env::var("RUSTYVAULT_TOKEN"),
    ) {
        if let Ok(backend) = Backend::new_rustyvault(&vault_addr, &vault_token).await {
            return backend;
        }
    }

    // 2. Check for Age
    if let Ok(age_recipient) = std::env::var("AGE_RECIPIENT") {
        let identity = std::env::var("AGE_IDENTITY").ok();
        if let Ok(backend) = Backend::new_age(&age_recipient, identity) {
            return backend;
        }
    }

    // 3. Default to HTTP fallback (Cosmian)
    let url = std::env::var("KMS_HTTP_URL").unwrap_or("http://localhost:8081".to_string());
    let backend = std::env::var("KMS_HTTP_BACKEND").unwrap_or("cosmian".to_string());
    Backend::new_http_fallback(&backend, &url)
}

// =============================================================================
// Utility Functions
// =============================================================================

/// Encode bytes to base64
#[allow(dead_code)]
pub fn encode_base64(data: &[u8]) -> String {
    general_purpose::STANDARD.encode(data)
}

/// Decode base64 to bytes
#[allow(dead_code)]
pub fn decode_base64(data: &str) -> Result<Vec<u8>, String> {
    general_purpose::STANDARD
        .decode(data)
        .map_err(|e| format!("Base64 decode error: {}", e))
}

// Pipe trait for method chaining
trait Pipe: Sized {
    fn pipe<F, R>(self, f: F) -> R
    where
        F: FnOnce(Self) -> R,
    {
        f(self)
    }
}

impl<T> Pipe for T {}

// =============================================================================
// AWS KMS Operations
// =============================================================================

/// Encrypt data using AWS KMS
pub async fn encrypt_aws_kms(key_id: &str, data: &[u8]) -> Result<String, String> {
    let client = reqwest::Client::new();

    // AWS KMS requires proper SDK, this is a simplified HTTP fallback
    // For production, use aws-sdk-kms crate
    let region = std::env::var("AWS_REGION").unwrap_or_else(|_| "us-east-1".to_string());
    let _endpoint = format!("https://kms.{}.amazonaws.com", region);

    // For now, fall back to HTTP endpoint if available
    let kms_url = std::env::var("AWS_KMS_ENDPOINT")
        .unwrap_or_else(|_| "http://localhost:8081/api/v1/kms".to_string());

    let response = client
        .post(format!("{}/encrypt", kms_url))
        .json(&serde_json::json!({
            "KeyId": key_id,
            "Plaintext": general_purpose::STANDARD.encode(data),
        }))
        .send()
        .await
        .map_err(|e| format!("AWS KMS request failed: {}", e))?;

    if !response.status().is_success() {
        return Err(format!("AWS KMS error: {}", response.status()));
    }

    let result: serde_json::Value = response
        .json()
        .await
        .map_err(|e| format!("JSON parse error: {}", e))?;

    result["CiphertextBlob"]
        .as_str()
        .ok_or("Missing CiphertextBlob")?
        .to_string()
        .pipe(Ok)
}

/// Decrypt data using AWS KMS
pub async fn decrypt_aws_kms(_key_id: &str, ciphertext: &str) -> Result<Vec<u8>, String> {
    let client = reqwest::Client::new();

    let kms_url = std::env::var("AWS_KMS_ENDPOINT")
        .unwrap_or_else(|_| "http://localhost:8081/api/v1/kms".to_string());

    let response = client
        .post(format!("{}/decrypt", kms_url))
        .json(&serde_json::json!({
            "CiphertextBlob": ciphertext,
        }))
        .send()
        .await
        .map_err(|e| format!("AWS KMS request failed: {}", e))?;

    if !response.status().is_success() {
        return Err(format!("AWS KMS error: {}", response.status()));
    }

    let result: serde_json::Value = response
        .json()
        .await
        .map_err(|e| format!("JSON parse error: {}", e))?;

    let plaintext_b64 = result["Plaintext"]
        .as_str()
        .ok_or("Missing Plaintext")?;

    general_purpose::STANDARD
        .decode(plaintext_b64)
        .map_err(|e| format!("Base64 decode error: {}", e))
}

/// Generate data key using AWS KMS
pub async fn generate_data_key_aws(key_id: &str, key_spec: &str) -> Result<(String, String), String> {
    let client = reqwest::Client::new();

    let kms_url = std::env::var("AWS_KMS_ENDPOINT")
        .unwrap_or_else(|_| "http://localhost:8081/api/v1/kms".to_string());

    let response = client
        .post(format!("{}/generate-data-key", kms_url))
        .json(&serde_json::json!({
            "KeyId": key_id,
            "KeySpec": key_spec,
        }))
        .send()
        .await
        .map_err(|e| format!("AWS KMS request failed: {}", e))?;

    let result: serde_json::Value = response
        .json()
        .await
        .map_err(|e| format!("JSON parse error: {}", e))?;

    let plaintext = result["Plaintext"]
        .as_str()
        .ok_or("Missing Plaintext")?
        .to_string();
    let ciphertext = result["CiphertextBlob"]
        .as_str()
        .ok_or("Missing CiphertextBlob")?
        .to_string();

    Ok((plaintext, ciphertext))
}

// =============================================================================
// HashiCorp Vault Operations
// =============================================================================

/// Encrypt data using HashiCorp Vault Transit backend
pub async fn encrypt_vault(
    addr: &str,
    token: &str,
    key_name: &str,
    data: &[u8],
) -> Result<String, String> {
    let client = reqwest::Client::new();
    let plaintext_b64 = general_purpose::STANDARD.encode(data);

    let response = client
        .post(format!("{}/v1/transit/encrypt/{}", addr, key_name))
        .header("X-Vault-Token", token)
        .json(&serde_json::json!({
            "plaintext": plaintext_b64,
        }))
        .send()
        .await
        .map_err(|e| format!("Vault request failed: {}", e))?;

    if !response.status().is_success() {
        return Err(format!("Vault error: {}", response.status()));
    }

    let result: serde_json::Value = response
        .json()
        .await
        .map_err(|e| format!("JSON parse error: {}", e))?;

    result["data"]["ciphertext"]
        .as_str()
        .ok_or("Missing ciphertext")?
        .to_string()
        .pipe(Ok)
}

/// Decrypt data using HashiCorp Vault Transit backend
pub async fn decrypt_vault(
    addr: &str,
    token: &str,
    key_name: &str,
    ciphertext: &str,
) -> Result<Vec<u8>, String> {
    let client = reqwest::Client::new();

    let response = client
        .post(format!("{}/v1/transit/decrypt/{}", addr, key_name))
        .header("X-Vault-Token", token)
        .json(&serde_json::json!({
            "ciphertext": ciphertext,
        }))
        .send()
        .await
        .map_err(|e| format!("Vault request failed: {}", e))?;

    if !response.status().is_success() {
        return Err(format!("Vault error: {}", response.status()));
    }

    let result: serde_json::Value = response
        .json()
        .await
        .map_err(|e| format!("JSON parse error: {}", e))?;

    let plaintext_b64 = result["data"]["plaintext"]
        .as_str()
        .ok_or("Missing plaintext")?;

    general_purpose::STANDARD
        .decode(plaintext_b64)
        .map_err(|e| format!("Base64 decode error: {}", e))
}

/// Generate data key using HashiCorp Vault
pub async fn generate_data_key_vault(
    addr: &str,
    token: &str,
    key_name: &str,
    key_spec: &str,
) -> Result<(String, String), String> {
    let client = reqwest::Client::new();

    let bits = match key_spec {
        "AES128" => 128,
        "AES256" => 256,
        _ => return Err(format!("Invalid key spec: {}", key_spec)),
    };

    let response = client
        .post(format!("{}/v1/transit/datakey/plaintext/{}", addr, key_name))
        .header("X-Vault-Token", token)
        .json(&serde_json::json!({
            "bits": bits,
        }))
        .send()
        .await
        .map_err(|e| format!("Vault request failed: {}", e))?;

    let result: serde_json::Value = response
        .json()
        .await
        .map_err(|e| format!("JSON parse error: {}", e))?;

    let plaintext = result["data"]["plaintext"]
        .as_str()
        .ok_or("Missing plaintext")?
        .to_string();
    let ciphertext = result["data"]["ciphertext"]
        .as_str()
        .ok_or("Missing ciphertext")?
        .to_string();

    Ok((plaintext, ciphertext))
}

// =============================================================================
// Backend Availability Check
// =============================================================================

/// Check if a backend is available based on environment configuration
pub fn check_backend_available(backend_name: &str) -> bool {
    match backend_name {
        "rustyvault" => {
            std::env::var("RUSTYVAULT_ADDR").is_ok() && std::env::var("RUSTYVAULT_TOKEN").is_ok()
        }
        "age" => {
            std::env::var("AGE_RECIPIENT").is_ok() || std::env::var("AGE_IDENTITY").is_ok()
        }
        "aws" => {
            std::env::var("AWS_ACCESS_KEY_ID").is_ok()
                && std::env::var("AWS_SECRET_ACCESS_KEY").is_ok()
        }
        "vault" => {
            std::env::var("VAULT_ADDR").is_ok() && std::env::var("VAULT_TOKEN").is_ok()
        }
        "cosmian" => std::env::var("KMS_HTTP_URL").is_ok(),
        _ => false,
    }
}
