#![cfg(test)]

use mock::*;
use super::*;
use hex_literal::hex;

use sp_runtime::AccountId32;
use frame_support::traits::Get;
use frame_support::assert_ok;
use libsecp256k1::{Message, SecretKey, PublicKey};

use ghost_helpers::merkle_tree::{generate_tree, generate_proof};

#[derive(Clone)]
pub struct NewTestUser {
    pub substrate_account: AccountId32,
    pub evm_secret: SecretKey,
    pub evm_address: EvmAddress,
    pub allowed_shares: u128,
    pub token_id: u128,
}

#[test]
fn claim_happy_path() {
    new_test_ext().execute_with(|| {
        let network_id = 69u64;

        let minimum_donation_u128 = <<Test as crate::Config>::MinimumDonation as Get<u128>>::get();
        let minimum_donation: BalanceOf<Test> = minimum_donation_u128.unique_saturated_into();

        let secret_key_1 = SecretKey::parse(&[1u8; 32]).unwrap();
        let public_key_1 = PublicKey::from_secret_key(&secret_key_1);
        let pubkey_raw_1 = &public_key_1.serialize()[1..65];
        let pubkey_hash_1 = SubstrateKeccakHasher::hash(pubkey_raw_1);

        let mut evm_addr_bytes_1 = [0u8; 20];
        evm_addr_bytes_1.copy_from_slice(&pubkey_hash_1.as_ref()[12..32]);

        let user_1 = NewTestUser {
            substrate_account: AccountId32::new([11u8; 32]),
            evm_secret: secret_key_1,
            evm_address: EvmAddress::from(evm_addr_bytes_1),
            allowed_shares: 420u128,
            token_id: 0,
        };

        let secret_key_2 = SecretKey::parse(&[2u8; 32]).unwrap();
        let public_key_2 = PublicKey::from_secret_key(&secret_key_2);
        let pubkey_raw_2 = &public_key_2.serialize()[1..65];
        let pubkey_hash_2 = SubstrateKeccakHasher::hash(pubkey_raw_2);

        let mut evm_addr_bytes_2 = [0u8; 20];
        evm_addr_bytes_2.copy_from_slice(&pubkey_hash_2.as_ref()[12..32]);

        let user_2 = NewTestUser {
            substrate_account: AccountId32::new([22u8; 32]),
            evm_secret: secret_key_2,
            evm_address: EvmAddress::from(evm_addr_bytes_2),
            allowed_shares: 1337u128,
            token_id: 1,
        };

        let secret_key_3 = SecretKey::parse(&[3u8; 32]).unwrap();
        let public_key_3 = PublicKey::from_secret_key(&secret_key_3);
        let pubkey_raw_3 = &public_key_3.serialize()[1..65];
        let pubkey_hash_3 = SubstrateKeccakHasher::hash(pubkey_raw_3);

        let mut evm_addr_bytes_3 = [0u8; 20];
        evm_addr_bytes_3.copy_from_slice(&pubkey_hash_3.as_ref()[12..32]);

        let user_3 = NewTestUser {
            substrate_account: AccountId32::new([69u8; 32]),
            evm_secret: secret_key_3,
            evm_address: EvmAddress::from(evm_addr_bytes_3),
            allowed_shares: 69u128,
            token_id: 2,
        };

        let users = vec![user_1.clone(), user_2.clone(), user_3.clone()];
        let max_index = 2u8;

        let raw_values_complex = vec![(0u8, &user_1), (1u8, &user_2), (2u8, &user_3)];
        let merkle_tree = generate_tree::<SubstrateKeccakHasher, u8, _, _, _>(
            max_index,
            raw_values_complex,

            |item: (u8, &NewTestUser)| -> Result<(usize, Vec<u8>), ()> {
                let (index, u) = item;

                let tmp_package = ClaimPackage {
                    shares: u.allowed_shares,
                    merkle_proof: Default::default(),
                    token_id: u.token_id,
                    index: index as u32
                };

                Ok((index as usize, tmp_package.get_preimage(&u.evm_address, network_id).to_vec()))
            }
        ).unwrap();

        let merkle_root = *merkle_tree.last().unwrap();

        let network_state = NetworkShare::new(1_000_000, merkle_root, 2);
        let global_state = ShareState::new(1_000_000);
        NetworkShares::<Test>::insert(&network_id, network_state);
        GlobalShares::<Test>::put(global_state);

        let mut global_activated = GlobalShares::<Test>::get().claimed_shares;
        let mut network_activated = NetworkShares::<Test>::get(&network_id)
            .claimed_shares();

        for (slot_index, current_user) in users.iter().enumerate() {
            MemberShares::<Test>::insert(&current_user.substrate_account, MemberShare {
                locked_balance: minimum_donation,
                initiated_network: None,
                activated_shares: 0,
            });

            let proof = generate_proof::<SubstrateKeccakHasher, u8>(&merkle_tree, max_index, slot_index as u8);

            let claim_package = ClaimPackage {
                shares: current_user.allowed_shares,
                merkle_proof: BoundedVec::try_from(proof).unwrap(),
                token_id: current_user.token_id,
                index: slot_index as u32,
            };

            let preimage = claim_package.get_preimage(&current_user.evm_address, network_id);
            let preimage_hash = SubstrateKeccakHasher::hash(&preimage);

            let message_string_bytes = Governance::ethereum_signable_message(
                &current_user.substrate_account,
                &network_id,
                &preimage_hash
            );

            let message_hash = SubstrateKeccakHasher::hash(&message_string_bytes);
            let mut message_bytes = [0u8; 32];
            message_bytes.copy_from_slice(message_hash.as_ref());

            let msg_to_sign = Message::parse(&message_bytes);

            let (sig, recovery_id) = libsecp256k1::sign(&msg_to_sign, &current_user.evm_secret);

            let mut signature_raw = [0u8; 65];
            signature_raw[0..32].copy_from_slice(&sig.r.b32());
            signature_raw[32..64].copy_from_slice(&sig.s.b32());
            signature_raw[64] = recovery_id.serialize();

            let evm_signature = EvmSignature::from(signature_raw);

            assert_ok!(Governance::claim(
                RuntimeOrigin::signed(current_user.substrate_account.clone()),
                network_id,
                claim_package,
                current_user.evm_address.clone(),
                evm_signature
            ));

            let updated_share = MemberShares::<Test>::get(&current_user.substrate_account);
            assert_eq!(updated_share.activated_shares, current_user.allowed_shares);

            global_activated += current_user.allowed_shares;
            network_activated += current_user.allowed_shares;
        }

        assert_eq!(global_activated, GlobalShares::<Test>::get().claimed_shares);
        assert_eq!(
            network_activated,
            NetworkShares::<Test>::get(&network_id).claimed_shares(),
        );
    });
}

#[test]
fn verify_proof_from_python_preclaims_for_sepolia() {
    new_test_ext().execute_with(|| {
		// ===========================================================================================================
		// === FINAL MERKLE ROOT FOR [SEPOLIA]: 0xd7f7f0061338c2c59a96caa8a2572491315755cdcce534954d4ce30150c3bb7c ===
		// ===========================================================================================================
		// [+] Total padded leaves: 128
		// [+] Merkle tree size: 255
		// [+] Network ID: 11155111
		// [+] Proof size: 7
        // [+] Total shares: 694000000000000000000

        let network_id = 11155111u64;
        let merkle_root = EvmHash::from(hex!("d7f7f0061338c2c59a96caa8a2572491315755cdcce534954d4ce30150c3bb7c"));
        let proof_size = 7;
        let total_shares = 694000000000000000000;

        let network_state = NetworkShare::new(total_shares, merkle_root, proof_size);
        let global_state = ShareState::new(total_shares);
        NetworkShares::<Test>::insert(&network_id, network_state);
        GlobalShares::<Test>::put(global_state);

        let evm_holder_1 = EvmAddress::from(hex!("D500EFDef75E89Bf6caF5C98F7633575d0049a72"));
        let claim_package_1 = ClaimPackage {
            index: 0,
            token_id: 1,
            shares: 670000000000000000000,
            merkle_proof: BoundedVec::try_from(
                vec![
                    EvmHash::from(hex!("4dba08ba4fae6cbb0a6be6bdce81e91baf913d1857f470e97388157edd4d8d5c")),
    				EvmHash::from(hex!("7c1a4d2ebe6a500171b0c0f6c624898261da62d09c132106c4acec277660bd01")),
    				EvmHash::from(hex!("00c5bca05597b46f72d9ee739bd3c6ff80ebd1d20944879fe0c8d1dc9fda1643")),
    				EvmHash::from(hex!("5cf752cd738db6581182f07395e89cc61cd2ca630d6e85235d506f7395217d5f")),
    				EvmHash::from(hex!("4df068cecd63b6ccfd326df735afbfbe254df62613f7d9b52769f0e99f156957")),
    				EvmHash::from(hex!("d7f20777e3a1fed303fe1a459ce115e2561a92d66b34fd16dd793b8b69295082")),
    				EvmHash::from(hex!("4524fdc1aa98beafabe1341f53ee2373e541ad2efd152963fd9ce94571d283e4")),
                ]
            ).unwrap(),
        };

        let preimage_1 = claim_package_1.get_preimage(&evm_holder_1, network_id);

        assert_ok!(
            Governance::verify_merkle_proof(
                &network_id,
                &preimage_1,
                &claim_package_1,
            )
        );

        let evm_holder_2 = EvmAddress::from(hex!("Ee71538ae7677829676BD8dD18259Ed93E6B44e9"));
        let claim_package_2 = ClaimPackage {
            index: 2,
            token_id: 3,
            shares: 8000000000000000000,
            merkle_proof: BoundedVec::try_from(
                vec![
                    EvmHash::from(hex!("a8bbe4e0578964e3e6c40b93380967d69b7a1fd0358bffe789473892ec82872d")),
                    EvmHash::from(hex!("9b771d13977930979a26632d74e350885d5750b00646a5b03114b5815afb9f2f")),
                    EvmHash::from(hex!("00c5bca05597b46f72d9ee739bd3c6ff80ebd1d20944879fe0c8d1dc9fda1643")),
                    EvmHash::from(hex!("5cf752cd738db6581182f07395e89cc61cd2ca630d6e85235d506f7395217d5f")),
                    EvmHash::from(hex!("4df068cecd63b6ccfd326df735afbfbe254df62613f7d9b52769f0e99f156957")),
                    EvmHash::from(hex!("d7f20777e3a1fed303fe1a459ce115e2561a92d66b34fd16dd793b8b69295082")),
                    EvmHash::from(hex!("4524fdc1aa98beafabe1341f53ee2373e541ad2efd152963fd9ce94571d283e4")),
                ]
            ).unwrap(),
        };

        let preimage_2 = claim_package_2.get_preimage(&evm_holder_2, network_id);

        assert_ok!(
            Governance::verify_merkle_proof(
                &network_id,
                &preimage_2,
                &claim_package_2,
            )
        );

        let evm_holder_3 = EvmAddress::from(hex!("29c6D9e3c3981e4FcF2AC6B486e4E4210daeA1Ee"));
        let claim_package_3 = ClaimPackage {
            index: 6,
            token_id: 7,
            shares: 4000000000000000000,
            merkle_proof: BoundedVec::try_from(
                vec![
                    EvmHash::from(hex!("d28eec1544597d9e050c88a7a84535be82caf9b9e0b0c9b59b39283e7885a0d1")),
                    EvmHash::from(hex!("6e939188f98c3ed00931e66fdfc220c036b98edc54a212f5b18e2d3776c7c22f")),
                    EvmHash::from(hex!("4141baa2d892b0111bbb102078bd3ad5331c74f176b228c588741c1c306cb67d")),
                    EvmHash::from(hex!("5cf752cd738db6581182f07395e89cc61cd2ca630d6e85235d506f7395217d5f")),
                    EvmHash::from(hex!("4df068cecd63b6ccfd326df735afbfbe254df62613f7d9b52769f0e99f156957")),
                    EvmHash::from(hex!("d7f20777e3a1fed303fe1a459ce115e2561a92d66b34fd16dd793b8b69295082")),
                    EvmHash::from(hex!("4524fdc1aa98beafabe1341f53ee2373e541ad2efd152963fd9ce94571d283e4"))
                ]
            ).unwrap(),
        };

        let preimage_3 = claim_package_3.get_preimage(&evm_holder_3, network_id);

        assert_ok!(
            Governance::verify_merkle_proof(
                &network_id,
                &preimage_3,
                &claim_package_3,
            )
        );

        let evm_holder_4 = EvmAddress::from(hex!("Fe962E8BB2a6877d14b53E0a07932C040eC885ea"));
        let claim_package_4 = ClaimPackage {
            index: 35,
            token_id: 36,
            shares: 4000000000000000000,
            merkle_proof: BoundedVec::try_from(
                vec![
                    EvmHash::from(hex!("6b405422bcab22b7c28a9b429096e31d664522154856efb0d7b16485080b2e2a")),
                    EvmHash::from(hex!("620bb1858eaf6c6bb543fcf5f4b0d0b5adfbb95506403038eb153f075a00f522")),
                    EvmHash::from(hex!("e0b7058849ff216808963201ad2336f5587ff02eff08aceddb4764cc785413fd")),
                    EvmHash::from(hex!("e23325a502fe4248c8e049400a92a1c655689499c129b0ce6be3fad5d4a12dc1")),
                    EvmHash::from(hex!("0cde8f646452003d6c8ff1fd265ea41cd3af2e733e63ec25a9dfc82ddf41e968")),
                    EvmHash::from(hex!("c012942dae89ee6d1e1e44e876ae29b9421aaf0597dd2345f07a1439a98e0cbd")),
                    EvmHash::from(hex!("4524fdc1aa98beafabe1341f53ee2373e541ad2efd152963fd9ce94571d283e4"))
                ]
            ).unwrap(),
        };

        let preimage_4 = claim_package_4.get_preimage(&evm_holder_4, network_id);

        assert_ok!(
            Governance::verify_merkle_proof(
                &network_id,
                &preimage_4,
                &claim_package_4,
            )
        );
    })
}
