use dat::crypt::CryptAlgorithm;
use dat::crypt::CryptAlgorithm::{AES128GCMN, AES256GCMN};
use dat::dat_key::{DatKey};
use dat::error::DatError;
use dat::sign::SignAlgorithm;
use dat::sign::SignAlgorithm::{P256, P384, P521};
use dat::util::now_unix_timestamp;
use rand::RngExt;
use dat::dat_key_active::DatKeyActive;

fn rand_string() -> String {
    let mut rng = rand::rng();
    (0..100).map(|_| { rng.sample(rand::distr::Alphanumeric) as char }).collect()
}


fn unit(fail_key: &DatKeyActive<String>, kid: String, sign_alg: SignAlgorithm, crypt_alg: CryptAlgorithm, plain: String, secure: String) -> Result<(), DatError> {
    let tag = format!("dat.{}.{}.{}", sign_alg.to_str(), crypt_alg.to_str(), &kid);

    let new_key = DatKey::generate(kid, sign_alg, crypt_alg, now_unix_timestamp() - 10, now_unix_timestamp() + 600, 60)?;
    let new_key_str = new_key.format(true)?;
    let new_key_active = new_key.active()?;

    let read_key: DatKey<String> = new_key_str.parse()?;
    let read_key_active = read_key.active()?;

    let dat = new_key_active.to_dat(&plain, &secure)?;
    println!("{tag}: {}", dat);

    let (de_plain, de_secure) = read_key_active.to_payload(&dat)?;
    println!("{tag}: plain: {}, secure: {}", de_plain, de_secure);
    assert_eq!(plain, de_plain);
    assert_eq!(secure, de_secure);
    assert!(fail_key.to_payload(&dat).is_err());
    Ok(())
}


#[test]
fn test() {
    let sign_alg_arr = [P256, P384, P521];
    let crypt_alg_arr = [AES128GCMN, AES256GCMN];

    let fail_key = &DatKey::generate("x".to_string(), P256, AES256GCMN, now_unix_timestamp() - 10, now_unix_timestamp() + 600, 60).unwrap().active().unwrap();

    sign_alg_arr.iter().for_each(|sign_alg| {
        crypt_alg_arr.iter().for_each(|crypt_alg| {
            // random
            (1..20).for_each(|_| {
                let kid = rand_string();
                let plain = rand_string();
                let secure = rand_string();
                assert!(unit(fail_key, kid, *sign_alg, *crypt_alg, plain, secure).is_ok())
            });
            // empty
            assert!(unit(fail_key, "".to_string(), *sign_alg, *crypt_alg, "".to_string(), "".to_string()).is_ok())
        });
    });
}