use cfg_traits::PreConditions;
use frame_support::traits::{
	BalanceStatus, Currency, ExistenceRequirement, LockIdentifier, LockableCurrency,
	ReservableCurrency, SignedImbalance, WithdrawReasons,
};
use super::*;
pub enum CurrencyEffects<AccountId, Balance> {
	CanSlash(AccountId, Balance, bool),
	EnsureCanWithdraw(AccountId, Balance, WithdrawReasons, Balance, DispatchResult),
	Transfer(AccountId, AccountId, Balance, ExistenceRequirement),
	Withdraw(AccountId, Balance, WithdrawReasons, ExistenceRequirement),
	DepositIntoExisting(AccountId, Balance),
}
impl<T: Config> Currency<T::AccountId> for Pallet<T> {
	type Balance = T::Balance;
	type NegativeImbalance = <T::NativeFungible as Currency<T::AccountId>>::NegativeImbalance;
	type PositiveImbalance = <T::NativeFungible as Currency<T::AccountId>>::PositiveImbalance;
	fn total_balance(who: &T::AccountId) -> Self::Balance {
		<T::NativeFungible as Currency<T::AccountId>>::total_balance(who)
	}
	fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool {
		T::PreCurrency::check(CurrencyEffects::CanSlash(
			who.clone(),
			value,
			<T::NativeFungible as Currency<T::AccountId>>::can_slash(who, value),
		))
	}
	fn total_issuance() -> Self::Balance {
		<T::NativeFungible as Currency<T::AccountId>>::total_issuance()
	}
	fn minimum_balance() -> Self::Balance {
		<T::NativeFungible as Currency<T::AccountId>>::minimum_balance()
	}
	fn burn(amount: Self::Balance) -> Self::PositiveImbalance {
		<T::NativeFungible as Currency<T::AccountId>>::burn(amount)
	}
	fn issue(amount: Self::Balance) -> Self::NegativeImbalance {
		<T::NativeFungible as Currency<T::AccountId>>::issue(amount)
	}
	fn free_balance(who: &T::AccountId) -> Self::Balance {
		<T::NativeFungible as Currency<T::AccountId>>::free_balance(who)
	}
	fn ensure_can_withdraw(
		who: &T::AccountId,
		_amount: Self::Balance,
		reasons: WithdrawReasons,
		new_balance: Self::Balance,
	) -> DispatchResult {
		ensure!(
			T::PreCurrency::check(CurrencyEffects::EnsureCanWithdraw(
				who.clone(),
				_amount,
				reasons,
				new_balance,
				<T::NativeFungible as Currency<T::AccountId>>::ensure_can_withdraw(
					who,
					_amount,
					reasons,
					new_balance,
				)
			)),
			Error::<T>::PreConditionsNotMet
		);
		Ok(())
	}
	fn transfer(
		source: &T::AccountId,
		dest: &T::AccountId,
		value: Self::Balance,
		existence_requirement: ExistenceRequirement,
	) -> DispatchResult {
		ensure!(
			T::PreCurrency::check(CurrencyEffects::Transfer(
				source.clone(),
				dest.clone(),
				value,
				existence_requirement
			)),
			Error::<T>::PreConditionsNotMet
		);
		<T::NativeFungible as Currency<T::AccountId>>::transfer(
			source,
			dest,
			value,
			existence_requirement,
		)
	}
	fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) {
		<T::NativeFungible as Currency<T::AccountId>>::slash(who, value)
	}
	fn deposit_into_existing(
		who: &T::AccountId,
		value: Self::Balance,
	) -> Result<Self::PositiveImbalance, DispatchError> {
		ensure!(
			T::PreCurrency::check(CurrencyEffects::DepositIntoExisting(who.clone(), value,)),
			Error::<T>::PreConditionsNotMet
		);
		<T::NativeFungible as Currency<T::AccountId>>::deposit_into_existing(who, value)
	}
	fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance {
		<T::NativeFungible as Currency<T::AccountId>>::deposit_creating(who, value)
	}
	fn withdraw(
		who: &T::AccountId,
		value: Self::Balance,
		reasons: WithdrawReasons,
		liveness: ExistenceRequirement,
	) -> Result<Self::NegativeImbalance, DispatchError> {
		ensure!(
			T::PreCurrency::check(CurrencyEffects::Withdraw(
				who.clone(),
				value,
				reasons,
				liveness
			)),
			Error::<T>::PreConditionsNotMet
		);
		<T::NativeFungible as Currency<T::AccountId>>::withdraw(who, value, reasons, liveness)
	}
	fn make_free_balance_be(
		who: &T::AccountId,
		balance: Self::Balance,
	) -> SignedImbalance<Self::Balance, Self::PositiveImbalance> {
		<T::NativeFungible as Currency<T::AccountId>>::make_free_balance_be(who, balance)
	}
}
pub enum ReservableCurrencyEffects<AccountId, Balance> {
	CanReserve(AccountId, Balance, bool),
	Reserve(AccountId, Balance),
	RepatriateReserved(AccountId, AccountId, Balance, BalanceStatus),
}
impl<T: Config> ReservableCurrency<T::AccountId> for Pallet<T> {
	fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool {
		T::PreReservableCurrency::check(ReservableCurrencyEffects::CanReserve(
			who.clone(),
			value,
			<T::NativeFungible as ReservableCurrency<T::AccountId>>::can_reserve(who, value),
		))
	}
	fn slash_reserved(
		who: &T::AccountId,
		value: Self::Balance,
	) -> (Self::NegativeImbalance, Self::Balance) {
		<T::NativeFungible as ReservableCurrency<T::AccountId>>::slash_reserved(who, value)
	}
	fn reserved_balance(who: &T::AccountId) -> Self::Balance {
		<T::NativeFungible as ReservableCurrency<T::AccountId>>::reserved_balance(who)
	}
	fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult {
		ensure!(
			T::PreReservableCurrency::check(ReservableCurrencyEffects::Reserve(who.clone(), value)),
			Error::<T>::PreConditionsNotMet
		);
		<T::NativeFungible as ReservableCurrency<T::AccountId>>::reserve(who, value)
	}
	fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance {
		<T::NativeFungible as ReservableCurrency<T::AccountId>>::unreserve(who, value)
	}
	fn repatriate_reserved(
		slashed: &T::AccountId,
		beneficiary: &T::AccountId,
		value: Self::Balance,
		status: BalanceStatus,
	) -> Result<Self::Balance, DispatchError> {
		ensure!(
			T::PreReservableCurrency::check(ReservableCurrencyEffects::RepatriateReserved(
				slashed.clone(),
				beneficiary.clone(),
				value,
				status
			)),
			Error::<T>::PreConditionsNotMet
		);
		<T::NativeFungible as ReservableCurrency<T::AccountId>>::repatriate_reserved(
			slashed,
			beneficiary,
			value,
			status,
		)
	}
}
impl<T: Config> LockableCurrency<T::AccountId> for Pallet<T> {
	type MaxLocks = <T::NativeFungible as LockableCurrency<T::AccountId>>::MaxLocks;
	type Moment = <T::NativeFungible as LockableCurrency<T::AccountId>>::Moment;
	fn set_lock(
		id: LockIdentifier,
		who: &T::AccountId,
		amount: Self::Balance,
		reasons: WithdrawReasons,
	) {
		<T::NativeFungible as LockableCurrency<T::AccountId>>::set_lock(id, who, amount, reasons)
	}
	fn extend_lock(
		id: LockIdentifier,
		who: &T::AccountId,
		amount: Self::Balance,
		reasons: WithdrawReasons,
	) {
		<T::NativeFungible as LockableCurrency<T::AccountId>>::extend_lock(id, who, amount, reasons)
	}
	fn remove_lock(id: LockIdentifier, who: &T::AccountId) {
		<T::NativeFungible as LockableCurrency<T::AccountId>>::remove_lock(id, who)
	}
}