use password_hash::{PasswordHash, PasswordHasher, SaltString};
use pbkdf2::{Params, Pbkdf2};
use rsa::{Pkcs1v15Encrypt, RsaPrivateKey};
use thiserror::Error;

const SDK_PRIVATE_KEY: &[u8] = include_bytes!("../security/sdk_private_key.der");

#[derive(Error, Debug)]
pub enum CryptoError {
    #[error("failed to decrypt: {0}")]
    DecryptFailed(#[from] rsa::Error),
    #[error("failed to decode base64 string")]
    FromBase64Failed,
    #[error("from_utf8 failed: {0}")]
    FromUtf8Failed(#[from] std::string::FromUtf8Error),
}

#[must_use]
pub fn rsa_decrypt(cipher: &str) -> Result<String, CryptoError> {
    let private_key: RsaPrivateKey = rsa::pkcs8::DecodePrivateKey::from_pkcs8_der(SDK_PRIVATE_KEY)
        .expect("failed to decode private key");
    let payload = private_key.decrypt(
        Pkcs1v15Encrypt,
        &rbase64::decode(cipher).map_err(|_| CryptoError::FromBase64Failed)?,
    )?;

    Ok(String::from_utf8(payload)?)
}

#[must_use]
pub fn hash_string(content: &str) -> Result<String, pbkdf2::password_hash::Error> {
    let salt = SaltString::generate(rand::thread_rng());
    let hash = Pbkdf2.hash_password_customized(
        content.as_bytes(),
        None,
        None,
        Params {
            rounds: 10000,
            output_length: 32,
        },
        &salt,
    )?;

    Ok(hash.serialize().to_string())
}

#[must_use]
pub fn verify_hash(content: &str, hash_str: &str) -> Option<()> {
    let hash = PasswordHash::new(hash_str).ok()?;
    hash.verify_password(&[&Pbkdf2], content).ok()
}
