use super::{OrderScope, OrderSide};
use crate::{AssetAmount, MemeMarketId, SpotMarketId};
use serde::{Deserialize, Serialize};

/// Meme order detail
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct MemeOrderDetail {
    /// Order side
    pub side: OrderSide,

    /// Order price
    pub price: AssetAmount,

    /// Order volume
    /// Note: volume means base asset quantity
    pub volume: AssetAmount,
}

/// Bonding curve exact tokens
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub enum BondingCurveExactTokens {
    /// Exact input
    /// Note: exact input means the input asset quantity is fixed
    ExactInput {
        /// Input amount
        /// Note: input amount means the input asset quantity
        amount: AssetAmount,
        /// Min out
        /// Note: min out means the output asset quantity must be greater than or equal to the min out
        min_out: AssetAmount,
    },
}

/// Bonding curve order detail
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct BondingCurveOrderDetail {
    /// Order side
    pub side: OrderSide,

    /// Exact tokens
    pub exact_tokens: BondingCurveExactTokens,
}

/// Order detail
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub enum MemeOrder {
    /// Meme order detail
    LimitOrder(MemeOrderDetail),

    /// Bonding curve order detail
    BondingCurve(BondingCurveOrderDetail),
}

/// Submit meme order
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct SubmitMemeOrder {
    /// Market id
    pub market_id: MemeMarketId,

    /// Order
    pub order: MemeOrder,
}

/// Cancel meme order
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct CancelMemeOrder {
    /// Market id
    pub market_id: SpotMarketId,

    /// Cancel order scope
    pub scope: OrderScope,
}

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

    /// Test serialization and deserialization of `SubmitMemeOrder`
    #[test]
    fn test_submit_order_serde() {
        let submit_order = SubmitMemeOrder {
            market_id: SpotMarketId::from(1u8),
            order: MemeOrder::LimitOrder(MemeOrderDetail {
                side: OrderSide::Ask,
                price: 100u32.into(),
                volume: 100u32.into(),
            }),
        };

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

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

    /// Test serialization and deserialization of `CancelMemeOrder`
    #[test]
    fn test_cancel_order_serde() {
        let cancel_order = CancelMemeOrder {
            market_id: SpotMarketId::from(1u8),
            scope: OrderScope::Exact(OrderId::from(1u8)),
        };

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

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