use super::*;
use frame_support::{
    parameter_types,
    traits::{
        tokens::pay::PayFromAccount, EitherOf, EitherOfDiverse, MapSuccess, OriginTrait,
        TryWithMorphedArg,
    },
};
use frame_system::EnsureRootWithSuccess;
use pallet_ranked_collective::EnsureOfRank;
use sp_core::{ConstU128, ConstU32};
use sp_runtime::traits::{ConstU16, TakeFirst};

use crate::{
    AccountId,
    Balance,
    Balances,
    Preimage,
    Runtime,
    // weights,
    RuntimeCall,
    RuntimeEvent,
    Scheduler,
    TreasuryAccount,
    CSPR,
    DAYS,
};

mod origins;
pub use origins::{
    pallet_cult_origins, CultTreasurySpender, Degens, EnsureCanPromoteTo, EnsureCanRetainAt,
    EnsureCult, Geniuses, Ghosts, Skeletons, ToVoice, Zombies,
};

mod tracks;
pub use tracks::TracksInfo;

pub mod ranks {
    use pallet_ranked_collective::Rank;

    pub const LEVEL_1: Rank = 1;
    pub const LEVEL_2: Rank = 2;
    pub const LEVEL_3: Rank = 3;
    pub const LEVEL_4: Rank = 4;
    pub const LEVEL_5: Rank = 5;
}

impl pallet_cult_origins::Config for Runtime {}

parameter_types! {
    pub const VoteLockingPeriod: BlockNumber = prod_or_fast!(7 * DAYS, 1);
}

impl pallet_whitelist::Config for Runtime {
    type RuntimeCall = RuntimeCall;
    type RuntimeEvent = RuntimeEvent;
    type WhitelistOrigin =
        EitherOfDiverse<EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>, Skeletons>;
    type DispatchWhitelistedOrigin =
        EitherOf<EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>, Geniuses>;
    type Preimages = Preimage;
    type WeightInfo = weights::pallet_whitelist::WeightInfo<Runtime>;
}

parameter_types! {
    pub const AlarmInterval: BlockNumber = 1;
    pub const UndecidingTimeout: BlockNumber = 7 * DAYS;
    pub const SubmissionDeposit: Balance = CSPR;
}

pub type CultReferendaInstance = pallet_referenda::Instance1;
impl pallet_referenda::Config<CultReferendaInstance> for Runtime {
    type WeightInfo = weights::pallet_referenda::WeightInfo<Runtime>;
    type RuntimeCall = RuntimeCall;
    type RuntimeEvent = RuntimeEvent;
    type Scheduler = Scheduler;
    type Currency = Balances;
    type SubmitOrigin = EitherOf<
        pallet_ranked_collective::EnsureMember<Runtime, CultCollectiveInstance, 3>,
        MapSuccess<
            TryWithMorphedArg<
                RuntimeOrigin,
                <RuntimeOrigin as OriginTrait>::PalletsOrigin,
                ToVoice,
                EnsureOfRank<Runtime, CultCollectiveInstance>,
                (AccountId, u16),
            >,
            TakeFirst,
        >,
    >;
    type CancelOrigin = Skeletons;
    type KillOrigin = Ghosts;
    type Slash = Treasury;
    type Votes = pallet_ranked_collective::Votes;
    type Tally = pallet_ranked_collective::TallyOf<Runtime, CultCollectiveInstance>;
    type SubmissionDeposit = SubmissionDeposit;
    type MaxQueued = ConstU32<100>;
    type UndecidingTimeout = UndecidingTimeout;
    type AlarmInterval = AlarmInterval;
    type Tracks = TracksInfo;
    type Preimages = Preimage;
}

pub type CultCollectiveInstance = pallet_ranked_collective::Instance1;
impl pallet_ranked_collective::Config<CultCollectiveInstance> for Runtime {
    type WeightInfo = weights::pallet_ranked_collective::WeightInfo<Runtime>;
    type RuntimeEvent = RuntimeEvent;

    #[cfg(not(feature = "runtime-benchmarks"))]
    type PromoteOrigin = frame_system::EnsureNever<pallet_ranked_collective::Rank>;
    #[cfg(feature = "runtime-benchmarks")]
    type PromoteOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;

    #[cfg(not(feature = "runtime-benchmarks"))]
    type AddOrigin = frame_system::EnsureNever<pallet_ranked_collective::Rank>;
    #[cfg(feature = "runtime-benchmarks")]
    type AddOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;

    #[cfg(not(feature = "runtime-benchmarks"))]
    type RemoveOrigin = frame_system::EnsureNever<pallet_ranked_collective::Rank>;
    #[cfg(feature = "runtime-benchmarks")]
    type RemoveOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;

    type DemoteOrigin = EitherOf<EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>, Ghosts>;
    type ExchangeOrigin = EitherOf<EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>, Ghosts>;

    type Polls = CultReferenda;
    type MinRankOfClass = tracks::MinRankOfClass;
    type MemberSwappedHandler = (crate::CultCore, crate::CultSalary);
    type VoteWeight = pallet_ranked_collective::Geometric;

    #[cfg(feature = "runtime-benchmarks")]
    type BenchmarkSetup = (crate::CultCore, crate::CultSalary);
}

pub type CultCoreInstance = pallet_core_fellowship::Instance1;
impl pallet_core_fellowship::Config<CultCoreInstance> for Runtime {
    type WeightInfo = weights::pallet_core_fellowship::WeightInfo<Runtime>;
    type RuntimeEvent = RuntimeEvent;
    type Members = pallet_ranked_collective::Pallet<Runtime, CultCollectiveInstance>;
    type Balance = Balance;

    type ParamsOrigin =
        EitherOf<EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>, Skeletons>;
    type InductOrigin = EitherOf<EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>, Zombies>;
    type ApproveOrigin =
        EitherOf<EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>, EnsureCanRetainAt>;
    type PromoteOrigin =
        EitherOf<EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>, EnsureCanPromoteTo>;

    type EvidenceSize = ConstU32<65536>;
}

pub type CultSalaryInstance = pallet_salary::Instance1;
impl pallet_salary::Config<CultSalaryInstance> for Runtime {
    type WeightInfo = weights::pallet_salary::WeightInfo<Runtime>;
    type RuntimeEvent = RuntimeEvent;

    type Paymaster = PayFromAccount<Balances, TreasuryAccount>;
    type Members = pallet_ranked_collective::Pallet<Runtime, CultCollectiveInstance>;

    #[cfg(not(feature = "runtime-benchmarks"))]
    type Salary = pallet_core_fellowship::Pallet<Runtime, CultCoreInstance>;
    #[cfg(feature = "runtime-benchmarks")]
    type Salary = frame_support::traits::tokens::ConvertRank<
        crate::impls::benchmarks::RankToSalary<Balances>,
    >;

    type RegistrationPeriod = ConstU32<{ 15 * DAYS }>;
    type PayoutPeriod = ConstU32<{ 15 * DAYS }>;
    type Budget = ConstU128<{ 100 * CSPR }>;
}
