use num_bigint::BigUint;
use serde::{Deserialize, Serialize};

use crate::asset::TokenType;
use crate::chain::Chain;
use crate::{AssetId, WalletRegisterId};

/// Register an asset
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct RegisterAsset {
    /// Specify the blockchain
    pub chain: Chain,
    /// Specify the network
    pub network: u8,
    /// Specify the type of token
    pub r#type: TokenType,
    /// Specify the parent asset ID
    pub parent_asset_id: Option<AssetId>,
    /// Specify the address of the asset
    pub address: String,
}

/// Update the confirmation times for a specific network and chain
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct UpdateChainConfirmationTimes {
    /// The chain of tokens belongs to
    pub chain: Chain,
    /// The network of tokens belongs to            
    pub network: u8,
    /// The confirmation time of the tx of the token can be confirmed
    pub confirmation_times: BigUint,
}

/// Update global fee to
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct UpdateGlobalWithdrawalFeeTo {
    pub fee_to: WalletRegisterId,
}

/// Update asset fee to
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct UpdateAssetWithdrawalFeeTo {
    /// The asset id
    pub asset_id: AssetId,

    /// The fee to
    pub fee_to: Option<WalletRegisterId>,
}

#[cfg(test)]
mod tests {
    use ex3_serde::{bincode, cbor};

    use crate::asset::TokenType;

    use super::*;

    #[test]
    fn test_register_asset_bincode_serde() {
        let register_asset = RegisterAsset {
            chain: Chain::Ethereum,
            network: 0,
            r#type: TokenType::ERC20,
            parent_asset_id: None,
            address: "0x01283012830123dfsf".to_string(),
        };

        // bincode
        let encoded = bincode::serialize(&register_asset).unwrap();
        let decoded: RegisterAsset = bincode::deserialize(&encoded).unwrap();
        assert_eq!(register_asset, decoded);

        // cbor
        let encoded = cbor::serialize(&register_asset).unwrap();
        let decoded: RegisterAsset = cbor::deserialize(&encoded).unwrap();
        assert_eq!(register_asset, decoded);
    }

    #[test]
    fn test_update_token_type_confirmation_times_bincode_serde() {
        let update_token_type_confirmation_times = UpdateChainConfirmationTimes {
            chain: Chain::Ethereum,
            network: 0,
            confirmation_times: BigUint::from(100u32),
        };

        let encoded = bincode::serialize(&update_token_type_confirmation_times).unwrap();
        let decoded: UpdateChainConfirmationTimes = bincode::deserialize(&encoded).unwrap();
        assert_eq!(update_token_type_confirmation_times, decoded);
    }

    #[test]
    fn test_update_token_type_confirmation_times_serde() {
        let update_token_type_confirmation_times = UpdateChainConfirmationTimes {
            chain: Chain::Ethereum,
            network: 0,
            confirmation_times: BigUint::from(100u32),
        };

        // bincode
        let encoded = cbor::serialize(&update_token_type_confirmation_times).unwrap();
        let decoded: UpdateChainConfirmationTimes = cbor::deserialize(&encoded).unwrap();
        assert_eq!(update_token_type_confirmation_times, decoded);

        // cbor
        let encoded = cbor::serialize(&update_token_type_confirmation_times).unwrap();
        let decoded: UpdateChainConfirmationTimes = cbor::deserialize(&encoded).unwrap();
        assert_eq!(update_token_type_confirmation_times, decoded);
    }

    #[test]
    fn test_update_global_fee_to_serde() {
        let update_global_fee_to = UpdateGlobalWithdrawalFeeTo {
            fee_to: WalletRegisterId::from(100u32),
        };

        // bincode
        let encoded = bincode::serialize(&update_global_fee_to).unwrap();
        let decoded: UpdateGlobalWithdrawalFeeTo = bincode::deserialize(&encoded).unwrap();
        assert_eq!(update_global_fee_to, decoded);

        // cbor
        let encoded = cbor::serialize(&update_global_fee_to).unwrap();
        let decoded: UpdateGlobalWithdrawalFeeTo = cbor::deserialize(&encoded).unwrap();
        assert_eq!(update_global_fee_to, decoded);
    }

    #[test]
    fn test_update_asset_fee_to_serde() {
        let update_asset_fee_to = UpdateAssetWithdrawalFeeTo {
            asset_id: AssetId::from(100u32),
            fee_to: Some(WalletRegisterId::from(100u32)),
        };

        // bincode
        let encoded = bincode::serialize(&update_asset_fee_to).unwrap();
        let decoded: UpdateAssetWithdrawalFeeTo = bincode::deserialize(&encoded).unwrap();
        assert_eq!(update_asset_fee_to, decoded);

        // cbor
        let encoded = cbor::serialize(&update_asset_fee_to).unwrap();
        let decoded: UpdateAssetWithdrawalFeeTo = cbor::deserialize(&encoded).unwrap();
        assert_eq!(update_asset_fee_to, decoded);
    }
}
