//! 邮件正文静态加密（AES-GCM-256）。
//!
//! 密钥来自 `smtp.body_encryption_key`（必须恰好 32 字符）。
//! 留空 → 不加密（向后兼容）。密文格式：base64(nonce(12) || ciphertext)。

use aes_gcm::aead::{Aead, KeyInit};
use aes_gcm::{Aes256Gcm, Key, Nonce};
use anyhow::Result;
use base64::Engine;
use rand::RngCore;

/// 从配置字符串派生 32 字节密钥；长度不为 32 则返回 None（=不加密）。
pub fn derive_key(s: &str) -> Option<[u8; 32]> {
    if s.len() != 32 {
        return None;
    }
    let mut k = [0u8; 32];
    k.copy_from_slice(s.as_bytes());
    Some(k)
}

pub fn encrypt(key: &[u8; 32], plaintext: &str) -> Result<String> {
    let cipher = Aes256Gcm::new(Key::<Aes256Gcm>::from_slice(key));
    let mut nonce_bytes = [0u8; 12];
    rand::thread_rng().fill_bytes(&mut nonce_bytes);
    let nonce = Nonce::from_slice(&nonce_bytes);
    let ct = cipher
        .encrypt(nonce, plaintext.as_bytes())
        .map_err(|e| anyhow::anyhow!("加密失败: {e}"))?;
    let mut combined = nonce_bytes.to_vec();
    combined.extend_from_slice(&ct);
    Ok(base64::engine::general_purpose::STANDARD.encode(&combined))
}

pub fn decrypt(key: &[u8; 32], b64: &str) -> Result<String> {
    let combined = base64::engine::general_purpose::STANDARD.decode(b64)?;
    if combined.len() <= 12 {
        anyhow::bail!("密文过短");
    }
    let (nonce_bytes, ct) = combined.split_at(12);
    let nonce = Nonce::from_slice(nonce_bytes);
    let cipher = Aes256Gcm::new(Key::<Aes256Gcm>::from_slice(key));
    let pt = cipher
        .decrypt(nonce, ct)
        .map_err(|e| anyhow::anyhow!("解密失败: {e}"))?;
    Ok(String::from_utf8(pt)?)
}

/// 加密 Option<String>；无密钥则原样返回，配置了密钥时绝不静默降级为明文。
pub fn encrypt_optional(key: &Option<[u8; 32]>, text: Option<String>) -> Result<Option<String>> {
    match (key, text) {
        (Some(k), Some(t)) => Ok(Some(encrypt(k, &t)?)),
        (_, t) => Ok(t),
    }
}

/// 解密 Option<String>；无密钥或解密失败（历史明文）则原样返回。
pub fn decrypt_optional(key: &Option<[u8; 32]>, text: Option<String>) -> Option<String> {
    match (key, text) {
        (Some(k), Some(t)) => decrypt(k, &t).ok().or(Some(t)),
        (_, t) => t,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn encrypt_decrypt_roundtrip() {
        let key = derive_key("0123456789abcdef0123456789abcdef").unwrap();
        let plaintext = "hello 邮件正文 secret 123";
        let ct = encrypt(&key, plaintext).unwrap();
        assert_ne!(ct, plaintext);
        assert_eq!(decrypt(&key, &ct).unwrap(), plaintext);
    }

    #[test]
    fn derive_key_requires_32_chars() {
        assert!(derive_key("short").is_none());
        assert!(derive_key(&"a".repeat(32)).is_some());
        assert!(derive_key(&"a".repeat(31)).is_none());
    }

    #[test]
    fn optional_handles_no_key() {
        let text = Some("plain".into());
        assert_eq!(encrypt_optional(&None, text.clone()).unwrap(), text);
        assert_eq!(decrypt_optional(&None, text.clone()), text);
    }
}
