use dat::certificate::DatCertificate;
use dat::crypto::DatCryptoAlgorithm;
use dat::crypto::DatCryptoAlgorithm::{AES128GCMN, AES256GCMN};
use dat::error::DatError;
use dat::manager::DatManager;
use dat::signature::DatSignatureAlgorithm;
use dat::signature::DatSignatureAlgorithm::{P256, P384, P521};
use dat::signature::DatSignatureKeyExportOption;
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_certificate: &DatCertificate, cid: u64, signature_algorithm: DatSignatureAlgorithm, crypto_algorithm: DatCryptoAlgorithm, plain: String, secure: String) -> Result<(), DatError> {
    let tag = format!("dat.{}.{}.{:x}", signature_algorithm, crypto_algorithm, cid);

    let new_certificate = DatCertificate::generate(cid, signature_algorithm, crypto_algorithm, now_unix_timestamp() - 10, now_unix_timestamp() + 600, 60)?;
    let new_certificate_str = new_certificate.export(DatSignatureKeyExportOption::SIGNING)?;

    let read_certificate: DatCertificate = new_certificate_str.parse()?;

    let dat = DatManager::_issue(&new_certificate, &plain, &secure)?;
    println!("{tag}: {}", dat);

    let payload = DatManager::_parse(&read_certificate, dat.clone().try_into()?)?.to_string_payload()?;
    println!("{tag}:{}", payload);
    assert_eq!(plain, payload.plain);
    assert_eq!(secure, payload.secure);
    assert!(DatManager::_parse(&fail_certificate, dat.try_into()?).is_err());
    Ok(())
}


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

    let fail_certificate = &DatCertificate::generate(192874, 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(|i| {
                let plain = rand_string();
                let secure = rand_string();
                assert!(unit(fail_certificate, i, *sign_alg, *crypto_alg, plain, secure)
                    .map_err(|e| {
                        println!("@{:?}", e);
                        e
                    }).is_ok())
            });
            // empty
            assert!(unit(fail_certificate, 0, *sign_alg, *crypto_alg, "".to_string(), "".to_string()).is_ok())
        });
    });
}