use crate::domain::book::{
    MarketId,
    Price,
};
use serde_with::{
    DisplayFromStr,
    serde_as,
};
use std::ops::Sub;

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct DepthView {
    pub precision: Precision,
    pub buys: Vec<DepthPriceView>,
    pub sells: Vec<DepthPriceView>,
}

#[serde_as]
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
pub struct DepthPriceView {
    #[serde_as(as = "DisplayFromStr")]
    pub price: Price,
    #[serde_as(as = "DisplayFromStr")]
    pub quantity: u128,
}

#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
pub struct Precision(usize);

impl Sub for Precision {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self::Output {
        let precision = self.0.saturating_sub(rhs.0);
        Self(precision)
    }
}

impl Precision {
    pub const MIN: usize = 0;
    pub const MAX: usize = 18;

    pub fn new(precision: usize) -> Self {
        let precision = precision.min(Self::MAX);
        let precision = precision.max(Self::MIN);
        Self(precision)
    }

    pub fn max() -> Self {
        Self(Self::MAX)
    }

    pub fn min() -> Self {
        Self(Self::MIN)
    }

    pub fn value(&self) -> usize {
        self.0
    }
}

#[derive(Clone, Default, Debug, serde::Deserialize, serde::Serialize)]
pub struct DepthBookChanges {
    pub buys: Vec<DepthBookChange>,
    pub sells: Vec<DepthBookChange>,
}

#[serde_as]
#[derive(Clone, Copy, Default, Debug, serde::Deserialize, serde::Serialize)]
pub struct DepthBookChange {
    #[serde_as(as = "DisplayFromStr")]
    pub price: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub quantity: i128,
}

/// Depth request params.
#[serde_as]
#[derive(serde::Deserialize, Debug)]
pub struct GetDepthParams {
    pub market_id: MarketId,
    #[serde_as(as = "DisplayFromStr")]
    pub precision: u64,
}

/// Depth response.
#[derive(serde::Serialize)]
pub struct GetDepthResponse {
    pub action: String,
    pub orders: DepthOrders,
    pub market_id: MarketId,
}

#[serde_as]
#[derive(serde::Serialize)]
pub struct DepthOrders {
    pub buys: Vec<DepthOrder>,
    pub sells: Vec<DepthOrder>,
}

#[serde_as]
#[derive(serde::Serialize)]
pub struct DepthOrder {
    #[serde_as(as = "DisplayFromStr")]
    pub quantity: u128,
    #[serde_as(as = "DisplayFromStr")]
    pub price: u64,
}
