//! Perpetuals Client

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

/// Perpetuals sub-client
pub struct PerpsClient {
    config: DexConfig,
    client: Client,
    closed: Arc<AtomicBool>,
}

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

    /// List all perpetual markets
    pub async fn list_markets(&self) -> Result<Vec<PerpMarket>, DexError> {
        self.get("/perps/markets").await
    }

    /// Get a specific perpetual market
    pub async fn get_market(&self, symbol: &str) -> Result<PerpMarket, DexError> {
        self.get(&format!("/perps/markets/{}", symbol)).await
    }

    /// Open a perpetual position
    pub async fn open_position(&self, params: OpenPositionParams) -> Result<PerpPosition, DexError> {
        let mut body = json!({
            "market": params.market,
            "side": params.side,
            "size": params.size,
            "leverage": params.leverage,
            "order_type": params.order_type,
            "margin_type": params.margin_type,
            "reduce_only": params.reduce_only,
        });

        if let Some(price) = params.limit_price {
            body["limit_price"] = json!(price);
        }
        if let Some(sl) = params.stop_loss {
            body["stop_loss"] = json!(sl);
        }
        if let Some(tp) = params.take_profit {
            body["take_profit"] = json!(tp);
        }

        self.post("/perps/positions", body).await
    }

    /// Close a perpetual position
    pub async fn close_position(&self, params: ClosePositionParams) -> Result<PerpPosition, DexError> {
        let mut body = json!({
            "market": params.market,
            "order_type": params.order_type,
        });

        if let Some(size) = params.size {
            body["size"] = json!(size);
        }
        if let Some(price) = params.limit_price {
            body["limit_price"] = json!(price);
        }

        self.post("/perps/positions/close", body).await
    }

    /// Modify a perpetual position
    pub async fn modify_position(&self, params: ModifyPositionParams) -> Result<PerpPosition, DexError> {
        let mut body = json!({});

        if let Some(leverage) = params.new_leverage {
            body["new_leverage"] = json!(leverage);
        }
        if let Some(margin) = params.new_margin {
            body["new_margin"] = json!(margin);
        }
        if let Some(sl) = params.new_stop_loss {
            body["new_stop_loss"] = json!(sl);
        }
        if let Some(tp) = params.new_take_profit {
            body["new_take_profit"] = json!(tp);
        }

        self.post(&format!("/perps/positions/{}/modify", params.position_id), body).await
    }

    /// Get all open positions
    pub async fn get_positions(&self) -> Result<Vec<PerpPosition>, DexError> {
        self.get("/perps/positions").await
    }

    /// Get position for a specific market
    pub async fn get_position(&self, market: &str) -> Result<Option<PerpPosition>, DexError> {
        self.get(&format!("/perps/positions/{}", market)).await
    }

    /// Get all open orders
    pub async fn get_orders(&self) -> Result<Vec<PerpOrder>, DexError> {
        self.get("/perps/orders").await
    }

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

    /// Cancel all orders
    pub async fn cancel_all_orders(&self, market: Option<&str>) -> Result<u32, DexError> {
        let path = match market {
            Some(m) => format!("/perps/orders?market={}", m),
            None => "/perps/orders".to_string(),
        };

        #[derive(serde::Deserialize)]
        struct CancelResult {
            cancelled: u32,
        }

        let result: CancelResult = self.delete(&path).await?;
        Ok(result.cancelled)
    }

    /// Get funding payment history
    pub async fn get_funding_history(&self, market: &str, limit: u32) -> Result<Vec<FundingPayment>, DexError> {
        self.get(&format!("/perps/funding/{}?limit={}", market, limit)).await
    }

    /// Get current funding rate
    pub async fn get_funding_rate(&self, market: &str) -> Result<FundingRateInfo, DexError> {
        self.get(&format!("/perps/funding/{}/current", market)).await
    }

    // Internal methods

    async fn get<T: serde::de::DeserializeOwned>(&self, path: &str) -> Result<T, DexError> {
        use std::sync::atomic::Ordering;

        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> {
        use std::sync::atomic::Ordering;

        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> {
        use std::sync::atomic::Ordering;

        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)
    }
}
