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

use crate::impl_from_uint_for;

/// Transaction type.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash, Copy)]
pub enum TransactionType {
    /// Wallet register.
    WalletRegister = 100,

    /// Deposit.
    Deposit = 200,

    /// Withdrawal.
    Withdrawal = 201,

    /// Force withdrawal.
    ForceWithdrawal = 202,

    /// Transfer.
    Transfer = 300,

    /// Reset main secret.
    ResetMainSecret = 400,

    /// Create API secret.
    CreateApiSecret = 401,

    /// Destroy API secret.
    DestroyApiSecret = 402,

    /// Submit spot order.
    SubmitSpotOrder = 500,

    /// Cancel spot order.
    CancelSpotOrder = 501,

    /// Add spot liquidity.
    AddAmmV2Liquidity = 600,

    /// Remove spot liquidity.
    RemoveAmmV2Liquidity = 601,

    /// Register asset
    RegisterAsset = 700,

    /// Update global withdrawal fee to
    UpdateGlobalWithdrawalFeeTo = 701,

    /// Update asset withdrawal fee to
    UpdateAssetWithdrawalFeeTo = 702,

    /// Update chain confirmation times
    UpdateChainConfirmationTimes = 800,

    /// Register market
    RegisterSpotMarket = 900,

    /// Update spot market trading settings
    UpdateSpotMarketTradingSettings = 901,

    /// Update spot market initial fee to
    UpdateSpotMarketInitialFeeTo = 902,

    /// Update spot market fee to
    UpdateSpotMarketFeeTo = 903,

    /// Update spot market initial trading fee
    UpdateSpotMarketInitialTradingFee = 904,

    /// Update spot market trading fee
    UpdateSpotMarketTradingFee = 905,

    /// Claim spot market trading fee
    ClaimSpotMarketTradingFee = 906,

    /// Update spot market royalty
    UpdateSpotMarketRoyalty = 907,

    /// Claim spot market royalty
    ClaimSpotMarketRoyalty = 908,

    /// Asset account binding
    AssetAccountBinding = 1000,

    /// Asset account unbinding
    AssetAccountUnbinding = 1001,
}

impl TransactionType {
    pub fn iterator() -> impl Iterator<Item = TransactionType> {
        [
            TransactionType::WalletRegister,
            TransactionType::Deposit,
            TransactionType::Withdrawal,
            TransactionType::ForceWithdrawal,
            TransactionType::Transfer,
            TransactionType::ResetMainSecret,
            TransactionType::CreateApiSecret,
            TransactionType::DestroyApiSecret,
            TransactionType::SubmitSpotOrder,
            TransactionType::CancelSpotOrder,
            TransactionType::AddAmmV2Liquidity,
            TransactionType::RemoveAmmV2Liquidity,
            TransactionType::RegisterAsset,
            TransactionType::UpdateGlobalWithdrawalFeeTo,
            TransactionType::UpdateAssetWithdrawalFeeTo,
            TransactionType::UpdateChainConfirmationTimes,
            TransactionType::RegisterSpotMarket,
            TransactionType::UpdateSpotMarketTradingSettings,
            TransactionType::UpdateSpotMarketInitialFeeTo,
            TransactionType::UpdateSpotMarketFeeTo,
            TransactionType::UpdateSpotMarketInitialTradingFee,
            TransactionType::UpdateSpotMarketTradingFee,
            TransactionType::ClaimSpotMarketTradingFee,
            TransactionType::UpdateSpotMarketRoyalty,
            TransactionType::ClaimSpotMarketRoyalty,
        ]
        .iter()
        .copied()
    }
}

impl From<BigUint> for TransactionType {
    fn from(value: BigUint) -> Self {
        match value.to_u64().unwrap() {
            100 => TransactionType::WalletRegister,
            200 => TransactionType::Deposit,
            201 => TransactionType::Withdrawal,
            202 => TransactionType::ForceWithdrawal,
            300 => TransactionType::Transfer,
            400 => TransactionType::ResetMainSecret,
            401 => TransactionType::CreateApiSecret,
            402 => TransactionType::DestroyApiSecret,
            500 => TransactionType::SubmitSpotOrder,
            501 => TransactionType::CancelSpotOrder,
            600 => TransactionType::AddAmmV2Liquidity,
            601 => TransactionType::RemoveAmmV2Liquidity,
            700 => TransactionType::RegisterAsset,
            701 => TransactionType::UpdateGlobalWithdrawalFeeTo,
            702 => TransactionType::UpdateAssetWithdrawalFeeTo,
            800 => TransactionType::UpdateChainConfirmationTimes,
            900 => TransactionType::RegisterSpotMarket,
            901 => TransactionType::UpdateSpotMarketTradingSettings,
            902 => TransactionType::UpdateSpotMarketInitialFeeTo,
            903 => TransactionType::UpdateSpotMarketFeeTo,
            904 => TransactionType::UpdateSpotMarketInitialTradingFee,
            905 => TransactionType::UpdateSpotMarketTradingFee,
            906 => TransactionType::ClaimSpotMarketTradingFee,
            907 => TransactionType::UpdateSpotMarketRoyalty,
            908 => TransactionType::ClaimSpotMarketRoyalty,
            1000 => TransactionType::AssetAccountBinding,
            1001 => TransactionType::AssetAccountUnbinding,
            _ => panic!("Invalid transaction type"),
        }
    }
}

impl Into<BigUint> for TransactionType {
    fn into(self) -> BigUint {
        BigUint::from(self as u128)
    }
}

impl_from_uint_for!(TransactionType, u32, u64, u128);
