use std::io::{Read, Write};
use std::path::PathBuf;

use age::{x25519, Decryptor, Encryptor, Identity, Recipient};
use tracing::{debug, info};

use crate::types::{EncryptionContext, KmsError, Result};

/// Age encryption client for development/local use
#[derive(Clone)]
pub struct AgeClient {
    public_key: x25519::Recipient,
    private_key_path: PathBuf,
}

impl AgeClient {
    /// Create a new Age client
    ///
    /// # Arguments
    /// * `public_key_path` - Path to the Age public key file
    /// * `private_key_path` - Path to the Age private key file
    pub fn new(public_key_path: PathBuf, private_key_path: PathBuf) -> Result<Self> {
        info!(
            "Initializing Age client with public key at {:?}",
            public_key_path
        );

        // Read public key
        let public_key_str = std::fs::read_to_string(&public_key_path)
            .map_err(|e| KmsError::ConfigurationError(format!("Failed to read public key: {}", e)))?
            .trim()
            .to_string();

        // Parse public key
        let public_key: x25519::Recipient = public_key_str.parse().map_err(|e| {
            KmsError::ConfigurationError(format!("Failed to parse public key: {}", e))
        })?;

        // Verify private key exists and is readable
        if !private_key_path.exists() {
            return Err(KmsError::ConfigurationError(format!(
                "Private key file not found: {:?}",
                private_key_path
            )));
        }

        Ok(Self {
            public_key,
            private_key_path,
        })
    }

    /// Encrypt data using Age
    ///
    /// # Arguments
    /// * `plaintext` - Data to encrypt
    /// * `_context` - Encryption context (not used by Age, kept for API
    ///   consistency)
    pub fn encrypt(&self, plaintext: &[u8], _context: &EncryptionContext) -> Result<Vec<u8>> {
        debug!("Encrypting {} bytes with Age", plaintext.len());

        let recipients: Vec<&dyn Recipient> = vec![&self.public_key];
        let encryptor =
            Encryptor::with_recipients(recipients.into_iter()).expect("we provided recipients");

        let mut encrypted = Vec::new();
        let mut writer = encryptor
            .wrap_output(&mut encrypted)
            .map_err(|e| KmsError::EncryptionError(format!("Failed to wrap output: {}", e)))?;

        writer
            .write_all(plaintext)
            .map_err(|e| KmsError::EncryptionError(format!("Failed to write plaintext: {}", e)))?;

        writer.finish().map_err(|e| {
            KmsError::EncryptionError(format!("Failed to finish encryption: {}", e))
        })?;

        debug!("Successfully encrypted to {} bytes", encrypted.len());
        Ok(encrypted)
    }

    /// Decrypt data using Age
    ///
    /// # Arguments
    /// * `ciphertext` - Data to decrypt
    /// * `_context` - Encryption context (not used by Age, kept for API
    ///   consistency)
    pub fn decrypt(&self, ciphertext: &[u8], _context: &EncryptionContext) -> Result<Vec<u8>> {
        debug!("Decrypting {} bytes with Age", ciphertext.len());

        // Read private key
        let private_key_str = std::fs::read_to_string(&self.private_key_path)
            .map_err(|e| KmsError::DecryptionError(format!("Failed to read private key: {}", e)))?;

        // Parse private key as x25519::Identity
        let identity: x25519::Identity = private_key_str.trim().parse().map_err(|e| {
            KmsError::DecryptionError(format!("Failed to parse private key: {}", e))
        })?;

        // Decrypt
        let decryptor = Decryptor::new(ciphertext)
            .map_err(|e| KmsError::DecryptionError(format!("Failed to create decryptor: {}", e)))?;

        let mut decrypted = Vec::new();
        let mut reader = decryptor
            .decrypt(std::iter::once(&identity as &dyn Identity))
            .map_err(|e| KmsError::DecryptionError(format!("Failed to decrypt: {}", e)))?;

        reader.read_to_end(&mut decrypted).map_err(|e| {
            KmsError::DecryptionError(format!("Failed to read decrypted data: {}", e))
        })?;

        debug!("Successfully decrypted to {} bytes", decrypted.len());
        Ok(decrypted)
    }

    /// Health check for Age client (always returns true if keys are accessible)
    pub fn health_check(&self) -> Result<bool> {
        // Verify private key is still readable
        std::fs::read_to_string(&self.private_key_path).map_err(|e| {
            KmsError::ConfigurationError(format!("Private key not accessible: {}", e))
        })?;
        Ok(true)
    }

    /// Get version info
    pub fn get_version(&self) -> String {
        format!("Age encryption v{}", env!("CARGO_PKG_VERSION"))
    }

    /// Get public key fingerprint
    pub fn get_public_key_fingerprint(&self) -> String {
        self.public_key.to_string()
    }
}

#[cfg(test)]
mod tests {
    use std::io::Write;

    use age::secrecy::ExposeSecret;
    use tempfile::TempDir;

    use super::*;

    #[test]
    fn test_age_encrypt_decrypt() {
        // Create temporary directory for keys
        let temp_dir = TempDir::new().unwrap();
        let public_key_path = temp_dir.path().join("public_key.txt");
        let private_key_path = temp_dir.path().join("private_key.txt");

        // Generate a test key pair (in real usage, use age-keygen)
        let secret_key = age::x25519::Identity::generate();
        let public_key = secret_key.to_public();

        // Write keys to files
        std::fs::write(&public_key_path, public_key.to_string()).unwrap();
        std::fs::write(&private_key_path, secret_key.to_string().expose_secret()).unwrap();

        // Create client
        let client = AgeClient::new(public_key_path, private_key_path).unwrap();

        // Test data
        let plaintext = b"Hello, Age encryption!";
        let context = EncryptionContext::new();

        // Encrypt
        let ciphertext = client.encrypt(plaintext, &context).unwrap();
        assert_ne!(ciphertext, plaintext);

        // Decrypt
        let decrypted = client.decrypt(&ciphertext, &context).unwrap();
        assert_eq!(decrypted, plaintext);
    }

    #[test]
    fn test_health_check() {
        let temp_dir = TempDir::new().unwrap();
        let public_key_path = temp_dir.path().join("public_key.txt");
        let private_key_path = temp_dir.path().join("private_key.txt");

        let secret_key = age::x25519::Identity::generate();
        let public_key = secret_key.to_public();

        std::fs::write(&public_key_path, public_key.to_string()).unwrap();
        std::fs::write(&private_key_path, secret_key.to_string().expose_secret()).unwrap();

        let client = AgeClient::new(public_key_path, private_key_path).unwrap();
        assert!(client.health_check().unwrap());
    }
}
