#[cfg(feature = "crypto")]
pub fn hash_sha256(input: &str) -> String {
    use sha2::{Digest, Sha256};
    let mut hasher = Sha256::new();
    hasher.update(input.as_bytes());
    format!("{:x}", hasher.finalize())
}

#[cfg(feature = "crypto")]
pub fn verify_sha256(input: &str, expected: &str) -> bool {
    hash_sha256(input) == expected
}

#[cfg(feature = "crypto")]
pub fn generate_token(bytes: usize) -> String {
    use base64::Engine;
    let random: Vec<u8> = (0..bytes).map(|_| rand::random::<u8>()).collect();
    base64::engine::general_purpose::URL_SAFE.encode(&random)
}

#[cfg(feature = "crypto")]
pub fn secure_compare(a: &str, b: &str) -> bool {
    use subtle::ConstantTimeEq;
    if a.len() != b.len() {
        return false;
    }
    a.as_bytes().ct_eq(b.as_bytes()).unwrap_u8() == 1
}

#[cfg(feature = "crypto")]
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_hash_sha256() {
        let h = hash_sha256("hello");
        assert_eq!(
            h,
            "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
        );
    }

    #[test]
    fn test_verify_sha256() {
        let h = hash_sha256("hello");
        assert!(verify_sha256("hello", &h));
        assert!(!verify_sha256("world", &h));
    }

    #[test]
    fn test_generate_token() {
        let t = generate_token(32);
        assert!(!t.is_empty());
    }

    #[test]
    fn test_secure_compare() {
        assert!(secure_compare("test", "test"));
        assert!(!secure_compare("test", "Test"));
    }
}
