use crate::market::MarketIdentifier;
use crate::{number::Ex3Uint, AssetAmount, BlockHeight, SpotMarketId, WalletRegisterId};
use candid::Deserialize;
use ex3_serde::bincode;
use ic_stable_structures::{storable::Bound, Storable};
use serde::Serialize;

/// Represents a spot market with its identifier, trading settings, fee, fee recipient, and optional royalty.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct SpotMarket {
    /// Spot market identifier
    pub identifier: MarketIdentifier,

    /// Market trading settings
    pub trading_settings: SpotMarketTradingSettings,

    /// Trading fee
    pub fee: SpotMarketTradingFee,

    /// Trading fee recipient
    pub fee_to: WalletRegisterId,

    /// Optional royalty fee
    pub royalty: Option<SpotMarketRoyalty>,
}

/// Represents a registered spot market with its ID, market details, and registered height.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct RegisteredSpotMarket {
    /// Spot market ID
    pub id: SpotMarketId,

    /// Spot market details
    pub market: SpotMarket,

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

impl Storable for RegisteredSpotMarket {
    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: 384,
        is_fixed_size: false,
    };
}

/// Represents the trading fee structure for a spot market.
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct SpotMarketTradingFee {
    /// Limit order taker fee
    /// Swap fee is the same as limit order taker fee
    pub taker_fee: Ex3Uint,

    /// Limit order maker fee
    pub maker_fee: Ex3Uint,

    /// AMM V2 fee taking ratio
    /// # Range
    /// 0 ~ 100
    /// # Example
    /// * 0: 0%
    /// * 100: 100%
    /// # Note
    /// This value is used for AMM V2
    pub amm_v2_fee_taking_ratio: Ex3Uint,

    /// Fee decimal for
    /// * `taker_fee` & `maker_fee` & `amm_v2_fee_taking_ratio`
    pub fee_decimal: u8,
}

impl Storable for SpotMarketTradingFee {
    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: 97,
        is_fixed_size: false,
    };
}

/// Represents the royalty fee structure for a spot market.
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct SpotMarketRoyalty {
    /// Royalty fee for buyer
    pub buyer_fee: Ex3Uint,

    /// Royalty fee for seller
    pub seller_fee: Ex3Uint,

    /// Fee decimal for
    /// * `buyer_fee` & `seller_fee`
    pub fee_decimal: u8,
}

impl Storable for SpotMarketRoyalty {
    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 orderbook price limitation.
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct SpotOrderPriceLimitation {
    /// Orderbook bidding price rate cap
    pub bid_price_rate_cap: Ex3Uint,
    /// Orderbook asking price rate floor
    pub ask_price_rate_floor: Ex3Uint,
    /// Price rate decimal
    pub rate_decimal: u8,
}

/// Represents the trading settings for a spot market.
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct SpotMarketTradingSettings {
    /// Maximum decimal places for price
    /// Orderbook price decimal & swap price decimal use this value
    pub price_max_decimal: u8,

    /// Maximum decimal places for volume
    /// Orderbook volume decimal & swap volume decimal use this value
    /// Note: volume means base asset quantity
    pub volume_max_decimal: u8,

    /// Minimum order amount
    /// Orderbook minimum order amount & swap minimum order amount use this value
    /// Note: amount means quota asset quantity
    pub min_order_amount: AssetAmount,

    /// Minimum order volume
    /// Orderbook minimum order volume & swap minimum order volume use this value
    /// Note: volume means base asset quantity
    pub min_order_volume: AssetAmount,

    /// Price limitation
    pub price_limitation: SpotOrderPriceLimitation,
}