#![cfg(feature = "runtime-benchmarks")]

use super::*;
use frame_benchmarking::v1::*;

use frame_support::traits::fungible::Inspect;
use frame_system::RawOrigin;

pub fn create_account<T: Config>() -> T::AccountId {
    let account_bytes = Vec::from([1u8; 32]);
    T::AccountId::decode(&mut &account_bytes[0..32])
        .expect("32 bytes always construct an AccountId32")
}

benchmarks! {
    slow_clap {
        let minimum_balance = <<T as pallet::Config>::Currency>::minimum_balance();
        let receiver = create_account::<T>();
        let amount = minimum_balance + minimum_balance;
        let network_id = NetworkIdOf::<T>::default();
        let session_index = T::ValidatorSet::session_index();

        let authorities = vec![T::AuthorityId::generate_pair(None)];
        let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities.clone())
            .map_err(|()| "more than the maximum number of keys provided")?;
        Authorities::<T>::set(&session_index, bounded_authorities);

        let clap = Clap {
            session_index: 0,
            authority_index: 0,
            transaction_hash: H256::repeat_byte(1u8),
            block_number: 69,
            removed: false,
            network_id,
            receiver: receiver.clone(),
            amount,
        };

        let authority_id = authorities
            .get(0usize)
            .expect("first authority should exist");
        let encoded_clap = clap.encode();
        let signature = authority_id.sign(&encoded_clap)
            .ok_or("couldn't make signature")?;

    }: _(RawOrigin::None, clap, signature)
    verify {
        assert_eq!(<<T as pallet::Config>::Currency>::total_balance(&receiver), amount);
    }

    self_applause {
        let session_index = T::ValidatorSet::session_index();
        let next_session_index = session_index.saturating_add(1);
        let authorities = vec![
            T::AuthorityId::generate_pair(None),
            T::AuthorityId::generate_pair(None),
        ];
        let bounded_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::try_from(authorities.clone())
            .map_err(|()| "more than the maximum number of keys provided")?;
        Authorities::<T>::set(&session_index, bounded_authorities.clone());
        Authorities::<T>::set(&next_session_index, bounded_authorities);

        let minimum_balance = <<T as pallet::Config>::Currency>::minimum_balance();
        let receiver = create_account::<T>();
        let receiver_clone = receiver.clone();
        let amount = minimum_balance + minimum_balance;
        let network_id = NetworkIdOf::<T>::default();
        let transaction_hash = H256::repeat_byte(1u8);

        let unique_transaction_hash = <Pallet<T>>::generate_unique_hash(
            &receiver,
            &amount,
            &network_id,
        );
        let storage_key = (session_index, &transaction_hash, &unique_transaction_hash);
        let next_storage_key = (next_session_index, &transaction_hash, &unique_transaction_hash);

        <Pallet::<T>>::trigger_nullification_for_benchmark();
        let clap = Clap {
            session_index,
            authority_index: 0,
            transaction_hash,
            block_number: 69,
            removed: false,
            network_id,
            receiver: receiver.clone(),
            amount,
        };

        let authority_id = authorities
            .get(0usize)
            .expect("first authority should exist");
        let encoded_clap = clap.encode();
        let signature = authority_id.sign(&encoded_clap).unwrap();
        Pallet::<T>::slow_clap(RawOrigin::None.into(), clap, signature)?;
        Pallet::<T>::trigger_nullification_for_benchmark();

        assert_eq!(<<T as pallet::Config>::Currency>::total_balance(&receiver), Default::default());
        assert_eq!(ApplausesForTransaction::<T>::get(&storage_key), false);

        frame_system::Pallet::<T>::on_initialize(1u32.into());

        let mut fake_received_clap =
            BoundedBTreeSet::<AuthIndex, T::MaxAuthorities>::new();
        assert_eq!(fake_received_clap.try_insert(1).unwrap(), true);
        pallet::ReceivedClaps::<T>::insert(&next_storage_key, fake_received_clap);
    }: _(RawOrigin::Signed(receiver_clone), network_id, session_index, transaction_hash, receiver_clone.clone(), amount)
    verify {
        assert_eq!(<<T as pallet::Config>::Currency>::total_balance(&receiver), amount);
        assert_eq!(ApplausesForTransaction::<T>::get(&storage_key), true);
    }

    impl_benchmark_test_suite!(
        Pallet,
        crate::mock::new_test_ext(),
        crate::mock::Runtime,
    );
}
