use base64::{engine::general_purpose::STANDARD, Engine};
use companytool_shared_protocol::crypto::SessionKey;
use serde::{de::DeserializeOwned, Serialize};
use tracing::warn;

#[derive(Clone)]
pub struct DataCrypto {
    key: SessionKey,
}

pub struct EncryptedField {
    pub ciphertext: Vec<u8>,
    pub nonce: Vec<u8>,
    pub key_id: String,
}

impl DataCrypto {
    pub fn from_env() -> Self {
        let key_id = std::env::var("COMPANYTOOL_DATA_KEY_ID")
            .unwrap_or_else(|_| "dev-data-key-v1".to_string());
        let key_base64 = std::env::var("COMPANYTOOL_DATA_KEY_BASE64").unwrap_or_else(|_| {
            warn!("COMPANYTOOL_DATA_KEY_BASE64 fehlt; verwende nur für Entwicklung geeigneten festen Schlüssel");
            STANDARD.encode([7_u8; 32])
        });
        let key = SessionKey::from_base64(key_id, &key_base64).expect("valid data encryption key");

        Self { key }
    }

    pub fn encrypt<T: Serialize>(&self, value: &T) -> anyhow::Result<EncryptedField> {
        let envelope = self.key.encrypt(value)?;
        Ok(EncryptedField {
            ciphertext: STANDARD.decode(envelope.ciphertext)?,
            nonce: STANDARD.decode(envelope.nonce)?,
            key_id: envelope.key_id,
        })
    }

    pub fn decrypt<T: DeserializeOwned>(
        &self,
        ciphertext: &[u8],
        nonce: &[u8],
        key_id: &str,
    ) -> anyhow::Result<T> {
        let envelope = companytool_shared_protocol::EncryptedEnvelope {
            enc: "aes-256-gcm-v1".to_string(),
            key_id: key_id.to_string(),
            nonce: STANDARD.encode(nonce),
            ciphertext: STANDARD.encode(ciphertext),
        };

        Ok(self.key.decrypt(&envelope)?)
    }
}
