use super::{entities::*, AccountId, IdtyIndex};
use frame_support::{
    pallet_prelude::Weight,
    traits::{Imbalance, UnfilteredDispatchable},
};
use pallet_smith_members::SmithRemovalReason;
use sp_core::Get;
pub struct OnNewSessionHandler<Runtime>(core::marker::PhantomData<Runtime>);
impl<Runtime> pallet_authority_members::traits::OnNewSession for OnNewSessionHandler<Runtime>
where
    Runtime: pallet_provide_randomness::Config + pallet_smith_members::Config,
{
    fn on_new_session(index: sp_staking::SessionIndex) {
        pallet_provide_randomness::Pallet::<Runtime>::on_new_epoch();
        pallet_smith_members::Pallet::<Runtime>::on_new_session(index);
    }
}
pub struct OnNewIdtyHandler<Runtime>(core::marker::PhantomData<Runtime>);
impl<Runtime: pallet_duniter_wot::Config + pallet_quota::Config>
    pallet_identity::traits::OnNewIdty<Runtime> for OnNewIdtyHandler<Runtime>
{
    fn on_created(idty_index: &IdtyIndex, creator: &IdtyIndex) {
        pallet_duniter_wot::Pallet::<Runtime>::on_created(idty_index, creator);
        pallet_quota::Pallet::<Runtime>::on_created(idty_index, creator);
    }
}
pub struct OnRemoveIdtyHandler<Runtime>(core::marker::PhantomData<Runtime>);
impl<Runtime: pallet_duniter_wot::Config + pallet_quota::Config>
    pallet_identity::traits::OnRemoveIdty<Runtime> for OnRemoveIdtyHandler<Runtime>
{
    fn on_removed(idty_index: &IdtyIndex) -> Weight {
        let mut weight = pallet_duniter_wot::Pallet::<Runtime>::on_removed(idty_index);
        weight += pallet_quota::Pallet::<Runtime>::on_removed(idty_index);
        weight
    }
    fn on_revoked(idty_index: &IdtyIndex) -> Weight {
        let mut weight = pallet_duniter_wot::Pallet::<Runtime>::on_revoked(idty_index);
        weight += pallet_quota::Pallet::<Runtime>::on_revoked(idty_index);
        weight
    }
}
pub struct OnNewMembershipHandler<Runtime>(core::marker::PhantomData<Runtime>);
impl<
        Runtime: frame_system::Config<AccountId = AccountId>
            + pallet_identity::Config<IdtyData = IdtyData, IdtyIndex = IdtyIndex>
            + pallet_duniter_wot::Config
            + pallet_universal_dividend::Config,
    > sp_membership::traits::OnNewMembership<IdtyIndex> for OnNewMembershipHandler<Runtime>
{
    fn on_created(idty_index: &IdtyIndex) {
        pallet_duniter_wot::Pallet::<Runtime>::on_created(idty_index);
        pallet_identity::Identities::<Runtime>::mutate_exists(idty_index, |idty_val_opt| {
            if let Some(ref mut idty_val) = idty_val_opt {
                idty_val.data = IdtyData {
                    first_eligible_ud:
                        pallet_universal_dividend::Pallet::<Runtime>::init_first_eligible_ud(),
                };
            }
        });
    }
    fn on_renewed(_idty_index: &IdtyIndex) {}
}
pub struct OnRemoveMembershipHandler<Runtime>(core::marker::PhantomData<Runtime>);
impl<
        Runtime: frame_system::Config
            + pallet_identity::Config<IdtyData = IdtyData, IdtyIndex = IdtyIndex>
            + pallet_smith_members::Config<IdtyIndex = IdtyIndex>
            + pallet_duniter_wot::Config
            + pallet_universal_dividend::Config,
    > sp_membership::traits::OnRemoveMembership<IdtyIndex> for OnRemoveMembershipHandler<Runtime>
{
    fn on_removed(idty_index: &IdtyIndex) -> Weight {
        let mut weight = pallet_duniter_wot::Pallet::<Runtime>::on_removed(idty_index);
        let mut add_db_reads_writes = |reads, writes| {
            weight += Runtime::DbWeight::get().reads_writes(reads, writes);
        };
        if let Some(idty_value) = pallet_identity::Identities::<Runtime>::get(idty_index) {
            add_db_reads_writes(1, 0);
            if let Some(first_ud_index) = idty_value.data.first_eligible_ud.into() {
                add_db_reads_writes(1, 0);
                weight += pallet_universal_dividend::Pallet::<Runtime>::on_removed_member(
                    first_ud_index,
                    &idty_value.owner_key,
                );
            }
        }
        weight.saturating_add(
            pallet_smith_members::Pallet::<Runtime>::on_removed_wot_member(*idty_index),
        )
    }
}
pub struct TreasurySpendFunds<Runtime>(core::marker::PhantomData<Runtime>);
impl<Runtime> pallet_treasury::SpendFunds<Runtime> for TreasurySpendFunds<Runtime>
where
    Runtime: pallet_treasury::Config,
{
    fn spend_funds(
        _budget_remaining: &mut pallet_treasury::BalanceOf<Runtime>,
        _imbalance: &mut pallet_treasury::PositiveImbalanceOf<Runtime>,
        _total_weight: &mut Weight,
        missed_any: &mut bool,
    ) {
        *missed_any = true;
    }
}
pub struct OnSmithDeletedHandler<Runtime>(core::marker::PhantomData<Runtime>);
impl<Runtime> pallet_smith_members::traits::OnSmithDelete<Runtime::MemberId>
    for OnSmithDeletedHandler<Runtime>
where
    Runtime: pallet_authority_members::Config,
{
    fn on_smith_delete(idty_index: Runtime::MemberId, _reason: SmithRemovalReason) {
        let call = pallet_authority_members::Call::<Runtime>::remove_member {
            member_id: idty_index,
        };
        if let Err(e) = call.dispatch_bypass_filter(frame_system::Origin::<Runtime>::Root.into()) {
            #[cfg(feature = "std")]
            println!("faid to remove member: {:?}", e)
        }
    }
}
pub struct OwnerKeyChangePermissionHandler<Runtime>(core::marker::PhantomData<Runtime>);
impl<
        Runtime: frame_system::Config
            + pallet_identity::Config<IdtyIndex = IdtyIndex>
            + pallet_authority_members::Config<MemberId = IdtyIndex>,
    > pallet_identity::traits::CheckKeyChangeAllowed<Runtime>
    for OwnerKeyChangePermissionHandler<Runtime>
{
    fn check_allowed(idty_index: &IdtyIndex) -> bool {
        !pallet_authority_members::Pallet::<Runtime>::online().contains(idty_index)
    }
}
pub struct HandleFees<TreasuryAccount, Balances>(
    frame_support::pallet_prelude::PhantomData<TreasuryAccount>,
    frame_support::pallet_prelude::PhantomData<Balances>,
);
type CreditOf<Balances> = frame_support::traits::tokens::fungible::Credit<AccountId, Balances>;
impl<TreasuryAccount, Balances> frame_support::traits::OnUnbalanced<CreditOf<Balances>>
    for HandleFees<TreasuryAccount, Balances>
where
    TreasuryAccount: Get<AccountId>,
    Balances: frame_support::traits::fungible::Balanced<AccountId>,
{
    fn on_nonzero_unbalanced(amount: CreditOf<Balances>) {
        let _ = Balances::deposit(
            &TreasuryAccount::get(),
            amount.peek(),
            frame_support::traits::tokens::Precision::Exact,
        );
    }
}