use crate::{
    OrderId,
    primitives::Side,
};
use std::{
    fmt::{
        Display,
        Formatter,
    },
    str::FromStr,
};

#[derive(
    Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash, Default,
)]
pub struct TradeId {
    pub maker_id: OrderId,
    pub taker_id: OrderId,
}

impl TradeId {
    pub fn new(maker_id: OrderId, taker_id: OrderId) -> Self {
        Self { maker_id, taker_id }
    }
}

impl Display for TradeId {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_fmt(format_args!("{}/{}", self.maker_id, self.taker_id))
    }
}

impl FromStr for TradeId {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts: Vec<&str> = s.split('/').collect();
        if parts.len() != 2 {
            return Err(anyhow::anyhow!("Invalid TradeId format"));
        }
        let maker_id: OrderId = parts[0].parse().map_err(|e| anyhow::anyhow!("{}", e))?;
        let taker_id: OrderId = parts[1].parse().map_err(|e| anyhow::anyhow!("{}", e))?;
        Ok(TradeId { maker_id, taker_id })
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Trade {
    pub trade_id: TradeId,
    pub side: Side,
    pub total: u128,
    pub quantity: u64,
    pub price: u64,
    pub timestamp: u128,
    pub maker_fee: u64,
    pub taker_fee: u64,
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
pub enum TradeSide {
    Maker,
    Taker,
    Both,
}

impl Display for TradeSide {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            TradeSide::Maker => write!(f, "maker"),
            TradeSide::Taker => write!(f, "taker"),
            TradeSide::Both => write!(f, "both"),
        }
    }
}

impl FromStr for TradeSide {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "maker" => Ok(TradeSide::Maker),
            "taker" => Ok(TradeSide::Taker),
            "both" => Ok(TradeSide::Both),
            _ => Err(anyhow::anyhow!("Invalid trade side: {s}")),
        }
    }
}
