//! Access Key parsing and validation
//!
//! Access Key format: ospab://<base64-encoded-data>
//! Decoded data: JSON with server, port, psk, and optional label

use serde::{Deserialize, Serialize};

/// Parsed access key data
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccessKeyData {
    pub server_ip: String,
    pub port: u16,
    pub psk: String,
    pub label: Option<String>,
}
use aes_gcm::{
    aead::{Aead, KeyInit},
    Aes256Gcm, Nonce,
};
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use sha2::{Digest, Sha256};
use thiserror::Error;

/// Key derivation salt (must match client)
const KEY_SALT: &[u8] = b"ospab.network.access.key.v1";

/// Static obfuscation key (XOR with device-specific data in production)
const OBFUSCATION_KEY: [u8; 32] = [
    0x4f, 0x53, 0x50, 0x41, 0x42, 0x2e, 0x4e, 0x45,
    0x54, 0x57, 0x4f, 0x52, 0x4b, 0x2e, 0x32, 0x30,
    0x32, 0x36, 0x2e, 0x53, 0x45, 0x43, 0x55, 0x52,
    0x45, 0x2e, 0x4b, 0x45, 0x59, 0x2e, 0x56, 0x31,
];

#[derive(Error, Debug)]
pub enum AccessKeyError {
    #[error("Invalid access key format")]
    InvalidFormat,
    #[error("Invalid base64 encoding")]
    Base64Error(#[from] base64::DecodeError),
    #[error("Decryption failed")]
    DecryptionFailed,
    #[error("Invalid JSON data")]
    JsonError(#[from] serde_json::Error),
    #[error("Missing required field: {0}")]
    MissingField(String),
}

/// Validate access key format (quick check)
pub fn validate(key: &str) -> bool {
    if !key.starts_with("ospab://") {
        return false;
    }
    
    let encoded = &key[8..]; // Skip "ospab://"
    URL_SAFE_NO_PAD.decode(encoded).is_ok()
}

/// Parse and decrypt access key
pub fn parse(key: &str) -> Result<AccessKeyData, AccessKeyError> {
    // Check prefix
    if !key.starts_with("ospab://") {
        return Err(AccessKeyError::InvalidFormat);
    }
    
    let encoded = &key[8..]; // Skip "ospab://"
    let encrypted = URL_SAFE_NO_PAD.decode(encoded)?;
    
    // Decrypt the data
    let decrypted = decrypt_access_key(&encrypted)?;
    
    // Parse JSON
    let json: serde_json::Value = serde_json::from_slice(&decrypted)?;
    
    // Extract fields
    let server_ip = json["server"]
        .as_str()
        .ok_or_else(|| AccessKeyError::MissingField("server".into()))?
        .to_string();
    
    let port = json["port"]
        .as_u64()
        .ok_or_else(|| AccessKeyError::MissingField("port".into()))? as u16;
    
    let psk = json["psk"]
        .as_str()
        .ok_or_else(|| AccessKeyError::MissingField("psk".into()))?
        .to_string();
    
    let label = json["label"].as_str().map(String::from);
    
    Ok(AccessKeyData {
        server_ip,
        port,
        psk,
        label,
    })
}

/// Decrypt access key data using AES-256-GCM
fn decrypt_access_key(encrypted: &[u8]) -> Result<Vec<u8>, AccessKeyError> {
    if encrypted.len() < 12 + 16 {
        // Nonce (12) + Tag (16) minimum
        return Err(AccessKeyError::InvalidFormat);
    }
    
    // Derive key from obfuscation key
    let key = derive_key(&OBFUSCATION_KEY);
    
    // Split nonce and ciphertext
    let (nonce_bytes, ciphertext) = encrypted.split_at(12);
    let nonce = Nonce::from_slice(nonce_bytes);
    
    // Decrypt
    let cipher = Aes256Gcm::new_from_slice(&key)
        .map_err(|_| AccessKeyError::DecryptionFailed)?;
    
    cipher
        .decrypt(nonce, ciphertext)
        .map_err(|_| AccessKeyError::DecryptionFailed)
}

/// Derive AES key from obfuscation key
fn derive_key(input: &[u8]) -> [u8; 32] {
    let mut hasher = Sha256::new();
    hasher.update(input);
    hasher.update(KEY_SALT);
    hasher.finalize().into()
}

/// Create an access key (for server-side use)
#[allow(dead_code)]
pub fn create_access_key(
    server: &str,
    port: u16,
    psk: &str,
    label: Option<&str>,
) -> Result<String, AccessKeyError> {
    use aes_gcm::aead::OsRng;
    use rand::RngCore;
    
    // Create JSON payload
    let mut json = serde_json::json!({
        "server": server,
        "port": port,
        "psk": psk
    });
    
    if let Some(l) = label {
        json["label"] = serde_json::Value::String(l.to_string());
    }
    
    let plaintext = serde_json::to_vec(&json)?;
    
    // Encrypt
    let key = derive_key(&OBFUSCATION_KEY);
    let cipher = Aes256Gcm::new_from_slice(&key)
        .map_err(|_| AccessKeyError::DecryptionFailed)?;
    
    // Generate random nonce
    let mut nonce_bytes = [0u8; 12];
    OsRng.fill_bytes(&mut nonce_bytes);
    let nonce = Nonce::from_slice(&nonce_bytes);
    
    let ciphertext = cipher
        .encrypt(nonce, plaintext.as_slice())
        .map_err(|_| AccessKeyError::DecryptionFailed)?;
    
    // Combine nonce + ciphertext
    let mut result = Vec::with_capacity(12 + ciphertext.len());
    result.extend_from_slice(&nonce_bytes);
    result.extend(ciphertext);
    
    // Encode and format
    let encoded = URL_SAFE_NO_PAD.encode(&result);
    Ok(format!("ospab://{}", encoded))
}

/// Secure memory wipe for sensitive data
#[allow(dead_code)]
pub struct SecureString {
    inner: String,
}

#[allow(dead_code)]
impl SecureString {
    pub fn new(s: String) -> Self {
        Self { inner: s }
    }
    
    pub fn as_str(&self) -> &str {
        &self.inner
    }
}

impl Drop for SecureString {
    fn drop(&mut self) {
        // Overwrite memory before deallocation
        unsafe {
            let bytes = self.inner.as_bytes_mut();
            for byte in bytes.iter_mut() {
                std::ptr::write_volatile(byte, 0);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_create_and_parse_access_key() {
        let key = create_access_key(
            "vpn.example.com",
            8443,
            "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
            Some("Test Server"),
        )
        .unwrap();
        
        assert!(key.starts_with("ospab://"));
        assert!(validate(&key));
        
        let parsed = parse(&key).unwrap();
        assert_eq!(parsed.server_ip, "vpn.example.com");
        assert_eq!(parsed.port, 8443);
        assert_eq!(parsed.label, Some("Test Server".to_string()));
    }
    
    #[test]
    fn test_invalid_key() {
        assert!(!validate("invalid://key"));
        assert!(!validate("ospab://!!!invalid-base64!!!"));
    }
}
