//! SSH Key Generator
//!
//! Generates Ed25519 SSH key pairs for dynamic key management.

use anyhow::Result;
use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
use chrono::Utc;
use rand::rngs::OsRng;
use sha2::{Digest, Sha256};
use uuid::Uuid;

use super::{SshKeyRequest, SshKeyType, TemporalSshKey};

/// SSH key generator
pub struct KeyGenerator;

impl KeyGenerator {
    /// Create new key generator
    pub fn new() -> Self {
        Self
    }

    /// Generate Ed25519 SSH key pair
    pub async fn generate_keypair(&self, request: &SshKeyRequest) -> Result<TemporalSshKey> {
        // Generate Ed25519 key pair
        let signing_key = ed25519_dalek::SigningKey::from_bytes(&rand::random::<[u8; 32]>());
        let verifying_key = signing_key.verifying_key();

        // Convert to OpenSSH format
        let public_key =
            self.format_public_key(&verifying_key, &request.user, &request.target_server)?;
        let private_key = self.format_private_key(&signing_key)?;

        // Calculate fingerprint
        let fingerprint = self.calculate_fingerprint(&verifying_key)?;

        // Calculate expiration
        let created_at = Utc::now();
        let expires_at = created_at + request.ttl;

        Ok(TemporalSshKey {
            id: Uuid::new_v4().to_string(),
            key_type: SshKeyType::DynamicKeyPair,
            public_key,
            private_key: Some(private_key),
            fingerprint,
            user: request.user.clone(),
            target_server: request.target_server.clone(),
            created_at,
            expires_at,
            ttl: request.ttl,
            used: false,
            deployed: false,
            vault_lease_id: None,
        })
    }

    /// Format public key in OpenSSH format
    fn format_public_key(
        &self,
        verifying_key: &ed25519_dalek::VerifyingKey,
        user: &str,
        server: &str,
    ) -> Result<String> {
        let key_bytes = verifying_key.as_bytes();

        // OpenSSH Ed25519 public key format:
        // ssh-ed25519 <base64-encoded-key> comment
        let mut buffer = Vec::new();

        // Key type length and data
        let key_type = b"ssh-ed25519";
        buffer.extend_from_slice(&(key_type.len() as u32).to_be_bytes());
        buffer.extend_from_slice(key_type);

        // Public key length and data
        buffer.extend_from_slice(&(key_bytes.len() as u32).to_be_bytes());
        buffer.extend_from_slice(key_bytes);

        // Base64 encode
        let encoded = BASE64.encode(&buffer);

        // Add comment
        let comment = format!("{}@{}", user, server);
        Ok(format!("ssh-ed25519 {} {}", encoded, comment))
    }

    /// Format private key in OpenSSH format
    fn format_private_key(&self, signing_key: &ed25519_dalek::SigningKey) -> Result<String> {
        // For simplicity, we'll output PEM format
        // In production, you might want to use OpenSSH private key format
        let key_bytes = signing_key.to_bytes();
        let encoded = BASE64.encode(key_bytes);

        let mut pem = String::new();
        pem.push_str("-----BEGIN OPENSSH PRIVATE KEY-----\n");

        // Wrap at 70 characters
        for chunk in encoded.as_bytes().chunks(70) {
            pem.push_str(std::str::from_utf8(chunk)?);
            pem.push('\n');
        }

        pem.push_str("-----END OPENSSH PRIVATE KEY-----\n");

        Ok(pem)
    }

    /// Calculate SSH fingerprint (SHA256)
    fn calculate_fingerprint(&self, verifying_key: &ed25519_dalek::VerifyingKey) -> Result<String> {
        let key_bytes = verifying_key.as_bytes();

        // OpenSSH fingerprint format
        let mut buffer = Vec::new();
        let key_type = b"ssh-ed25519";
        buffer.extend_from_slice(&(key_type.len() as u32).to_be_bytes());
        buffer.extend_from_slice(key_type);
        buffer.extend_from_slice(&(key_bytes.len() as u32).to_be_bytes());
        buffer.extend_from_slice(key_bytes);

        // SHA256 hash
        let mut hasher = Sha256::new();
        hasher.update(&buffer);
        let hash = hasher.finalize();

        // Format as SHA256:base64
        let encoded = BASE64.encode(hash);
        Ok(format!("SHA256:{}", encoded))
    }
}

impl Default for KeyGenerator {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use chrono::Duration;

    use super::*;

    #[tokio::test]
    async fn test_generate_keypair() {
        let generator = KeyGenerator::new();
        let request = SshKeyRequest {
            key_type: SshKeyType::DynamicKeyPair,
            user: "root".to_string(),
            target_server: "test.example.com".to_string(),
            ttl: Duration::hours(1),
            allowed_ip: None,
            principal: None,
        };

        let key = generator.generate_keypair(&request).await.unwrap();

        assert_eq!(key.key_type, SshKeyType::DynamicKeyPair);
        assert!(key.public_key.starts_with("ssh-ed25519"));
        assert!(key.private_key.is_some());
        assert!(key.fingerprint.starts_with("SHA256:"));
        assert_eq!(key.user, "root");
        assert_eq!(key.target_server, "test.example.com");
    }

    #[tokio::test]
    async fn test_public_key_format() {
        let generator = KeyGenerator::new();
        let mut rng = rand::rng();
        let random_bytes: [u8; 32] = rand::Rng::random(&mut rng);
        let signing_key = ed25519_dalek::SigningKey::from_bytes(&random_bytes);
        let verifying_key = signing_key.verifying_key();

        let public_key = generator
            .format_public_key(&verifying_key, "testuser", "server.local")
            .unwrap();

        assert!(public_key.starts_with("ssh-ed25519"));
        assert!(public_key.ends_with("testuser@server.local"));
    }

    #[tokio::test]
    async fn test_fingerprint_generation() {
        let generator = KeyGenerator::new();
        let mut rng = rand::rng();
        let random_bytes: [u8; 32] = rand::Rng::random(&mut rng);
        let signing_key = ed25519_dalek::SigningKey::from_bytes(&random_bytes);
        let verifying_key = signing_key.verifying_key();

        let fingerprint = generator.calculate_fingerprint(&verifying_key).unwrap();

        assert!(fingerprint.starts_with("SHA256:"));
        assert!(fingerprint.len() > 10);
    }
}
