#![cfg(feature = "runtime-benchmarks")]
#![allow(unused_assignments)] use super::{mock_helpers::*, Pallet as MessageQueue, *};
use frame_benchmarking::v2::*;
use frame_support::traits::Get;
use frame_system::RawOrigin;
use sp_std::prelude::*;
#[benchmarks(
	where
		<<T as Config>::MessageProcessor as ProcessMessage>::Origin: From<u32> + PartialEq,
		<T as Config>::Size: From<u32>,
		)]
mod benchmarks {
	use super::*;
	#[benchmark]
	fn ready_ring_knit() {
		let mid: MessageOriginOf<T> = 1.into();
		build_ring::<T>(&[0.into(), mid.clone(), 2.into()]);
		unknit::<T>(&mid);
		assert_ring::<T>(&[0.into(), 2.into()]);
		let mut neighbours = None;
		#[block]
		{
			neighbours = MessageQueue::<T>::ready_ring_knit(&mid).ok();
		}
		BookStateFor::<T>::mutate(&mid, |b| b.ready_neighbours = neighbours);
		assert_ring::<T>(&[0.into(), 2.into(), mid]);
	}
	#[benchmark]
	fn ready_ring_unknit() {
		build_ring::<T>(&[0.into(), 1.into(), 2.into()]);
		assert_ring::<T>(&[0.into(), 1.into(), 2.into()]);
		let o: MessageOriginOf<T> = 0.into();
		let neighbours = BookStateFor::<T>::get(&o).ready_neighbours.unwrap();
		#[block]
		{
			MessageQueue::<T>::ready_ring_unknit(&o, neighbours);
		}
		assert_ring::<T>(&[1.into(), 2.into()]);
	}
	#[benchmark]
	fn service_queue_base() {
		#[block]
		{
			MessageQueue::<T>::service_queue(0.into(), &mut WeightMeter::max_limit(), Weight::MAX);
		}
	}
	#[benchmark]
	fn service_page_base_completion() {
		let origin: MessageOriginOf<T> = 0.into();
		let page = PageOf::<T>::default();
		Pages::<T>::insert(&origin, 0, &page);
		let mut book_state = single_page_book::<T>();
		let mut meter = WeightMeter::max_limit();
		let limit = Weight::MAX;
		#[block]
		{
			MessageQueue::<T>::service_page(&origin, &mut book_state, &mut meter, limit);
		}
	}
	#[benchmark]
	fn service_page_base_no_completion() {
		let origin: MessageOriginOf<T> = 0.into();
		let mut page = PageOf::<T>::default();
		page.first = 1.into();
		page.remaining = 1.into();
		Pages::<T>::insert(&origin, 0, &page);
		let mut book_state = single_page_book::<T>();
		let mut meter = WeightMeter::max_limit();
		let limit = Weight::MAX;
		#[block]
		{
			MessageQueue::<T>::service_page(&origin, &mut book_state, &mut meter, limit);
		}
	}
	#[benchmark]
	fn service_page_item() {
		let msg = vec![1u8; MaxMessageLenOf::<T>::get() as usize];
		let mut page = page::<T>(&msg.clone());
		let mut book = book_for::<T>(&page);
		assert!(page.peek_first().is_some(), "There is one message");
		let mut weight = WeightMeter::max_limit();
		#[block]
		{
			let status = MessageQueue::<T>::service_page_item(
				&0u32.into(),
				0,
				&mut book,
				&mut page,
				&mut weight,
				Weight::MAX,
			);
			assert_eq!(status, ItemExecutionStatus::Executed(true));
		}
		assert_last_event::<T>(
			Event::Processed {
				id: sp_io::hashing::blake2_256(&msg),
				origin: 0.into(),
				weight_used: 1.into_weight(),
				success: true,
			}
			.into(),
		);
		let (_, processed, _) = page.peek_index(0).unwrap();
		assert!(processed);
		assert_eq!(book.message_count, 0);
	}
	#[benchmark]
	fn bump_service_head() {
		setup_bump_service_head::<T>(0.into(), 10.into());
		let mut weight = WeightMeter::max_limit();
		#[block]
		{
			MessageQueue::<T>::bump_service_head(&mut weight);
		}
		assert_eq!(ServiceHead::<T>::get().unwrap(), 10u32.into());
		assert_eq!(weight.consumed(), T::WeightInfo::bump_service_head());
	}
	#[benchmark]
	fn reap_page() {
		let origin: MessageOriginOf<T> = 0.into();
		let mut book = single_page_book::<T>();
		let (page, msgs) = full_page::<T>();
		for p in 0..T::MaxStale::get() * T::MaxStale::get() {
			if p == 0 {
				Pages::<T>::insert(&origin, p, &page);
			}
			book.end += 1;
			book.count += 1;
			book.message_count += msgs as u64;
			book.size += page.remaining_size.into() as u64;
		}
		book.begin = book.end - T::MaxStale::get();
		BookStateFor::<T>::insert(&origin, &book);
		assert!(Pages::<T>::contains_key(&origin, 0));
		#[extrinsic_call]
		_(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0);
		assert_last_event::<T>(Event::PageReaped { origin: 0.into(), index: 0 }.into());
		assert!(!Pages::<T>::contains_key(&origin, 0));
	}
	#[benchmark]
	fn execute_overweight_page_removed() {
		let origin: MessageOriginOf<T> = 0.into();
		let (mut page, msgs) = full_page::<T>();
		for _ in 1..msgs {
			page.skip_first(true);
		}
		page.skip_first(false);
		let book = book_for::<T>(&page);
		Pages::<T>::insert(&origin, 0, &page);
		BookStateFor::<T>::insert(&origin, &book);
		#[block]
		{
			MessageQueue::<T>::execute_overweight(
				RawOrigin::Signed(whitelisted_caller()).into(),
				0u32.into(),
				0u32,
				((msgs - 1) as u32).into(),
				Weight::MAX,
			)
			.unwrap();
		}
		assert_last_event::<T>(
			Event::Processed {
				id: sp_io::hashing::blake2_256(&((msgs - 1) as u32).encode()),
				origin: 0.into(),
				weight_used: Weight::from_parts(1, 1),
				success: true,
			}
			.into(),
		);
		assert!(!Pages::<T>::contains_key(&origin, 0), "Page must be removed");
	}
	#[benchmark]
	fn execute_overweight_page_updated() {
		let origin: MessageOriginOf<T> = 0.into();
		let (mut page, msgs) = full_page::<T>();
		for _ in 0..msgs {
			page.skip_first(false);
		}
		let book = book_for::<T>(&page);
		Pages::<T>::insert(&origin, 0, &page);
		BookStateFor::<T>::insert(&origin, &book);
		#[block]
		{
			MessageQueue::<T>::execute_overweight(
				RawOrigin::Signed(whitelisted_caller()).into(),
				0u32.into(),
				0u32,
				((msgs - 1) as u32).into(),
				Weight::MAX,
			)
			.unwrap();
		}
		assert_last_event::<T>(
			Event::Processed {
				id: sp_io::hashing::blake2_256(&((msgs - 1) as u32).encode()),
				origin: 0.into(),
				weight_used: Weight::from_parts(1, 1),
				success: true,
			}
			.into(),
		);
		assert!(Pages::<T>::contains_key(&origin, 0), "Page must be updated");
	}
	impl_benchmark_test_suite! {
		MessageQueue,
		crate::mock::new_test_ext::<crate::integration_test::Test>(),
		crate::integration_test::Test
	}
}