use dat::crypto_algorithm::CryptoAlgorithm;
use dat::crypto_algorithm::CryptoAlgorithm::{AES128GCMN, AES256GCMN};
use dat::dat_key::DatKey;
use dat::dat::Dat;
use dat::error::DatError;
use dat::signature_algorithm::SignatureAlgorithm;
use dat::signature_algorithm::SignatureAlgorithm::{P256, P384, P521};
use dat::signature_key::SignatureKeyOutOption;
use dat::util::now_unix_timestamp;
use rand::RngExt;

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


fn unit(fail_key: &DatKey<String>, kid: String, signature_algorithm: SignatureAlgorithm, crypto_algorithm: CryptoAlgorithm, plain: String, secure: String) -> Result<(), DatError> {
    let tag = format!("dat.{}.{}.{}", signature_algorithm, crypto_algorithm, &kid);

    let new_key = DatKey::generate(kid, signature_algorithm, crypto_algorithm, now_unix_timestamp() - 10, now_unix_timestamp() + 600, 60)?;
    let new_key_str = new_key.format(SignatureKeyOutOption::SIGNING)?;

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

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

    let dat: Dat<String> = dat.try_into()?;
    let payload = read_key.to_payload(&dat)?.to_string_payload()?;
    println!("{tag}:{}", payload);
    assert_eq!(plain, payload.plain);
    assert_eq!(secure, payload.secure);
    assert!(fail_key.to_payload(&dat).is_err());
    Ok(())
}


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

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

    sign_alg_arr.iter().for_each(|sign_alg| {
        crypto_alg_arr.iter().for_each(|crypto_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, *crypto_alg, plain, secure).map_err(|e| {println!("{}", e.to_string()); e}).is_ok())
            });
            // empty
            assert!(unit(fail_key, "".to_string(), *sign_alg, *crypto_alg, "".to_string(), "".to_string()).is_ok())
        });
    });
}