use super::*;
use crate::digest;
use proptest::prelude::*;
use quickcheck::{Arbitrary, Gen};
use rand::rngs::SmallRng;
use rand_core::{CryptoRng, Error as RngError, RngCore, SeedableRng};
#[derive(Clone, Debug, test_strategy::Arbitrary)]
pub struct TestCryptoGen(pub u64);
pub struct TestCryptoRng(SmallRng);
impl RngCore for TestCryptoRng {
    fn next_u32(&mut self) -> u32 {
        self.0.next_u32()
    }
    fn next_u64(&mut self) -> u64 {
        self.0.next_u64()
    }
    fn fill_bytes(&mut self, dest: &mut [u8]) {
        self.0.fill_bytes(dest)
    }
    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), RngError> {
        self.0.try_fill_bytes(dest)
    }
}
impl SeedableRng for TestCryptoRng {
    type Seed = <SmallRng as SeedableRng>::Seed;
    fn from_seed(seed: Self::Seed) -> Self {
        Self(SmallRng::from_seed(seed))
    }
}
impl CryptoRng for TestCryptoRng {}
impl Arbitrary for TestCryptoGen {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        TestCryptoGen(Arbitrary::arbitrary(g))
    }
}
impl TestCryptoGen {
    pub fn get_rng(&self, idx: u32) -> TestCryptoRng {
        TestCryptoRng::seed_from_u64((idx as u64) * 4096 + self.0)
    }
    pub fn secret_key<A: AsymmetricKey>(&self, idx: u32) -> SecretKey<A> {
        SecretKey::generate(self.get_rng(idx))
    }
    pub fn keypair<A: AsymmetricKey>(&self, idx: u32) -> KeyPair<A> {
        KeyPair::from(self.secret_key(idx))
    }
}
#[allow(dead_code)]
pub fn arbitrary_public_key<A: AsymmetricKey, G: Gen>(g: &mut G) -> PublicKey<A::PubAlg> {
    TestCryptoGen::arbitrary(g)
        .keypair::<A>(0)
        .public_key()
        .clone()
}
pub fn arbitrary_secret_key<A, G>(g: &mut G) -> SecretKey<A>
where
    A: AsymmetricKey,
    G: Gen,
{
    TestCryptoGen::arbitrary(g).secret_key(0)
}
#[allow(dead_code)]
pub fn static_secret_key<A>() -> SecretKey<A>
where
    A: AsymmetricKey,
{
    let rng = TestCryptoRng::seed_from_u64(0xfedc_ba98);
    SecretKey::generate(rng)
}
impl<A> Arbitrary for SecretKey<A>
where
    A: AsymmetricKey + 'static,
    A::Secret: Send,
{
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        arbitrary_secret_key(g)
    }
}
impl<A> Arbitrary for KeyPair<A>
where
    A: AsymmetricKey + 'static,
    A::Secret: Send,
    <A::PubAlg as AsymmetricPublicKey>::Public: Send,
{
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        let secret_key = SecretKey::arbitrary(g);
        KeyPair::from(secret_key)
    }
}
impl<T, A> Arbitrary for Signature<T, A>
where
    A: VerificationAlgorithm + 'static,
    A::Signature: Send,
    T: Send + 'static,
{
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        let bytes: Vec<_> = std::iter::repeat_with(|| u8::arbitrary(g))
            .take(A::SIGNATURE_SIZE)
            .collect();
        Signature::from_binary(&bytes).unwrap()
    }
}
impl Arbitrary for Blake2b256 {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        let bytes: Vec<_> = std::iter::repeat_with(|| u8::arbitrary(g))
            .take(Self::HASH_SIZE)
            .collect();
        Self::try_from_slice(&bytes).unwrap()
    }
}
impl<H: digest::DigestAlg + 'static> Arbitrary for digest::Digest<H> {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        let bytes: Vec<_> = std::iter::repeat_with(|| u8::arbitrary(g))
            .take(26) .collect();
        digest::Digest::<H>::digest(&bytes[..])
    }
}
impl<H: digest::DigestAlg + 'static, T: 'static + Send> Arbitrary for digest::DigestOf<H, T> {
    fn arbitrary<G: Gen>(g: &mut G) -> Self {
        let bytes: Vec<_> = std::iter::repeat_with(|| u8::arbitrary(g))
            .take(26) .collect();
        digest::DigestOf::<H, Vec<u8>>::digest(&bytes).coerce()
    }
}
pub fn public_key_strategy<A: AsymmetricKey>() -> impl Strategy<Value = PublicKey<A::PubAlg>> {
    any::<(TestCryptoGen, u32)>()
        .prop_map(|(gen, idx)| SecretKey::<A>::generate(gen.get_rng(idx)).to_public())
}