use crate::market::{CandidMarketIdentifier, MarketIdentifier};
use crate::{
    number::Ex3Uint, AssetAmount, BlockHeight, CandidAssetAmount, CandidBlockHeight,
    CandidMemeMarketId, CandidWalletRegisterId, MemeMarketId, WalletRegisterId,
};
use candid::{CandidType, Deserialize, Nat};
use ex3_serde::bincode;
use ic_stable_structures::{storable::Bound, Storable};
use serde::Serialize;

/// Represents a meme market with its identifier, trading settings, fee, and fee recipient.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct MemeMarket {
    /// Meme market identifier
    pub identifier: MarketIdentifier,

    /// Market trading settings
    pub trading_settings: MemeMarketTradingSettings,

    /// Trading fee
    pub fee: MemeMarketTradingFee,

    /// Trading fee recipient
    pub fee_to: WalletRegisterId,
}

/// Represents a registered meme market with its ID, market details, and registered height.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct RegisteredMemeMarket {
    /// Meme market ID
    pub id: MemeMarketId,

    /// Meme market details
    pub market: MemeMarket,

    /// Registered block height
    pub registered_height: BlockHeight,
}

impl Storable for RegisteredMemeMarket {
    fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
        bincode::serialize(self).unwrap().into()
    }

    fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
        bincode::deserialize(bytes.as_ref()).unwrap()
    }

    const BOUND: Bound = Bound::Bounded {
        max_size: 320,
        is_fixed_size: false,
    };
}

/// Represents the trading fee structure for a meme market.
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct MemeMarketTradingFee {
    /// The fee that limit orders need to pay
    pub limit_order_fee: Ex3Uint,

    /// Bonding curve fee taking ratio
    /// # Range
    /// 0 ~ 100
    /// # Example
    /// * 0: 0%
    /// * 100: 100%
    /// # Note
    /// This value is used for bonding curve
    pub bonding_curve_fee_taking_ratio: Ex3Uint,

    /// Fee decimal for `limit_order_fee` and `bonding_curve_fee_taking_ratio`
    pub fee_decimal: u8,
}

impl Storable for MemeMarketTradingFee {
    fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
        bincode::serialize(self).unwrap().into()
    }

    fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
        bincode::deserialize(bytes.as_ref()).unwrap()
    }

    const BOUND: Bound = Bound::Bounded {
        max_size: 65,
        is_fixed_size: false,
    };
}

/// Represents the trading settings for a meme market.
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct MemeMarketTradingSettings {
    /// Maximum decimal places for price
    /// Used for limit order price and swap price
    pub price_max_decimal: u8,

    /// Maximum decimal places for volume
    /// The decimal precision for the volume of limit orders and swaps.
    /// Note: volume refers to the quantity of the base asset.
    pub volume_max_decimal: u8,

    /// Minimum order amount
    /// Used for limit order's minimum order amount and swap minimum order amount
    /// Note: amount means quota asset quantity
    pub min_order_amount: AssetAmount,

    /// Minimum order volume
    /// Used for limit order's minimum order volume and swap minimum order volume
    /// Note: volume means base asset quantity
    pub min_order_volume: AssetAmount,
}

/// Represents the trading fee structure for a meme market in Candid format.
#[derive(CandidType, Clone, Debug, Eq, PartialEq, Deserialize)]
pub struct CandidMemeMarketTradingFee {
    /// The fee that limit orders need to pay
    pub limit_order_fee: Nat,

    /// Bonding curve fee taking ratio
    /// # Range
    /// 0 ~ 100
    /// # Example
    /// * 0: 0%
    /// * 100: 100%
    /// # Note
    /// This value is used for bonding curve
    pub bonding_curve_fee_taking_ratio: Nat,

    /// Fee decimal for `limit_order_fee` and `bonding_curve_fee_taking_ratio`
    pub fee_decimal: u8,
}

/// Represents the trading settings for a meme market in Candid format.
#[derive(CandidType, Clone, Debug, Eq, PartialEq, Deserialize)]
pub struct CandidMemeMarketTradingSettings {
    /// Maximum decimal places for price
    /// Used for limit order's price and swap price
    pub price_max_decimal: u8,

    /// Maximum decimal places for volume
    /// The decimal precision for the volume of limit orders and swaps.
    /// Note: volume refers to the quantity of the base asset.
    pub volume_max_decimal: u8,

    /// Minimum order amount
    /// Used for orderbook minimum order amount and swap minimum order amount
    /// Note: amount means quota asset quantity
    pub min_order_amount: CandidAssetAmount,

    /// Minimum order volume
    /// Used for limit order's minimum order volume and swap minimum order volume
    /// Note: volume means base asset quantity
    pub min_order_volume: CandidAssetAmount,
}

/// Represents a meme market in Candid format.
#[derive(CandidType, Clone, Debug, Eq, PartialEq, Deserialize)]
pub struct CandidMemeMarket {
    /// Meme market identifier
    pub identifier: CandidMarketIdentifier,

    /// Market trading settings
    pub trading_settings: CandidMemeMarketTradingSettings,

    /// Market trading fee
    pub fee: CandidMemeMarketTradingFee,

    /// Trading fee recipient
    pub fee_to: CandidWalletRegisterId,
}

/// Represents a registered meme market in Candid format.
#[derive(CandidType, Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct CandidRegisteredMemeMarket {
    /// Meme market ID
    pub id: CandidMemeMarketId,

    /// Meme market details
    pub market: CandidMemeMarket,

    /// Registered block height
    pub registered_height: CandidBlockHeight,
}

impl From<CandidMemeMarketTradingFee> for MemeMarketTradingFee {
    fn from(candid_market_trading_fee: CandidMemeMarketTradingFee) -> Self {
        MemeMarketTradingFee {
            limit_order_fee: candid_market_trading_fee.limit_order_fee.into(),
            bonding_curve_fee_taking_ratio: candid_market_trading_fee
                .bonding_curve_fee_taking_ratio
                .into(),
            fee_decimal: candid_market_trading_fee.fee_decimal,
        }
    }
}

impl From<MemeMarketTradingFee> for CandidMemeMarketTradingFee {
    fn from(market_trading_fee: MemeMarketTradingFee) -> Self {
        CandidMemeMarketTradingFee {
            limit_order_fee: market_trading_fee.limit_order_fee.into(),
            bonding_curve_fee_taking_ratio: market_trading_fee
                .bonding_curve_fee_taking_ratio
                .into(),
            fee_decimal: market_trading_fee.fee_decimal,
        }
    }
}

impl From<CandidMemeMarketTradingSettings> for MemeMarketTradingSettings {
    fn from(candid_market_trading_settings: CandidMemeMarketTradingSettings) -> Self {
        MemeMarketTradingSettings {
            price_max_decimal: candid_market_trading_settings.price_max_decimal,
            volume_max_decimal: candid_market_trading_settings.volume_max_decimal,
            min_order_amount: candid_market_trading_settings.min_order_amount.into(),
            min_order_volume: candid_market_trading_settings.min_order_volume.into(),
        }
    }
}

impl From<MemeMarketTradingSettings> for CandidMemeMarketTradingSettings {
    fn from(market_trading_settings: MemeMarketTradingSettings) -> Self {
        CandidMemeMarketTradingSettings {
            price_max_decimal: market_trading_settings.price_max_decimal,
            volume_max_decimal: market_trading_settings.volume_max_decimal,
            min_order_amount: market_trading_settings.min_order_amount.into(),
            min_order_volume: market_trading_settings.min_order_volume.into(),
        }
    }
}

impl From<CandidMemeMarket> for MemeMarket {
    fn from(candid_market: CandidMemeMarket) -> Self {
        MemeMarket {
            identifier: candid_market.identifier.into(),
            trading_settings: candid_market.trading_settings.into(),
            fee: candid_market.fee.into(),
            fee_to: candid_market.fee_to.into(),
        }
    }
}

impl From<MemeMarket> for CandidMemeMarket {
    fn from(market: MemeMarket) -> Self {
        CandidMemeMarket {
            identifier: market.identifier.into(),
            trading_settings: market.trading_settings.into(),
            fee: market.fee.into(),
            fee_to: market.fee_to.into(),
        }
    }
}

impl From<RegisteredMemeMarket> for CandidRegisteredMemeMarket {
    fn from(registered_spot_market: RegisteredMemeMarket) -> Self {
        CandidRegisteredMemeMarket {
            id: registered_spot_market.id.into(),
            market: registered_spot_market.market.into(),
            registered_height: registered_spot_market.registered_height.into(),
        }
    }
}

impl From<CandidRegisteredMemeMarket> for RegisteredMemeMarket {
    fn from(candid_registered_spot_market: CandidRegisteredMemeMarket) -> Self {
        RegisteredMemeMarket {
            id: candid_registered_spot_market.id.into(),
            market: candid_registered_spot_market.market.into(),
            registered_height: candid_registered_spot_market.registered_height.into(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::AssetId;

    /// Test the stability of the `RegisteredMemeMarket` struct's serialization and deserialization.
    #[test]
    fn test_stable_registered_meme_market() {
        let registered_meme_market = RegisteredMemeMarket {
            id: Ex3Uint::from(u128::MAX),
            market: MemeMarket {
                identifier: MarketIdentifier {
                    base_asset_id: AssetId::from(u128::MAX),
                    quote_asset_id: AssetId::from(u128::MAX),
                },
                trading_settings: MemeMarketTradingSettings {
                    price_max_decimal: 8,
                    volume_max_decimal: 8,
                    min_order_amount: AssetAmount::from(u128::MAX),
                    min_order_volume: AssetAmount::from(u128::MAX),
                },
                fee: MemeMarketTradingFee {
                    limit_order_fee: Ex3Uint::from(u128::MAX),
                    bonding_curve_fee_taking_ratio: Ex3Uint::from(u128::MAX),
                    fee_decimal: 2,
                },
                fee_to: WalletRegisterId::from(u128::MAX),
            },
            registered_height: BlockHeight::from(u128::MAX),
        };

        let bytes = registered_meme_market.to_bytes();
        assert!(bytes.len() <= RegisteredMemeMarket::BOUND.max_size() as usize);
    }
}
