use mock::{
    ExtBuilder, System, RegistererAccount, UpdaterAccount, RemoverAccount, 
    RandomAccount, GhostNetworks, Test, RuntimeEvent, RuntimeOrigin, RewardCurve,
};
use frame_support::{assert_err, assert_ok};
use sp_runtime::DispatchError;
use pallet_staking::EraPayout;
use super::*;

fn prepare_network_data() -> (u32, NetworkData) {
    (1u32, NetworkData {
        chain_name: "Ethereum".into(),
        default_endpoint: "https:://some-endpoint.my-server.com/v1/my-super-secret-key".into(),
        finality_delay: Some(69),
        release_delay: Some(69),
        network_type: NetworkType::Evm,
        gatekeeper: b"0x1234567891234567891234567891234567891234".to_vec(),
        topic_name: b"0x12345678912345678912345678912345678912345678912345678912345678".to_vec(),
        incoming_fee: 0,
        outgoing_fee: 0,
    })
}

fn register_and_check_network(chain_id: u32, network: NetworkData) {
    assert_ok!(GhostNetworks::register_network(
            RuntimeOrigin::signed(RegistererAccount::get()),
            chain_id,
            network.clone()));
    assert_eq!(Networks::<Test>::get(chain_id), Some(network.clone()));
}

#[test]
fn could_add_network_from_authority() {
    ExtBuilder::build()
        .execute_with(|| {
            let (chain_id, network) = prepare_network_data();
            assert_eq!(Networks::<Test>::get(chain_id), None);
            assert_ok!(GhostNetworks::register_network(
                    RuntimeOrigin::signed(RegistererAccount::get()),
                    chain_id,
                    network.clone(),
            ));
            System::assert_last_event(RuntimeEvent::GhostNetworks(
                    crate::Event::NetworkRegistered {
                        chain_id, network: network.clone()
                    }));
            assert_eq!(Networks::<Test>::get(chain_id), Some(network));
        });
}

#[test]
fn could_not_add_network_from_random_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let (chain_id, network) = prepare_network_data();
            assert_eq!(Networks::<Test>::get(chain_id), None);
            assert_err!(GhostNetworks::register_network(
                    RuntimeOrigin::signed(RandomAccount::get()),
                    chain_id,
                    network.clone(),
            ), DispatchError::BadOrigin);
            assert_err!(GhostNetworks::register_network(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id,
                    network.clone(),
            ), DispatchError::BadOrigin);
            assert_err!(GhostNetworks::register_network(
                    RuntimeOrigin::signed(RemoverAccount::get()),
                    chain_id,
                    network,
            ), DispatchError::BadOrigin);
            assert_eq!(Networks::<Test>::get(chain_id), None);
        });
}

#[test]
fn could_update_network_name_from_authority_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let new_name = b"Polygon".to_vec();
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_ok!(GhostNetworks::update_network_name(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, new_name.clone()));
            System::assert_last_event(RuntimeEvent::GhostNetworks(
                    crate::Event::NetworkNameUpdated {
                        chain_id,
                        chain_name: new_name.clone() }));
            let mut final_network = network.clone();
            final_network.chain_name = new_name;
            assert_eq!(Networks::<Test>::get(chain_id), Some(final_network.clone()));
            assert_ne!(network, final_network);
        });
}

#[test]
fn could_update_network_endpoint_from_authority_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let new_endpoint = b"https:://google.com".to_vec();
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_ok!(GhostNetworks::update_network_endpoint(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, new_endpoint.clone()));
            System::assert_last_event(RuntimeEvent::GhostNetworks(
                    crate::Event::NetworkEndpointUpdated {
                        chain_id,
                        default_endpoint: new_endpoint.clone() }));
            let mut final_network = network.clone();
            final_network.default_endpoint = new_endpoint;
            assert_eq!(Networks::<Test>::get(chain_id), Some(final_network.clone()));
            assert_ne!(network, final_network);
        });
}

#[test]
fn could_update_network_finality_delay_from_authority_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let new_finality_delay = Some(1337);
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_ok!(GhostNetworks::update_network_finality_delay(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, new_finality_delay));
            System::assert_last_event(RuntimeEvent::GhostNetworks(
                    crate::Event::NetworkFinalityDelayUpdated {
                        chain_id,
                        finality_delay: new_finality_delay }));
            let mut final_network = network.clone();
            final_network.finality_delay = new_finality_delay;
            assert_eq!(Networks::<Test>::get(chain_id), Some(final_network.clone()));
            assert_ne!(network, final_network);
        });
}

#[test]
fn could_update_network_release_delay_from_authority_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let new_release_delay = Some(1337);
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_ok!(GhostNetworks::update_network_release_delay(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, new_release_delay));
            System::assert_last_event(RuntimeEvent::GhostNetworks(
                    crate::Event::NetworkReleaseDelayUpdated {
                        chain_id,
                        release_delay: new_release_delay }));
            let mut final_network = network.clone();
            final_network.release_delay = new_release_delay;
            assert_eq!(Networks::<Test>::get(chain_id), Some(final_network.clone()));
            assert_ne!(network, final_network);
        });
}

#[test]
fn could_update_network_type_from_authority_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let new_type = NetworkType::Utxo;
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_ok!(GhostNetworks::update_network_type(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, new_type.clone()));
            System::assert_last_event(RuntimeEvent::GhostNetworks(
                    crate::Event::NetworkTypeUpdated {
                        chain_id,
                        network_type: new_type.clone() }));
            let mut final_network = network.clone();
            final_network.network_type = new_type;
            assert_eq!(Networks::<Test>::get(chain_id), Some(final_network.clone()));
            assert_ne!(network, final_network);
        });
}

#[test]
fn could_update_network_gatekeeper_from_authority_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let new_gatekeeper = b"0x9876543219876543219876543219876543219876".to_vec();
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_ok!(GhostNetworks::update_network_gatekeeper(
                    RuntimeOrigin::signed(UpdaterAccount::get()), 
                    chain_id, new_gatekeeper.clone()));
            System::assert_last_event(RuntimeEvent::GhostNetworks(
                    crate::Event::NetworkGatekeeperUpdated {
                        chain_id,
                        gatekeeper: new_gatekeeper.clone() }));
            let mut final_network = network.clone();
            final_network.gatekeeper = new_gatekeeper;
            assert_eq!(Networks::<Test>::get(chain_id), Some(final_network.clone()));
            assert_ne!(network, final_network);
        });
}

#[test]
fn could_update_network_topic_name_from_authority_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let new_topic_name = b"0x9876543219876543219876543219876543219876543219876543219876543219".to_vec();
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_ok!(GhostNetworks::update_network_topic_name(
                    RuntimeOrigin::signed(UpdaterAccount::get()), 
                    chain_id, new_topic_name.clone()));
            System::assert_last_event(RuntimeEvent::GhostNetworks(
                    crate::Event::NetworkTopicNameUpdated {
                        chain_id,
                        topic_name: new_topic_name.clone() }));
            let mut final_network = network.clone();
            final_network.topic_name = new_topic_name;
            assert_eq!(Networks::<Test>::get(chain_id), Some(final_network.clone()));
            assert_ne!(network, final_network);
        });
}

#[test]
fn could_update_incoming_network_fee_from_authority_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let new_incoming_fee = 69;
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_ok!(GhostNetworks::update_incoming_network_fee(
                    RuntimeOrigin::signed(UpdaterAccount::get()), 
                    chain_id, new_incoming_fee));
            System::assert_last_event(RuntimeEvent::GhostNetworks(
                    crate::Event::NetworkIncomingFeeUpdated {
                        chain_id,
                        incoming_fee: new_incoming_fee }));
            let mut final_network = network.clone();
            final_network.incoming_fee = new_incoming_fee;
            assert_eq!(Networks::<Test>::get(chain_id), Some(final_network.clone()));
            assert_ne!(network, final_network);
        });
}

#[test]
fn could_update_outgoing_network_fee_from_authority_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let new_outgoing_fee = 69;
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_ok!(GhostNetworks::update_outgoing_network_fee(
                    RuntimeOrigin::signed(UpdaterAccount::get()), 
                    chain_id, new_outgoing_fee));
            System::assert_last_event(RuntimeEvent::GhostNetworks(
                    crate::Event::NetworkOutgoingFeeUpdated {
                        chain_id,
                        outgoing_fee: new_outgoing_fee }));
            let mut final_network = network.clone();
            final_network.outgoing_fee = new_outgoing_fee;
            assert_eq!(Networks::<Test>::get(chain_id), Some(final_network.clone()));
            assert_ne!(network, final_network);
        });
}

#[test]
fn could_not_update_network_name_from_random_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_err!(GhostNetworks::update_network_name(
                    RuntimeOrigin::signed(RegistererAccount::get()),
                    chain_id, "Binance".into()),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_name(
                    RuntimeOrigin::signed(RemoverAccount::get()),
                    chain_id, "Binance".into()),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_name(
                    RuntimeOrigin::signed(RandomAccount::get()),
                    chain_id, "Binance".into()),
                    DispatchError::BadOrigin);
            assert_eq!(Networks::<Test>::get(chain_id), Some(network));
        });
}

#[test]
fn could_not_update_network_endpoint_from_random_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_err!(GhostNetworks::update_network_endpoint(
                    RuntimeOrigin::signed(RegistererAccount::get()),
                    chain_id, "https:://google.com".into()),
                DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_endpoint(
                    RuntimeOrigin::signed(RemoverAccount::get()),
                    chain_id, "https:://google.com".into()),
                DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_endpoint(
                    RuntimeOrigin::signed(RandomAccount::get()),
                    chain_id, "https:://google.com".into()),
                DispatchError::BadOrigin);
            assert_eq!(Networks::<Test>::get(chain_id), Some(network));
        });
}

#[test]
fn could_not_update_network_finality_delay_from_random_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_err!(GhostNetworks::update_network_finality_delay(
                    RuntimeOrigin::signed(RegistererAccount::get()),
                    chain_id, Some(1337)),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_finality_delay(
                    RuntimeOrigin::signed(RemoverAccount::get()),
                    chain_id, Some(1337)),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_finality_delay(
                    RuntimeOrigin::signed(RandomAccount::get()),
                    chain_id, Some(1337)),
                    DispatchError::BadOrigin);
            assert_eq!(Networks::<Test>::get(chain_id), Some(network));
        });
}

#[test]
fn could_not_update_network_release_delay_from_random_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_err!(GhostNetworks::update_network_release_delay(
                    RuntimeOrigin::signed(RegistererAccount::get()),
                    chain_id, Some(1337)),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_release_delay(
                    RuntimeOrigin::signed(RemoverAccount::get()),
                    chain_id, Some(1337)),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_release_delay(
                    RuntimeOrigin::signed(RandomAccount::get()),
                    chain_id, Some(1337)),
                    DispatchError::BadOrigin);
            assert_eq!(Networks::<Test>::get(chain_id), Some(network));
        });
}

#[test]
fn could_not_update_network_type_from_random_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_err!(GhostNetworks::update_network_type(
                    RuntimeOrigin::signed(RegistererAccount::get()),
                    chain_id, NetworkType::Utxo),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_type(
                    RuntimeOrigin::signed(RemoverAccount::get()),
                    chain_id, NetworkType::Utxo),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_type(
                    RuntimeOrigin::signed(RandomAccount::get()),
                    chain_id, NetworkType::Utxo),
                    DispatchError::BadOrigin);
            assert_eq!(Networks::<Test>::get(chain_id), Some(network));
        });
}

#[test]
fn could_not_update_network_gatekeeper_from_random_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_err!(GhostNetworks::update_network_gatekeeper(
                    RuntimeOrigin::signed(RegistererAccount::get()),
                    chain_id, b"0x9876543219876543219876543219876543219876".to_vec()),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_gatekeeper(
                    RuntimeOrigin::signed(RemoverAccount::get()),
                    chain_id, b"0x9876543219876543219876543219876543219876".to_vec()),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_gatekeeper(
                    RuntimeOrigin::signed(RandomAccount::get()),
                    chain_id, b"0x9876543219876543219876543219876543219876".to_vec()),
                    DispatchError::BadOrigin);
            assert_eq!(Networks::<Test>::get(chain_id), Some(network));
        });
}

#[test]
fn could_not_update_network_topic_name_from_random_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_err!(GhostNetworks::update_network_topic_name(
                    RuntimeOrigin::signed(RegistererAccount::get()),
                    chain_id, b"0x9876543219876543219876543219876543219876543219876543219876543219".to_vec()),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_topic_name(
                    RuntimeOrigin::signed(RemoverAccount::get()),
                    chain_id, b"0x9876543219876543219876543219876543219876543219876543219876543219".to_vec()),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_network_topic_name(
                    RuntimeOrigin::signed(RandomAccount::get()),
                    chain_id, b"0x9876543219876543219876543219876543219876543219876543219876543219".to_vec()),
                    DispatchError::BadOrigin);
            assert_eq!(Networks::<Test>::get(chain_id), Some(network));
        });
}

#[test]
fn could_not_update_network_incoming_fee_from_random_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_err!(GhostNetworks::update_incoming_network_fee(
                    RuntimeOrigin::signed(RegistererAccount::get()),
                    chain_id, 69),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_incoming_network_fee(
                    RuntimeOrigin::signed(RemoverAccount::get()),
                    chain_id, 69),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_incoming_network_fee(
                    RuntimeOrigin::signed(RandomAccount::get()),
                    chain_id, 69),
                    DispatchError::BadOrigin);
            assert_eq!(Networks::<Test>::get(chain_id), Some(network));
        });
}

#[test]
fn could_not_update_network_outgoing_fee_from_random_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_err!(GhostNetworks::update_outgoing_network_fee(
                    RuntimeOrigin::signed(RegistererAccount::get()),
                    chain_id, 69),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_outgoing_network_fee(
                    RuntimeOrigin::signed(RemoverAccount::get()),
                    chain_id, 69),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::update_outgoing_network_fee(
                    RuntimeOrigin::signed(RandomAccount::get()),
                    chain_id, 69),
                    DispatchError::BadOrigin);
            assert_eq!(Networks::<Test>::get(chain_id), Some(network));
        });
}

#[test]
fn could_not_update_name_for_non_existent_network() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id: u32 = 1;
            assert_eq!(Networks::<Test>::get(chain_id), None);
            assert_err!(GhostNetworks::update_network_name(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, "Binance".into()),
                    crate::Error::<Test>::NetworkDoesNotExist);
            assert_eq!(Networks::<Test>::get(chain_id), None);
        });
}

#[test]
fn could_not_update_endpoint_for_non_existent_network() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id: u32 = 1;
            assert_eq!(Networks::<Test>::get(chain_id), None);
            assert_err!(GhostNetworks::update_network_endpoint(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, "https:://google.com".into()),
                crate::Error::<Test>::NetworkDoesNotExist);
            assert_eq!(Networks::<Test>::get(chain_id), None);
        });
}

#[test]
fn could_not_update_finality_delay_for_non_existent_network() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id: u32 = 1;
            assert_eq!(Networks::<Test>::get(chain_id), None);
            assert_err!(GhostNetworks::update_network_finality_delay(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, Some(1337)),
                    crate::Error::<Test>::NetworkDoesNotExist);
            assert_eq!(Networks::<Test>::get(chain_id), None);
        });
}

#[test]
fn could_not_update_release_delay_for_non_existent_network() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id: u32 = 1;
            assert_eq!(Networks::<Test>::get(chain_id), None);
            assert_err!(GhostNetworks::update_network_release_delay(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, Some(1337)),
                    crate::Error::<Test>::NetworkDoesNotExist);
            assert_eq!(Networks::<Test>::get(chain_id), None);
        });
}

#[test]
fn could_not_update_type_for_non_existent_network() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id: u32 = 1;
            assert_eq!(Networks::<Test>::get(chain_id), None);
            assert_err!(GhostNetworks::update_network_type(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, NetworkType::Utxo),
                    crate::Error::<Test>::NetworkDoesNotExist);
            assert_eq!(Networks::<Test>::get(chain_id), None);
        });
}

#[test]
fn could_not_update_gatekeeper_for_non_existent_network() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id: u32 = 1;
            assert_eq!(Networks::<Test>::get(chain_id), None);
            assert_err!(GhostNetworks::update_network_gatekeeper(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, b"0x9876543219876543219876543219876543219876".to_vec()),
                    crate::Error::<Test>::NetworkDoesNotExist);
            assert_eq!(Networks::<Test>::get(chain_id), None);
        });
}

#[test]
fn could_not_update_topic_name_for_non_existent_network() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id: u32 = 1;
            assert_eq!(Networks::<Test>::get(chain_id), None);
            assert_err!(GhostNetworks::update_network_topic_name(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, b"0x9876543219876543219876543219876543219876543219876543219876543219".to_vec()),
                    crate::Error::<Test>::NetworkDoesNotExist);
            assert_eq!(Networks::<Test>::get(chain_id), None);
        });
}

#[test]
fn could_not_update_incoming_fee_for_non_existent_network() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id: u32 = 1;
            assert_eq!(Networks::<Test>::get(chain_id), None);
            assert_err!(GhostNetworks::update_incoming_network_fee(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, 1337),
                    crate::Error::<Test>::NetworkDoesNotExist);
            assert_eq!(Networks::<Test>::get(chain_id), None);
        });
}

#[test]
fn could_not_update_outgoing_fee_for_non_existent_network() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id: u32 = 1;
            assert_eq!(Networks::<Test>::get(chain_id), None);
            assert_err!(GhostNetworks::update_outgoing_network_fee(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id, 1337),
                    crate::Error::<Test>::NetworkDoesNotExist);
            assert_eq!(Networks::<Test>::get(chain_id), None);
        });
}

#[test]
fn could_remove_network_from_authority_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_ok!(GhostNetworks::remove_network(
                    RuntimeOrigin::signed(RemoverAccount::get()),
                    chain_id,
            ));
            assert_eq!(Networks::<Test>::get(chain_id), None);
        });
}

#[test]
fn could_not_remove_network_from_random_account() {
    ExtBuilder::build()
        .execute_with(|| {
            let (chain_id, network) = prepare_network_data();
            register_and_check_network(chain_id, network.clone());
            assert_err!(GhostNetworks::remove_network(
                    RuntimeOrigin::signed(RegistererAccount::get()),
                    chain_id),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::remove_network(
                    RuntimeOrigin::signed(UpdaterAccount::get()),
                    chain_id),
                    DispatchError::BadOrigin);
            assert_err!(GhostNetworks::remove_network(
                    RuntimeOrigin::signed(RandomAccount::get()),
                    chain_id),
                    DispatchError::BadOrigin);
            assert_eq!(Networks::<Test>::get(chain_id), Some(network));
        });
}

#[test]
fn could_not_remove_non_existent_network() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id: u32 = 1;
            assert_eq!(Networks::<Test>::get(chain_id), None);
            assert_err!(GhostNetworks::remove_network(
                    RuntimeOrigin::signed(RemoverAccount::get()),
                    chain_id),
                    crate::Error::<Test>::NetworkDoesNotExist);
            assert_eq!(Networks::<Test>::get(chain_id), None);
        });
}

#[test]
fn bridge_storage_is_empty_by_default() {
    ExtBuilder::build()
        .execute_with(|| {
            assert_eq!(AccumulatedCommission::<Test>::get(), 0);
            assert_eq!(BridgedImbalance::<Test>::get(),
                BridgeAdjustment::default());
        });
}

#[test]
fn gatekeeper_amount_changes_correctly() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id: u32 = 1;
            let amount_in: u128 = 420;
            let amount_out: u128 = 69;
            let result = amount_in - 3 * amount_out;

            assert_eq!(GatekeeperAmount::<Test>::get(&chain_id), 0);
            assert_eq!(BridgedImbalance::<Test>::get(),
                BridgeAdjustment::default());

            assert_ok!(GhostNetworks::increase_gatekeeper_amount(&chain_id, &amount_in));
            assert_ok!(GhostNetworks::decrease_gatekeeper_amount(&chain_id, &amount_out));
            assert_ok!(GhostNetworks::decrease_gatekeeper_amount(&chain_id, &amount_out));
            assert_ok!(GhostNetworks::decrease_gatekeeper_amount(&chain_id, &amount_out));

            assert_eq!(GatekeeperAmount::<Test>::get(&chain_id), result);
            assert_eq!(BridgedImbalance::<Test>::get(), BridgeAdjustment {
                bridged_out: 3 * amount_out,
                bridged_in: amount_in });
        });
}

#[test]
fn commission_accumulation_is_correct() {
    ExtBuilder::build()
        .execute_with(|| {
            let commission_first: u128 = 420;
            let commission_second: u128 = 69;
            let result = commission_first + commission_second;

            assert_eq!(AccumulatedCommission::<Test>::get(), 0);
            assert_ok!(GhostNetworks::accumulate_commission(&commission_first));
            assert_ok!(GhostNetworks::accumulate_commission(&commission_second));
            assert_eq!(AccumulatedCommission::<Test>::get(), result);
        });
}

#[test]
fn commission_overflow_and_underflow_emits_error() {
    ExtBuilder::build()
        .execute_with(|| {
            let commission: u128 = u128::MAX - 69;
            assert_eq!(AccumulatedCommission::<Test>::get(), 0);
            assert_ok!(GhostNetworks::accumulate_commission(&commission));
            assert_err!(GhostNetworks::accumulate_commission(&commission), ());
            assert_eq!(AccumulatedCommission::<Test>::get(), commission);
        });
}

#[test]
fn gatekeeper_amount_overflow_and_underflow_emits_error() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id: u32 = 1;
            let commission: u128 = u128::MAX - 69;
            assert_eq!(GatekeeperAmount::<Test>::get(&chain_id), 0);
            assert_ok!(GhostNetworks::increase_gatekeeper_amount(
                    &chain_id,
                    &commission,
            ), (commission, commission));
            assert_err!(GhostNetworks::increase_gatekeeper_amount(
                    &chain_id,
                    &commission,
            ), ());
            assert_eq!(GatekeeperAmount::<Test>::get(&chain_id), commission);
            assert_ok!(GhostNetworks::decrease_gatekeeper_amount(
                    &chain_id,
                    &commission,
            ), (0, commission));
            assert_err!(GhostNetworks::decrease_gatekeeper_amount(
                    &chain_id,
                    &commission,
            ), ());
            assert_eq!(GatekeeperAmount::<Test>::get(&chain_id), 0);
            assert_eq!(BridgedImbalance::<Test>::get(), BridgeAdjustment {
                bridged_out: commission,
                bridged_in: commission,
            });
        });
}

#[test]
fn bridged_amount_overflow_and_underflow_emits_error() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id_first: u32 = 1;
            let chain_id_second: u32 = 2;
            let commission: u128 = u128::MAX - 69;
            assert_eq!(BridgedImbalance::<Test>::get(), BridgeAdjustment {
                bridged_out: 0,
                bridged_in: 0,
            });
            assert_ok!(GhostNetworks::increase_gatekeeper_amount(
                    &chain_id_first,
                    &commission,
            ), (commission, commission));
            assert_err!(GhostNetworks::increase_gatekeeper_amount(
                    &chain_id_second,
                    &commission,
            ), ());
            assert_err!(GhostNetworks::increase_gatekeeper_amount(
                    &chain_id_first,
                    &u128::MAX,
            ), ());
            assert_err!(GhostNetworks::increase_gatekeeper_amount(
                    &chain_id_first,
                    &commission,
            ), ());
            assert_eq!(GatekeeperAmount::<Test>::get(&chain_id_first), commission);
            assert_eq!(GatekeeperAmount::<Test>::get(&chain_id_second), 0);
            assert_err!(GhostNetworks::decrease_gatekeeper_amount(
                    &chain_id_second,
                    &commission,
            ), ());
        });
}

#[test]
fn accumulated_commission_could_be_nullified() {
    ExtBuilder::build()
        .execute_with(|| {
            let commission_first: u128 = 420;
            let commission_second: u128 = 69;

            assert_eq!(AccumulatedCommission::<Test>::get(), 0);
            assert_ok!(GhostNetworks::accumulate_commission(
                    &commission_first
            ), commission_first);
            assert_ok!(GhostNetworks::accumulate_commission(
                    &commission_second,
            ), commission_first + commission_second);
            assert_eq!(AccumulatedCommission::<Test>::get(),
                commission_first + commission_second);
            GhostNetworks::nullify_commission();
            assert_eq!(AccumulatedCommission::<Test>::get(), 0);
        });
}

#[test]
fn bridged_inlation_reward_works() {
    ExtBuilder::build()
        .execute_with(|| {
            let chain_id: u32 = 1;
            let amount: u128 = 1337 * 1_000_000_000;
            let commission: u128 = amount / 100; // 1% commission
            let total_staked_ideal: u128 = 69;
            let total_staked_not_ideal: u128 = 68;
            let total_issuance: u128 = 100;


            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_ideal * 1_000,
                    total_issuance * 1_000,
                    0), (0, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_ideal * 1_000_000_000_000,
                    total_issuance * 1_000_000_000_000,
                    0), (0, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_ideal * 1_000_000_000_000_000_000_000_000,
                    total_issuance * 1_000_000_000_000_000_000_000_000,
                    0), (0, 0));

            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_not_ideal * 1_000,
                    total_issuance * 1_000,
                    0), (0, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_not_ideal * 1_000_000_000_000,
                    total_issuance * 1_000_000_000_000,
                    0), (0, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_not_ideal * 1_000_000_000_000_000_000_000_000,
                    total_issuance * 1_000_000_000_000_000_000_000_000,
                    0), (0, 0));

            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    1, total_issuance * 1_000, 0), (0, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    1, total_issuance * 1_000_000_000_000, 0), (0, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    1, total_issuance * 1_000_000_000_000_000_000_000_000, 0), (0, 0));

            assert_ok!(GhostNetworks::accumulate_commission(&commission));
            assert_ok!(GhostNetworks::increase_gatekeeper_amount(&chain_id, &amount));

            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_ideal * 1_000,
                    total_issuance * 1_000 + amount,
                    0), (commission, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_ideal * 1_000_000_000_000,
                    total_issuance * 1_000_000_000_000 + amount,
                    0), (commission, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_ideal * 1_000_000_000_000_000_000_000_000,
                    total_issuance * 1_000_000_000_000_000_000_000_000 + amount,
                    0), (commission, 0));

            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_not_ideal * 1_000,
                    total_issuance * 1_000 + amount,
                    0), (13177472000, 192528000));
            assert_eq!(13177472000 + 192528000, commission);
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_not_ideal * 1_000_000_000_000,
                    total_issuance * 1_000_000_000_000 + amount,
                    0), (13177568884, 192431116));
            assert_eq!(13177568884 + 192431116, commission);
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_not_ideal * 1_000_000_000_000_000_000_000_000,
                    total_issuance * 1_000_000_000_000_000_000_000_000 + amount,
                    0), (13177568884, 192431116));
            assert_eq!(13177568884 + 192431116, commission);

            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    1, total_issuance * 1_000 + amount, 0),
                (92386700, 13277613300));
            assert_eq!(92386700 + 13277613300, commission);
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    1, total_issuance * 1_000_000_000_000 + amount, 0),
                (92253000, 13277747000));
            assert_eq!(92253000 + 13277747000, commission);
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    1, total_issuance * 1_000_000_000_000_000_000_000_000 + amount, 0),
                (92253000, 13277747000));
            assert_eq!(92253000 + 13277747000, commission);

            GhostNetworks::nullify_commission();

            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_ideal * 1_000,
                    total_issuance * 1_000 + amount,
                    0), (0, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_ideal * 1_000_000_000_000,
                    total_issuance * 1_000_000_000_000 + amount,
                    0), (0, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_ideal * 1_000_000_000_000_000_000_000_000,
                    total_issuance * 1_000_000_000_000_000_000_000_000 + amount,
                    0), (0, 0));

            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_not_ideal * 1_000,
                    total_issuance * 1_000 + amount,
                    0), (0, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_not_ideal * 1_000_000_000_000,
                    total_issuance * 1_000_000_000_000 + amount,
                    0), (0, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    total_staked_not_ideal * 1_000_000_000_000_000_000_000_000,
                    total_issuance * 1_000_000_000_000_000_000_000_000 + amount,
                    0), (0, 0));

            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    1, total_issuance * 1_000 + amount, 0), (0, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    1, total_issuance * 1_000_000_000_000 + amount, 0), (0, 0));
            assert_eq!(BridgedInflationCurve::<RewardCurve, Test>::era_payout(
                    1, total_issuance * 1_000_000_000_000_000_000_000_000 + amount, 0), (0, 0));
        });
}
