#![cfg(test)]

use super::*;

pub use crate as ghost_claims;
use frame_support::{
    derive_impl, parameter_types,
    traits::{ConstU16, ConstU64, PollStatus, Polling, WithdrawReasons},
};
use frame_system::EnsureRootWithSuccess;
pub use pallet_ranked_collective::{Rank, TallyOf};
use sp_runtime::{traits::Convert, BuildStorage};

pub mod eth_keys {
    use crate::{mock::Test, EcdsaSignature, EthereumAddress};
    use codec::Encode;
    use hex_literal::hex;

    pub fn total_claims() -> u64 {
        10 + 100 + 1000
    }
    pub fn alice_account_id() -> <Test as frame_system::Config>::AccountId {
        69
    }
    pub fn bob_account_id() -> <Test as frame_system::Config>::AccountId {
        1337
    }

    pub fn first_eth_public_known() -> EthereumAddress {
        EthereumAddress(hex!("1A69d2D5568D1878023EeB121a73d33B9116A760"))
    }
    pub fn second_eth_public_known() -> EthereumAddress {
        EthereumAddress(hex!("2f86cfBED3fbc1eCf2989B9aE5fc019a837A9C12"))
    }
    pub fn third_eth_public_known() -> EthereumAddress {
        EthereumAddress(hex!("e83f67361Ac74D42A48E2DAfb6706eb047D8218D"))
    }
    pub fn fourth_eth_public_known() -> EthereumAddress {
        EthereumAddress(hex!("827ee4ad9b259b6fa1390ed60921508c78befd63"))
    }

    fn first_eth_private_key() -> libsecp256k1::SecretKey {
        libsecp256k1::SecretKey::parse(&hex!(
            "01c928771aea942a1e7ac06adf2b73dfbc9a25d9eaa516e3673116af7f345198"
        ))
        .unwrap()
    }
    fn second_eth_private_key() -> libsecp256k1::SecretKey {
        libsecp256k1::SecretKey::parse(&hex!(
            "b19a435901872f817185f7234a1484eae837613f9d10cf21927a23c2d8cb9139"
        ))
        .unwrap()
    }
    fn third_eth_private_key() -> libsecp256k1::SecretKey {
        libsecp256k1::SecretKey::parse(&hex!(
            "d3baf57b74d65719b2dc33f5a464176022d0cc5edbca002234229f3e733875fc"
        ))
        .unwrap()
    }
    fn fourth_eth_private_key() -> libsecp256k1::SecretKey {
        libsecp256k1::SecretKey::parse(&hex!(
            "c4683d566436af6b58b4a59c8f501319226e85b21869bf93d5eeb4596d4791d4"
        ))
        .unwrap()
    }
    fn wrong_eth_private_key() -> libsecp256k1::SecretKey {
        libsecp256k1::SecretKey::parse(&hex!(
            "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
        ))
        .unwrap()
    }

    pub fn first_eth_public_key() -> EthereumAddress {
        crate::secp_utils::eth(&first_eth_private_key())
    }
    pub fn second_eth_public_key() -> EthereumAddress {
        crate::secp_utils::eth(&second_eth_private_key())
    }
    pub fn third_eth_public_key() -> EthereumAddress {
        crate::secp_utils::eth(&third_eth_private_key())
    }
    pub fn fourth_eth_public_key() -> EthereumAddress {
        crate::secp_utils::eth(&fourth_eth_private_key())
    }

    pub fn first_account_id() -> <Test as frame_system::Config>::AccountId {
        crate::secp_utils::into_account_id::<Test, ()>(first_eth_public_key())
    }
    pub fn second_account_id() -> <Test as frame_system::Config>::AccountId {
        crate::secp_utils::into_account_id::<Test, ()>(second_eth_public_key())
    }
    pub fn third_account_id() -> <Test as frame_system::Config>::AccountId {
        crate::secp_utils::into_account_id::<Test, ()>(third_eth_public_key())
    }
    pub fn fourth_account_id() -> <Test as frame_system::Config>::AccountId {
        crate::secp_utils::into_account_id::<Test, ()>(fourth_eth_public_key())
    }

    pub fn first_signature() -> EcdsaSignature {
        crate::secp_utils::sig::<Test, ()>(&first_eth_private_key(), &alice_account_id().encode())
    }
    pub fn second_signature() -> EcdsaSignature {
        crate::secp_utils::sig::<Test, ()>(&second_eth_private_key(), &alice_account_id().encode())
    }
    pub fn third_signature() -> EcdsaSignature {
        crate::secp_utils::sig::<Test, ()>(&third_eth_private_key(), &alice_account_id().encode())
    }
    pub fn fourth_signature() -> EcdsaSignature {
        crate::secp_utils::sig::<Test, ()>(&fourth_eth_private_key(), &bob_account_id().encode())
    }
    pub fn wrong_signature() -> EcdsaSignature {
        crate::secp_utils::sig::<Test, ()>(&wrong_eth_private_key(), &alice_account_id().encode())
    }
}

#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
impl frame_system::Config for Test {
    type RuntimeOrigin = RuntimeOrigin;
    type RuntimeCall = RuntimeCall;
    type Block = Block;
    type RuntimeEvent = RuntimeEvent;
    type AccountData = pallet_balances::AccountData<u64>;
    type MaxConsumers = frame_support::traits::ConstU32<16>;
}

#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
impl pallet_balances::Config for Test {
    type AccountStore = System;
}

parameter_types! {
    pub const MinVestedTransfer: u64 = 1;
    pub UnvestedFundsAllowedWithdrawReasons: WithdrawReasons =
        WithdrawReasons::except(WithdrawReasons::TRANSFER | WithdrawReasons::RESERVE);
}

impl pallet_vesting::Config for Test {
    type RuntimeEvent = RuntimeEvent;
    type Currency = Balances;
    type BlockNumberToBalance = sp_runtime::traits::ConvertInto;
    type MinVestedTransfer = MinVestedTransfer;
    type WeightInfo = ();
    type UnvestedFundsAllowedWithdrawReasons = UnvestedFundsAllowedWithdrawReasons;

    type BlockNumberProvider = System;
    const MAX_VESTING_SCHEDULES: u32 = 28;
}

parameter_types! {
    pub MinRankOfClassDelta: Rank = 1;
}

pub struct MinRankOfClass<Delta>(sp_std::marker::PhantomData<Delta>);
impl<Delta: Get<Rank>> Convert<Class, Rank> for MinRankOfClass<Delta> {
    fn convert(a: Class) -> Rank {
        a.saturating_sub(Delta::get())
    }
}

pub struct TestPolls;
impl Polling<TallyOf<Test>> for TestPolls {
    type Index = u8;
    type Votes = u32;
    type Moment = u64;
    type Class = Class;

    fn classes() -> Vec<Self::Class> {
        unimplemented!()
    }

    fn as_ongoing(_index: u8) -> Option<(TallyOf<Test>, Self::Class)> {
        unimplemented!()
    }

    fn access_poll<R>(
        _index: Self::Index,
        _f: impl FnOnce(PollStatus<&mut TallyOf<Test>, Self::Moment, Self::Class>) -> R,
    ) -> R {
        unimplemented!()
    }

    fn try_access_poll<R>(
        _index: Self::Index,
        _f: impl FnOnce(
            PollStatus<&mut TallyOf<Test>, Self::Moment, Self::Class>,
        ) -> Result<R, DispatchError>,
    ) -> Result<R, DispatchError> {
        unimplemented!()
    }

    #[cfg(feature = "runtime-benchmarks")]
    fn create_ongoing(_class: Self::Class) -> Result<Self::Index, ()> {
        unimplemented!()
    }

    #[cfg(feature = "runtime-benchmarks")]
    fn end_ongoing(_index: Self::Index, _approved: bool) -> Result<(), ()> {
        unimplemented!()
    }
}

impl pallet_ranked_collective::Config for Test {
    type WeightInfo = ();
    type RuntimeEvent = RuntimeEvent;

    type AddOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;
    type RemoveOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;
    type PromoteOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;
    type DemoteOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;
    type ExchangeOrigin = EnsureRootWithSuccess<Self::AccountId, ConstU16<65535>>;

    type Polls = TestPolls;
    type MemberSwappedHandler = ();
    type MinRankOfClass = MinRankOfClass<MinRankOfClassDelta>;
    type VoteWeight = pallet_ranked_collective::Geometric;
    #[cfg(feature = "runtime-benchmarks")]
    type BenchmarkSetup = ();
}

parameter_types! {
    pub Prefix: &'static [u8] = b"AccountId:";
}

impl Config for Test {
    type RuntimeEvent = RuntimeEvent;
    type VestingSchedule = Vesting;
    type BlockNumberProvider = System;
    type MemberSwappedHandler = ();

    type Prefix = Prefix;
    type MaximumWithdrawAmount = ConstU64<200>;
    type VestingBlocks = ConstU32<80>;

    type WeightInfo = ();
}

type Block = frame_system::mocking::MockBlock<Test>;
type Class = Rank;

frame_support::construct_runtime!(
    pub enum Test
    {
        System: frame_system,
        Balances: pallet_balances,
        Vesting: pallet_vesting,
        Club: pallet_ranked_collective,
        Claims: ghost_claims,
    }
);

pub fn new_test_ext() -> sp_io::TestExternalities {
    let mut t = frame_system::GenesisConfig::<Test>::default()
        .build_storage()
        .unwrap();

    pallet_balances::GenesisConfig::<Test> {
        balances: vec![
            (crate::mock::eth_keys::first_account_id(), 10),
            (crate::mock::eth_keys::second_account_id(), 100),
            (crate::mock::eth_keys::third_account_id(), 1000),
        ],
    }
    .assimilate_storage(&mut t)
    .unwrap();

    ghost_claims::GenesisConfig::<Test> {
        total: crate::mock::eth_keys::total_claims(),
        members_and_ranks: vec![
            (crate::mock::eth_keys::second_account_id(), 1),
            (crate::mock::eth_keys::third_account_id(), 3),
        ],
    }
    .assimilate_storage(&mut t)
    .unwrap();

    let mut ext = sp_io::TestExternalities::new(t);
    ext.execute_with(|| {
        System::set_block_number(1);
    });
    ext
}
