//! Order Book Client

use crate::dex::types::*;
use reqwest::Client;
use serde_json::json;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

/// Order book sub-client
pub struct OrderBookClient {
    config: DexConfig,
    client: Client,
    closed: Arc<AtomicBool>,
}

impl OrderBookClient {
    pub(crate) fn new(config: DexConfig, client: Client, closed: Arc<AtomicBool>) -> Self {
        Self { config, client, closed }
    }

    /// Get order book for a market
    pub async fn get_order_book(&self, market: &str, depth: u32) -> Result<OrderBook, DexError> {
        self.get(&format!("/orderbook/{}?depth={}", market, depth)).await
    }

    /// Place a limit order
    pub async fn place_limit_order(&self, params: LimitOrderParams) -> Result<Order, DexError> {
        self.post("/orderbook/orders", json!({
            "market": params.market,
            "side": params.side,
            "price": params.price,
            "size": params.size,
            "time_in_force": params.time_in_force,
            "post_only": params.post_only,
        })).await
    }

    /// Cancel an order
    pub async fn cancel_order(&self, order_id: &str) -> Result<(), DexError> {
        self.delete(&format!("/orderbook/orders/{}", order_id)).await
    }

    /// Get all open orders
    pub async fn get_open_orders(&self, market: Option<&str>) -> Result<Vec<Order>, DexError> {
        let path = match market {
            Some(m) => format!("/orderbook/orders?market={}", m),
            None => "/orderbook/orders".to_string(),
        };
        self.get(&path).await
    }

    /// Get order history
    pub async fn get_order_history(&self, limit: u32) -> Result<Vec<Order>, DexError> {
        self.get(&format!("/orderbook/orders/history?limit={}", limit)).await
    }

    // Internal methods

    async fn get<T: serde::de::DeserializeOwned>(&self, path: &str) -> Result<T, DexError> {
        if self.closed.load(Ordering::SeqCst) {
            return Err(DexError::ClientClosed);
        }

        let url = format!("{}{}", self.config.endpoint, path);
        let response = self.client
            .get(&url)
            .header("Content-Type", "application/json")
            .header("Authorization", format!("Bearer {}", self.config.api_key))
            .header("X-SDK-Version", "rust/0.1.0")
            .send()
            .await?;

        if !response.status().is_success() {
            return Err(DexError::Http {
                message: format!("HTTP {}", response.status()),
                code: None,
                status: Some(response.status().as_u16()),
            });
        }

        response.json().await.map_err(DexError::from)
    }

    async fn post<T: serde::de::DeserializeOwned>(&self, path: &str, body: serde_json::Value) -> Result<T, DexError> {
        if self.closed.load(Ordering::SeqCst) {
            return Err(DexError::ClientClosed);
        }

        let url = format!("{}{}", self.config.endpoint, path);
        let response = self.client
            .post(&url)
            .header("Content-Type", "application/json")
            .header("Authorization", format!("Bearer {}", self.config.api_key))
            .header("X-SDK-Version", "rust/0.1.0")
            .json(&body)
            .send()
            .await?;

        if !response.status().is_success() {
            return Err(DexError::Http {
                message: format!("HTTP {}", response.status()),
                code: None,
                status: Some(response.status().as_u16()),
            });
        }

        response.json().await.map_err(DexError::from)
    }

    async fn delete<T: serde::de::DeserializeOwned>(&self, path: &str) -> Result<T, DexError> {
        if self.closed.load(Ordering::SeqCst) {
            return Err(DexError::ClientClosed);
        }

        let url = format!("{}{}", self.config.endpoint, path);
        let response = self.client
            .delete(&url)
            .header("Content-Type", "application/json")
            .header("Authorization", format!("Bearer {}", self.config.api_key))
            .header("X-SDK-Version", "rust/0.1.0")
            .send()
            .await?;

        if !response.status().is_success() {
            return Err(DexError::Http {
                message: format!("HTTP {}", response.status()),
                code: None,
                status: Some(response.status().as_u16()),
            });
        }

        response.json().await.map_err(DexError::from)
    }
}
