#![cfg_attr(not(feature = "std"), no_std)]
use frame_support::traits::{ExecuteBlock, FindAuthor};
use sp_application_crypto::RuntimeAppPublic;
use sp_consensus_aura::{digests::CompatibleDigestItem, Slot};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
pub mod consensus_hook;
pub use consensus_hook::FixedVelocityConsensusHook;
type Aura<T> = pallet_aura::Pallet<T>;
pub use pallet::*;
#[frame_support::pallet]
pub mod pallet {
	use super::*;
	use frame_support::pallet_prelude::*;
	use frame_system::pallet_prelude::*;
	#[pallet::config]
	pub trait Config: pallet_aura::Config + frame_system::Config {}
	#[pallet::pallet]
	pub struct Pallet<T>(_);
	#[pallet::hooks]
	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
		fn on_finalize(_: BlockNumberFor<T>) {
			Authorities::<T>::put(pallet_aura::Authorities::<T>::get());
		}
		fn on_initialize(_: BlockNumberFor<T>) -> Weight {
			Authorities::<T>::get();
			let new_slot = pallet_aura::CurrentSlot::<T>::get();
			let (new_slot, authored) = match SlotInfo::<T>::get() {
				Some((slot, authored)) if slot == new_slot => (slot, authored + 1),
				Some((slot, _)) if slot < new_slot => (new_slot, 1),
				Some(..) => {
					panic!("slot moved backwards")
				},
				None => (new_slot, 1),
			};
			SlotInfo::<T>::put((new_slot, authored));
			T::DbWeight::get().reads_writes(2, 1)
		}
	}
	#[pallet::storage]
	pub(crate) type Authorities<T: Config> = StorageValue<
		_,
		BoundedVec<T::AuthorityId, <T as pallet_aura::Config>::MaxAuthorities>,
		ValueQuery,
	>;
	#[pallet::storage]
	pub(crate) type SlotInfo<T: Config> = StorageValue<_, (Slot, u32), OptionQuery>;
	#[pallet::genesis_config]
	#[derive(frame_support::DefaultNoBound)]
	pub struct GenesisConfig<T: Config> {
		#[serde(skip)]
		pub _config: sp_std::marker::PhantomData<T>,
	}
	#[pallet::genesis_build]
	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
		fn build(&self) {
			let authorities = pallet_aura::Authorities::<T>::get();
			Authorities::<T>::put(authorities);
		}
	}
}
pub struct BlockExecutor<T, I>(sp_std::marker::PhantomData<(T, I)>);
impl<Block, T, I> ExecuteBlock<Block> for BlockExecutor<T, I>
where
	Block: BlockT,
	T: Config,
	I: ExecuteBlock<Block>,
{
	fn execute_block(block: Block) {
		let (mut header, extrinsics) = block.deconstruct();
		let authorities = Authorities::<T>::get();
		let mut seal = None;
		header.digest_mut().logs.retain(|s| {
			let s =
				CompatibleDigestItem::<<T::AuthorityId as RuntimeAppPublic>::Signature>::as_aura_seal(s);
			match (s, seal.is_some()) {
				(Some(_), true) => panic!("Found multiple AuRa seal digests"),
				(None, _) => true,
				(Some(s), false) => {
					seal = Some(s);
					false
				},
			}
		});
		let seal = seal.expect("Could not find an AuRa seal digest!");
		let author = Aura::<T>::find_author(
			header.digest().logs().iter().filter_map(|d| d.as_pre_runtime()),
		)
		.expect("Could not find AuRa author index!");
		let pre_hash = header.hash();
		if !authorities
			.get(author as usize)
			.unwrap_or_else(|| {
				panic!("Invalid AuRa author index {} for authorities: {:?}", author, authorities)
			})
			.verify(&pre_hash, &seal)
		{
			panic!("Invalid AuRa seal");
		}
		I::execute_block(Block::new(header, extrinsics));
	}
}