use crate::{
    OrderId,
    domain::{
        book::{
            MarketId,
            OrderBooksBalances,
            Price,
            Quantity,
        },
        depth::{
            DepthBookChanges,
            DepthView,
        },
        event::{
            Identity,
            OnChainTimeStamp,
            SeenTimeStamp,
        },
        trade::{
            TradeId,
            TradeSide,
        },
    },
    fuel_types::{
        AssetId,
        Bytes32,
        ContractId,
        TxId,
    },
    parse::HexDisplayFromStr,
    primitives::{
        OrderType,
        Side,
    },
};
use serde::{
    Deserialize,
    Serialize,
};
use serde_with::{
    DisplayFromStr,
    serde_as,
};
use std::{
    collections::BTreeMap,
    sync::Arc,
};

#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SubscribeDepthResponse {
    pub action: String,
    pub view: Arc<DepthView>,
    #[serde_as(as = "HexDisplayFromStr")]
    pub market_id: Bytes32,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct SubscribeDepthResponseUpdate {
    pub action: String,
    pub changes: Arc<DepthBookChanges>,
    #[serde_as(as = "HexDisplayFromStr")]
    pub market_id: Bytes32,
    #[serde_as(as = "Option<DisplayFromStr>")]
    pub onchain_timestamp: Option<OnChainTimeStamp>,
    #[serde_as(as = "DisplayFromStr")]
    pub seen_timestamp: SeenTimeStamp,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct OrderHistoryResponse {
    #[serde(rename = "type")]
    pub kind: String,
    pub status: String,
    #[serde_as(as = "HexDisplayFromStr")]
    pub tx_id: TxId,
}

#[serde_as]
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
pub struct Fill {
    #[serde_as(as = "HexDisplayFromStr")]
    pub order_id: OrderId,
    #[serde_as(as = "DisplayFromStr")]
    pub quantity: Quantity,
    #[serde_as(as = "DisplayFromStr")]
    pub price: Price,
    #[serde_as(as = "DisplayFromStr")]
    pub timestamp: u128,
    #[serde_as(as = "DisplayFromStr")]
    pub fee: u64,
}

#[serde_as]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SubscribeOrder {
    #[serde_as(as = "HexDisplayFromStr")]
    pub order_id: OrderId,
    pub owner: Identity,
    #[serde_as(as = "DisplayFromStr")]
    pub side: Side,
    pub order_type: OrderType,
    #[serde_as(as = "HexDisplayFromStr")]
    pub market_id: MarketId,
    #[serde_as(as = "DisplayFromStr")]
    pub quantity: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub quantity_fill: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub price: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub price_fill: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub timestamp: u128,
    pub close: bool,
    pub partially_filled: bool,
    pub cancel: bool,
    pub history: Vec<OrderHistoryResponse>,
    pub fills: Vec<Fill>,
}

#[serde_as]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SubscribeOrdersResponse {
    pub action: String,
    pub orders: Vec<SubscribeOrder>,
    #[serde_as(as = "Option<DisplayFromStr>")]
    pub onchain_timestamp: Option<OnChainTimeStamp>,
    #[serde_as(as = "DisplayFromStr")]
    pub seen_timestamp: SeenTimeStamp,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub struct SubscribeCoinsResponse {
    pub action: String,
    pub trades: Vec<TradeResponse>,
    pub market_id: String,
    #[serde_as(as = "Option<DisplayFromStr>")]
    pub onchain_timestamp: Option<OnChainTimeStamp>,
    #[serde_as(as = "DisplayFromStr")]
    pub seen_timestamp: SeenTimeStamp,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct TradeResponse {
    #[serde_as(as = "DisplayFromStr")]
    pub trade_id: TradeId,
    #[serde_as(as = "DisplayFromStr")]
    pub side: Side,
    #[serde_as(as = "DisplayFromStr")]
    pub total: u128,
    #[serde_as(as = "DisplayFromStr")]
    pub quantity: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub price: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub timestamp: u128,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub struct TradeByAccountResponse {
    #[serde_as(as = "DisplayFromStr")]
    pub trade_id: TradeId,
    #[serde_as(as = "DisplayFromStr")]
    pub side: Side,
    #[serde_as(as = "DisplayFromStr")]
    pub total: u128,
    #[serde_as(as = "DisplayFromStr")]
    pub quantity: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub price: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub timestamp: u128,
    #[serde_as(as = "DisplayFromStr")]
    pub trader_side: TradeSide,
    #[serde_as(as = "DisplayFromStr")]
    pub maker_fee: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub taker_fee: u64,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SubscribeTradeResponse {
    pub action: String,
    pub trades: Vec<TradeResponse>,
    #[serde_as(as = "HexDisplayFromStr")]
    pub market_id: Bytes32,
    #[serde_as(as = "Option<DisplayFromStr>")]
    pub onchain_timestamp: Option<OnChainTimeStamp>,
    #[serde_as(as = "DisplayFromStr")]
    pub seen_timestamp: SeenTimeStamp,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub struct GetTradesResponse {
    pub action: String,
    pub trades: Vec<TradeResponse>,
    #[serde_as(as = "HexDisplayFromStr")]
    pub market_id: Bytes32,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub struct GetTradesByAccountResponse {
    pub action: String,
    pub trades: Vec<TradeByAccountResponse>,
    #[serde_as(as = "HexDisplayFromStr")]
    pub market_id: Bytes32,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub struct GetTradeByAccountResponse {
    pub trade: TradeByAccountResponse,
    #[serde_as(as = "HexDisplayFromStr")]
    pub market_id: Bytes32,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub struct CreateLimitOrderResponse {
    pub action: String,
    #[serde_as(as = "HexDisplayFromStr")]
    pub transaction_id: Bytes32,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub struct CreateStopLimitOrderResponse {
    pub action: String,
    #[serde_as(as = "HexDisplayFromStr")]
    pub transaction_id: Bytes32,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct GetUtxosResponse {
    pub action: String,
    pub utxos: Vec<String>,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
pub struct GetTradeDataResponse {
    pub action: String,
    pub trend: String,
    #[serde_as(as = "DisplayFromStr")]
    pub low_price: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub high_price: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub current_price: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub ratio: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub buys: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub sells: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub buy_volume: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub sell_volume: u64,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct BalanceUpdate {
    pub identity: Identity,
    #[serde_as(as = "HexDisplayFromStr")]
    pub asset_id: AssetId,
    #[serde_as(as = "DisplayFromStr")]
    pub total_locked: u128,
    #[serde_as(as = "DisplayFromStr")]
    pub total_unlocked: u128,
    #[serde_as(as = "DisplayFromStr")]
    pub trading_account_balance: u128,
    #[serde_as(as = "BTreeMap<HexDisplayFromStr, _>")]
    pub order_books: OrderBooksBalances,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SubscribeBalancesResponse {
    pub action: String,
    pub balance: Arc<[BalanceUpdate]>,
    #[serde_as(as = "Option<DisplayFromStr>")]
    pub onchain_timestamp: Option<OnChainTimeStamp>,
    #[serde_as(as = "DisplayFromStr")]
    pub seen_timestamp: SeenTimeStamp,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SubscribeNonceResponse {
    pub action: String,
    #[serde_as(as = "DisplayFromStr")]
    pub contract_id: ContractId,
    #[serde_as(as = "DisplayFromStr")]
    pub nonce: u64,
    #[serde_as(as = "Option<DisplayFromStr>")]
    pub onchain_timestamp: Option<OnChainTimeStamp>,
    #[serde_as(as = "DisplayFromStr")]
    pub seen_timestamp: SeenTimeStamp,
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GetFeeForTradeResponse {
    #[serde_as(as = "DisplayFromStr")]
    pub maker_fee: u64,
    #[serde_as(as = "DisplayFromStr")]
    pub taker_fee: u64,
}
