#![cfg(feature = "runtime-benchmarks")]
use super::*;
use frame_support::traits::UnfilteredDispatchable;
use frame_system::{Pallet as System, RawOrigin};
use sp_runtime::traits::Hash;
frame_benchmarking::benchmarks! {
	storage_single_value_read {
		Value::<T>::put(123);
	}: {
		assert_eq!(Value::<T>::get(), Some(123));
	}
	#[pov_mode = Ignored]
	storage_single_value_ignored_read {
		Value::<T>::put(123);
	}: {
		assert_eq!(Value::<T>::get(), Some(123));
	}
	#[pov_mode = MaxEncodedLen {
		Pov::Value2: Ignored
	}]
	storage_single_value_ignored_some_read {
		Value::<T>::put(123);
		Value2::<T>::put(123);
	}: {
		assert_eq!(Value::<T>::get(), Some(123));
		assert_eq!(Value2::<T>::get(), Some(123));
	}
	storage_single_value_read_twice {
		Value::<T>::put(123);
	}: {
		assert_eq!(Value::<T>::get(), Some(123));
		assert_eq!(Value::<T>::get(), Some(123));
	}
	storage_single_value_write {
	}: {
		Value::<T>::put(123);
	} verify {
		assert_eq!(Value::<T>::get(), Some(123));
	}
	storage_single_value_kill {
		Value::<T>::put(123);
	}: {
		Value::<T>::kill();
	} verify {
		assert!(!Value::<T>::exists());
	}
	#[pov_mode = Measured]
	storage_1m_map_read_one_value_two_additional_layers {
		(0..(1<<10)).for_each(|i| Map1M::<T>::insert(i, i));
		(0..(1u32<<4)).for_each(|i| {
			let k = T::Hashing::hash(&i.to_be_bytes());
			frame_support::storage::unhashed::put(k.as_ref(), &i);
		});
	}: {
		assert_eq!(Map1M::<T>::get(1<<9), Some(1<<9));
	}
	#[pov_mode = Measured]
	storage_1m_map_read_one_value_three_additional_layers {
		(0..(1<<10)).for_each(|i| Map1M::<T>::insert(i, i));
		(0..(1u32<<8)).for_each(|i| {
			let k = T::Hashing::hash(&i.to_be_bytes());
			frame_support::storage::unhashed::put(k.as_ref(), &i);
		});
	}: {
		assert_eq!(Map1M::<T>::get(1<<9), Some(1<<9));
	}
	#[pov_mode = Measured]
	storage_1m_map_read_one_value_four_additional_layers {
		(0..(1<<10)).for_each(|i| Map1M::<T>::insert(i, i));
		(0..(1u32<<12)).for_each(|i| {
			let k = T::Hashing::hash(&i.to_be_bytes());
			frame_support::storage::unhashed::put(k.as_ref(), &i);
		});
	}: {
		assert_eq!(Map1M::<T>::get(1<<9), Some(1<<9));
	}
	storage_map_read_per_component {
		let n in 0 .. 100;
		let m in 0 .. 100;
		(0..m*10).for_each(|i| Map1M::<T>::insert(i, i));
		(0..n*10).for_each(|i| Map16M::<T>::insert(i, i));
	}: {
		(0..m).for_each(|i|
			assert_eq!(Map1M::<T>::get(i*10), Some(i*10)));
		(0..n).for_each(|i|
			assert_eq!(Map16M::<T>::get(i*10), Some(i*10)));
	}
	#[pov_mode = MaxEncodedLen {
		Pov::Map1M: Ignored
	}]
	storage_map_read_per_component_one_ignored {
		let n in 0 .. 100;
		let m in 0 .. 100;
		(0..m*10).for_each(|i| Map1M::<T>::insert(i, i));
		(0..n*10).for_each(|i| Map16M::<T>::insert(i, i));
	}: {
		(0..m).for_each(|i|
			assert_eq!(Map1M::<T>::get(i*10), Some(i*10)));
		(0..n).for_each(|i|
			assert_eq!(Map16M::<T>::get(i*10), Some(i*10)));
	}
	storage_1m_map_one_entry_repeated_read {
		let n in 0 .. 100;
		Map1M::<T>::insert(0, 0);
	}: {
		(0..n).for_each(|i|
			assert_eq!(Map1M::<T>::get(0), Some(0)));
	}
	storage_1m_map_multiple_entry_repeated_read {
		let n in 0 .. 100;
		(0..n).for_each(|i| Map1M::<T>::insert(i, i));
	}: {
		(0..n).for_each(|i| {
			(0..10).for_each(|j|
				assert_eq!(Map1M::<T>::get(i), Some(i)));
		});
	}
	storage_1m_double_map_read_per_component {
		let n in 0 .. 1024;
		(0..(1<<10)).for_each(|i| DoubleMap1M::<T>::insert(i, i, i));
	}: {
		(0..n).for_each(|i|
			assert_eq!(DoubleMap1M::<T>::get(i, i), Some(i)));
	}
	storage_value_bounded_read {
	}: {
		assert!(BoundedValue::<T>::get().is_none());
	}
	storage_value_unbounded_read {
	}: {
		assert!(UnboundedValue::<T>::get().is_none());
	}
	#[pov_mode = Ignored]
	storage_value_unbounded_ignored_read {
	}: {
		assert!(UnboundedValue::<T>::get().is_none());
	}
	storage_value_bounded_and_unbounded_read {
		(0..1024).for_each(|i| Map1M::<T>::insert(i, i));
	}: {
		assert!(UnboundedValue::<T>::get().is_none());
		assert!(BoundedValue::<T>::get().is_none());
	}
	#[pov_mode = Measured]
	measured_storage_value_read_linear_size {
		let l in 0 .. 1<<22;
		let v: sp_runtime::BoundedVec<u8, _> = sp_std::vec![0u8; l as usize].try_into().unwrap();
		LargeValue::<T>::put(&v);
	}: {
		assert!(LargeValue::<T>::get().is_some());
	}
	#[pov_mode = MaxEncodedLen]
	mel_storage_value_read_linear_size {
		let l in 0 .. 1<<22;
		let v: sp_runtime::BoundedVec<u8, _> = sp_std::vec![0u8; l as usize].try_into().unwrap();
		LargeValue::<T>::put(&v);
	}: {
		assert!(LargeValue::<T>::get().is_some());
	}
	#[pov_mode = Measured]
	measured_storage_double_value_read_linear_size {
		let l in 0 .. 1<<22;
		let v: sp_runtime::BoundedVec<u8, _> = sp_std::vec![0u8; l as usize].try_into().unwrap();
		LargeValue::<T>::put(&v);
		LargeValue2::<T>::put(&v);
	}: {
		assert!(LargeValue::<T>::get().is_some());
		assert!(LargeValue2::<T>::get().is_some());
	}
	#[pov_mode = MaxEncodedLen]
	mel_storage_double_value_read_linear_size {
		let l in 0 .. 1<<22;
		let v: sp_runtime::BoundedVec<u8, _> = sp_std::vec![0u8; l as usize].try_into().unwrap();
		LargeValue::<T>::put(&v);
		LargeValue2::<T>::put(&v);
	}: {
		assert!(LargeValue::<T>::get().is_some());
		assert!(LargeValue2::<T>::get().is_some());
	}
	#[pov_mode = MaxEncodedLen {
		Pov::LargeValue2: Measured
	}]
	mel_mixed_storage_double_value_read_linear_size {
		let l in 0 .. 1<<22;
		let v: sp_runtime::BoundedVec<u8, _> = sp_std::vec![0u8; l as usize].try_into().unwrap();
		LargeValue::<T>::put(&v);
		LargeValue2::<T>::put(&v);
	}: {
		assert!(LargeValue::<T>::get().is_some());
		assert!(LargeValue2::<T>::get().is_some());
	}
	#[pov_mode = Measured {
		Pov::LargeValue2: MaxEncodedLen
	}]
	measured_mixed_storage_double_value_read_linear_size {
		let l in 0 .. 1<<22;
		let v: sp_runtime::BoundedVec<u8, _> = sp_std::vec![0u8; l as usize].try_into().unwrap();
		LargeValue::<T>::put(&v);
		LargeValue2::<T>::put(&v);
	}: {
		assert!(LargeValue::<T>::get().is_some());
		assert!(LargeValue2::<T>::get().is_some());
	}
	#[pov_mode = Measured]
	storage_map_unbounded_both_measured_read {
		let i in 0 .. 1000;
		UnboundedMap::<T>::insert(i, sp_std::vec![0; i as usize]);
		UnboundedMap2::<T>::insert(i, sp_std::vec![0; i as usize]);
	}: {
		assert!(UnboundedMap::<T>::get(i).is_some());
		assert!(UnboundedMap2::<T>::get(i).is_some());
	}
	#[pov_mode = MaxEncodedLen {
		Pov::UnboundedMap: Measured
	}]
	storage_map_partial_unbounded_read {
		let i in 0 .. 1000;
		Map1M::<T>::insert(i, 0);
		UnboundedMap::<T>::insert(i, sp_std::vec![0; i as usize]);
	}: {
		assert!(Map1M::<T>::get(i).is_some());
		assert!(UnboundedMap::<T>::get(i).is_some());
	}
	#[pov_mode = MaxEncodedLen {
		Pov::UnboundedMap: Ignored
	}]
	storage_map_partial_unbounded_ignored_read {
		let i in 0 .. 1000;
		Map1M::<T>::insert(i, 0);
		UnboundedMap::<T>::insert(i, sp_std::vec![0; i as usize]);
	}: {
		assert!(Map1M::<T>::get(i).is_some());
		assert!(UnboundedMap::<T>::get(i).is_some());
	}
	emit_event {
		let call = Call::<T>::emit_event {  };
	}: { call.dispatch_bypass_filter(RawOrigin::Root.into()).unwrap(); }
	verify {
		assert_eq!(System::<T>::events().len(), 1);
	}
	noop {
		let call = Call::<T>::noop { };
	}: {
		call.dispatch_bypass_filter(RawOrigin::Root.into()).unwrap();
	}
	storage_iteration {
		for i in 0..65000 {
			UnboundedMapTwox::<T>::insert(i, sp_std::vec![0; 64]);
		}
	}: {
		for (key, value) in UnboundedMapTwox::<T>::iter() {
			unsafe {
				core::ptr::read_volatile(&key);
				core::ptr::read_volatile(value.as_ptr());
			}
		}
	}
	impl_benchmark_test_suite!(
		Pallet,
		mock::new_test_ext(),
		mock::Test,
	);
}
#[cfg(test)]
mod mock {
	use sp_runtime::{testing::H256, BuildStorage};
	type AccountId = u64;
	type Nonce = u32;
	type Block = frame_system::mocking::MockBlock<Test>;
	frame_support::construct_runtime!(
		pub enum Test
		{
			System: frame_system::{Pallet, Call, Config<T>, Storage, Event<T>},
			Baseline: crate::{Pallet, Call, Storage, Event<T>},
		}
	);
	impl frame_system::Config for Test {
		type BaseCallFilter = frame_support::traits::Everything;
		type BlockWeights = ();
		type BlockLength = ();
		type DbWeight = ();
		type RuntimeOrigin = RuntimeOrigin;
		type Nonce = Nonce;
		type RuntimeCall = RuntimeCall;
		type Hash = H256;
		type Hashing = ::sp_runtime::traits::BlakeTwo256;
		type AccountId = AccountId;
		type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
		type Block = Block;
		type RuntimeEvent = RuntimeEvent;
		type BlockHashCount = ();
		type Version = ();
		type PalletInfo = PalletInfo;
		type AccountData = ();
		type OnNewAccount = ();
		type OnKilledAccount = ();
		type SystemWeightInfo = ();
		type SS58Prefix = ();
		type OnSetCode = ();
		type MaxConsumers = frame_support::traits::ConstU32<16>;
	}
	impl crate::Config for Test {
		type RuntimeEvent = RuntimeEvent;
	}
	pub fn new_test_ext() -> sp_io::TestExternalities {
		frame_system::GenesisConfig::<Test>::default().build_storage().unwrap().into()
	}
}