use codec::{Decode, Encode};
use frame_support::pallet_prelude::*;

use sp_runtime::serde::{Deserialize, Serialize};
use strum::{EnumIter, EnumCount, FromRepr};

use scale_info::TypeInfo;
use sp_runtime::RuntimeDebug;
use sp_std::vec::Vec;
use sp_core::H256;

pub const MAX_ENDPOINT_LEN: u32 = 128;
pub const MAX_ENDPOINTS_LEN: u32 = 10;
pub const MAX_GATEKEEPER_LEN: u32 = 65;
pub const MAX_SELECTOR_LEN: u32 = 4;

#[derive(
    Default,
    Encode,
    Decode,
    Clone,
    Copy,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    RuntimeDebug,
    TypeInfo,
    MaxEncodedLen,
    EnumIter,
    EnumCount,
    FromRepr,
    Serialize,
    Deserialize,
)]
#[repr(u8)]
pub enum NetworkType {
    #[default]
    Evm = 0,
    Utxo = 1,
    Unknown = 2,
}

impl NetworkType {
    pub fn is_evm(&self) -> bool {
        matches!(self, NetworkType::Evm)
    }

    pub fn is_utxo(&self) -> bool {
        matches!(self, NetworkType::Utxo)
    }
}

impl core::fmt::Display for NetworkType {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        let text = match self {
            NetworkType::Evm => "EVM",
            NetworkType::Utxo => "UTXO",
            NetworkType::Unknown => "Unknown",
        };
        write!(f, "{}", text)
    }
}

#[derive(
    Default,
    Encode,
    Decode,
    Clone,
    Copy,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    RuntimeDebug,
    TypeInfo,
    MaxEncodedLen,
    EnumIter,
    EnumCount,
    FromRepr,
    Serialize,
    Deserialize,
)]
#[repr(u8)]
pub enum NetworkCurve {
    #[default]
    Secp256k1 = 0,
    Ed25519 = 1,
}

impl core::fmt::Display for NetworkCurve {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        let text = match self {
            NetworkCurve::Secp256k1 => "secp256k1",
            NetworkCurve::Ed25519 => "ed25519"
        };
        write!(f, "{}", text)
    }
}

#[derive(
    Encode,
    Decode,
    Clone,
    PartialEq,
    Eq,
    RuntimeDebug,
    TypeInfo,
    MaxEncodedLen,
    Serialize,
    Deserialize,
)]
pub struct NetworkInitiation<NetworkId, Balance> {
    pub id: NetworkId,
    pub amount: Balance,
    pub data: NetworkData,
}

#[derive(Default)]
pub struct NetworkInitiationBuilder<NetworkId, Balance>
where
	NetworkId: Default,
	Balance: Default,
{
    pub id: Option<NetworkId>,
	pub amount: Option<Balance>,
	pub data: Option<NetworkData>,
}

impl<NetworkId, Balance> NetworkInitiationBuilder<NetworkId, Balance>
where
	NetworkId: Default,
	Balance: Default,
{
    pub fn with_network_id(mut self, network_id: NetworkId) -> Self {
        self.id = Some(network_id);
        self
    }

    pub fn with_gatekeeper_amount(mut self, amount: Balance) -> Self {
        self.amount = Some(amount);
        self
    }

    pub fn with_network_data(mut self, data: NetworkData) -> Self {
        self.data = Some(data);
        self
    }

    pub fn build(self) -> NetworkInitiation<NetworkId, Balance> {
        NetworkInitiation {
            id: self.id.unwrap(),
            amount: self.amount.unwrap(),
            data: self.data.unwrap(),
        }
    }
}

#[derive(
    Encode,
    Decode,
    Clone,
    PartialEq,
    Eq,
    RuntimeDebug,
    TypeInfo,
    MaxEncodedLen,
    Serialize,
    Deserialize,
)]
pub struct NetworkClaim<NetworkId, Balance> {
    pub network_id: NetworkId,
    pub merkle_root: H256,
    pub amount: Balance,
    pub proof_size: u32
}

impl<NetworkId, Balance> NetworkClaim<NetworkId, Balance> {
    pub fn with_amount(mut self, amount: Balance) -> Self {
        self.amount = amount;
        self
    }

    pub fn with_network_id(mut self, network_id: NetworkId) -> Self {
        self.network_id = network_id;
        self
    }

    pub fn with_merkle_root(mut self, merkle_root: H256) -> Self {
        self.merkle_root = merkle_root;
        self
    }

    pub fn with_proof_size(mut self, proof_size: u32) -> Self {
        self.proof_size = proof_size;
        self
    }

}

#[derive(
    Encode,
    Decode,
    Clone,
    PartialEq,
    Eq,
    RuntimeDebug,
    TypeInfo,
    MaxEncodedLen,
    Serialize,
    Deserialize,
)]
pub struct NetworkData {
    pub gatekeeper: BoundedVec<u8, ConstU32<MAX_GATEKEEPER_LEN>>,
    pub selector: BoundedVec<u8, ConstU32<MAX_SELECTOR_LEN>>,
    pub default_endpoints:
        BoundedVec<BoundedVec<u8, ConstU32<MAX_ENDPOINT_LEN>>, ConstU32<MAX_ENDPOINTS_LEN>>,

    pub r#type: NetworkType,
    pub curve: NetworkCurve,

    pub finality_delay: u64,
    pub rate_limit_delay: u64,
    pub block_deviation: u64,

    pub incoming_share: u32,
    pub outgoing_share: u32,
}

#[derive(Default)]
pub struct NetworkDataBuilder {
    pub gatekeeper: Vec<u8>,
    pub selector: Vec<u8>,
    pub default_endpoints: Vec<Vec<u8>>,

    pub r#type: NetworkType,
    pub curve: NetworkCurve,

    pub finality_delay: u64,
    pub rate_limit_delay: u64,
    pub block_deviation: u64,

    pub incoming_share: u32,
    pub outgoing_share: u32,
}

impl NetworkDataBuilder {
    pub fn with_gatekeeper(mut self, gatekeeper: Vec<u8>) -> Self {
        self.gatekeeper = gatekeeper;
        self
    }

    pub fn with_selector(mut self, selector: Vec<u8>) -> Self {
        self.selector = selector;
        self
    }

    pub fn with_network_type(mut self, network_type: NetworkType) -> Self {
        self.r#type = network_type;
        self
    }

    pub fn with_network_curve(mut self, curve: NetworkCurve) -> Self {
        self.curve = curve;
        self
    }

    pub fn with_finality_delay(mut self, finality_delay: u64) -> Self {
        self.finality_delay = finality_delay;
        self
    }

    pub fn with_rate_limit_delay(mut self, rate_limit_delay: u64) -> Self {
        self.rate_limit_delay = rate_limit_delay;
        self
    }

    pub fn with_block_deviation(mut self, block_deviation: u64) -> Self {
        self.block_deviation = block_deviation;
        self
    }

    pub fn with_incoming_share(mut self, incoming_share: u32) -> Self {
        self.incoming_share = incoming_share;
        self
    }

    pub fn with_outgoing_share(mut self, outgoing_share: u32) -> Self {
        self.outgoing_share = outgoing_share;
        self
    }

    pub fn with_default_endpoints(mut self, default_endpoints: Vec<Vec<u8>>) -> Self {
        self.default_endpoints = default_endpoints;
        self
    }

    pub fn build(self) -> NetworkData {
        let gatekeeper = BoundedVec::<u8, ConstU32<MAX_GATEKEEPER_LEN>>::try_from(self.gatekeeper)
            .expect("Gatekeeper exceeds max length");

        let selector = BoundedVec::<u8, ConstU32<MAX_SELECTOR_LEN>>::try_from(self.selector)
            .expect("Selector exceeds max length");

        let default_endpoints = BoundedVec::<
            BoundedVec<u8, ConstU32<MAX_ENDPOINT_LEN>>,
            ConstU32<MAX_ENDPOINTS_LEN>,
        >::try_from(
            self.default_endpoints
                .into_iter()
                .map(|endpoint| {
                    BoundedVec::<u8, ConstU32<MAX_ENDPOINT_LEN>>::try_from(endpoint)
                        .expect("Endpoint exceeds max length")
                })
                .collect::<Vec<BoundedVec<u8, ConstU32<MAX_ENDPOINT_LEN>>>>(),
        )
        .expect("Too many endpoints");

        NetworkData {
            gatekeeper,
            selector,
            default_endpoints,

            r#type: self.r#type,
            curve: self.curve,

            finality_delay: self.finality_delay,
            rate_limit_delay: self.rate_limit_delay,
            block_deviation: self.block_deviation,

            incoming_share: self.incoming_share,
            outgoing_share: self.outgoing_share,
        }
    }
}
