/*
 * Prediction Trading REST API
 *
 * Place and manage prediction market orders, query positions, and transfer funds via the Prediction Trading REST API.
 *
 * The version of the OpenAPI document: 1.0.0
 *
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */

#![allow(unused_imports)]
use async_trait::async_trait;
use derive_builder::Builder;
use reqwest;
use rust_decimal::prelude::*;
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
use std::collections::BTreeMap;

use crate::common::{
    config::ConfigurationRestApi,
    models::{ParamBuildError, RestApiResponse},
    utils::send_request,
};
use crate::w3w_prediction::rest_api::models;

const HAS_TIME_UNIT: bool = false;

#[async_trait]
pub trait TradeApi: Send + Sync {
    async fn batch_cancel_orders(
        &self,
        params: BatchCancelOrdersParams,
    ) -> anyhow::Result<RestApiResponse<models::BatchCancelOrdersResponse>>;
    async fn get_quote(
        &self,
        params: GetQuoteParams,
    ) -> anyhow::Result<RestApiResponse<models::GetQuoteResponse>>;
    async fn place_order(
        &self,
        params: PlaceOrderParams,
    ) -> anyhow::Result<RestApiResponse<models::PlaceOrderResponse>>;
    async fn query_active_orders(
        &self,
        params: QueryActiveOrdersParams,
    ) -> anyhow::Result<RestApiResponse<models::QueryActiveOrdersResponse>>;
    async fn query_order_history(
        &self,
        params: QueryOrderHistoryParams,
    ) -> anyhow::Result<RestApiResponse<models::QueryOrderHistoryResponse>>;
}

#[derive(Debug, Clone)]
pub struct TradeApiClient {
    configuration: ConfigurationRestApi,
}

impl TradeApiClient {
    pub fn new(configuration: ConfigurationRestApi) -> Self {
        Self { configuration }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum GetQuoteSideEnum {
    #[serde(rename = "BUY")]
    Buy,
    #[serde(rename = "SELL")]
    Sell,
}

impl GetQuoteSideEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

impl std::str::FromStr for GetQuoteSideEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "BUY" => Ok(Self::Buy),
            "SELL" => Ok(Self::Sell),
            other => Err(format!("invalid GetQuoteSideEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum GetQuoteOrderTypeEnum {
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
}

impl GetQuoteOrderTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
        }
    }
}

impl std::str::FromStr for GetQuoteOrderTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            other => Err(format!("invalid GetQuoteOrderTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum GetQuoteFundingSourceEnum {
    #[serde(rename = "MPC")]
    Mpc,
    #[serde(rename = "CEX")]
    Cex,
}

impl GetQuoteFundingSourceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Mpc => "MPC",
            Self::Cex => "CEX",
        }
    }
}

impl std::str::FromStr for GetQuoteFundingSourceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "MPC" => Ok(Self::Mpc),
            "CEX" => Ok(Self::Cex),
            other => Err(format!("invalid GetQuoteFundingSourceEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PlaceOrderAccountTypeEnum {
    #[serde(rename = "SPOT")]
    Spot,
    #[serde(rename = "FUNDING")]
    Funding,
}

impl PlaceOrderAccountTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Spot => "SPOT",
            Self::Funding => "FUNDING",
        }
    }
}

impl std::str::FromStr for PlaceOrderAccountTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "SPOT" => Ok(Self::Spot),
            "FUNDING" => Ok(Self::Funding),
            other => Err(format!("invalid PlaceOrderAccountTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PlaceOrderOrderTypeEnum {
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
}

impl PlaceOrderOrderTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
        }
    }
}

impl std::str::FromStr for PlaceOrderOrderTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            other => Err(format!("invalid PlaceOrderOrderTypeEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PlaceOrderFundingSourceEnum {
    #[serde(rename = "MPC")]
    Mpc,
    #[serde(rename = "CEX")]
    Cex,
}

impl PlaceOrderFundingSourceEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Mpc => "MPC",
            Self::Cex => "CEX",
        }
    }
}

impl std::str::FromStr for PlaceOrderFundingSourceEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "MPC" => Ok(Self::Mpc),
            "CEX" => Ok(Self::Cex),
            other => Err(format!("invalid PlaceOrderFundingSourceEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum QueryActiveOrdersTradeSideEnum {
    #[serde(rename = "BUY")]
    Buy,
    #[serde(rename = "SELL")]
    Sell,
}

impl QueryActiveOrdersTradeSideEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

impl std::str::FromStr for QueryActiveOrdersTradeSideEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "BUY" => Ok(Self::Buy),
            "SELL" => Ok(Self::Sell),
            other => Err(format!("invalid QueryActiveOrdersTradeSideEnum: {}", other).into()),
        }
    }
}

#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum QueryOrderHistoryOrderTypeEnum {
    #[serde(rename = "MARKET")]
    Market,
    #[serde(rename = "LIMIT")]
    Limit,
}

impl QueryOrderHistoryOrderTypeEnum {
    #[must_use]
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Market => "MARKET",
            Self::Limit => "LIMIT",
        }
    }
}

impl std::str::FromStr for QueryOrderHistoryOrderTypeEnum {
    type Err = Box<dyn std::error::Error + Send + Sync>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "MARKET" => Ok(Self::Market),
            "LIMIT" => Ok(Self::Limit),
            other => Err(format!("invalid QueryOrderHistoryOrderTypeEnum: {}", other).into()),
        }
    }
}

/// Request parameters for the [`batch_cancel_orders`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`batch_cancel_orders`](#method.batch_cancel_orders).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct BatchCancelOrdersParams {
    /// User's prediction wallet address
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub wallet_address: String,
    /// Wallet ID
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub wallet_id: String,
    /// List of orders to cancel (index `i` starts from 0)
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub cancel_info_list: Option<Vec<models::BatchCancelOrdersCancelInfoListParameterInner>>,
}

impl BatchCancelOrdersParams {
    /// Create a builder for [`batch_cancel_orders`].
    ///
    /// Required parameters:
    ///
    /// * `wallet_address` — User's prediction wallet address
    /// * `wallet_id` — Wallet ID
    ///
    #[must_use]
    pub fn builder(wallet_address: String, wallet_id: String) -> BatchCancelOrdersParamsBuilder {
        BatchCancelOrdersParamsBuilder::default()
            .wallet_address(wallet_address)
            .wallet_id(wallet_id)
    }
}
/// Request parameters for the [`get_quote`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`get_quote`](#method.get_quote).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct GetQuoteParams {
    /// User's prediction wallet address
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub wallet_address: String,
    /// Prediction outcome token ID
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub token_id: String,
    /// Trade direction. Enum: `BUY`, `SELL`
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub side: GetQuoteSideEnum,
    /// Input amount in wei (18 decimals). Must be > 0. For `MARKET` orders, minimum is approximately 1.5 USDT (varies by market depth). Example: `1000000000000000000` = 1 USDT
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub amount_in: String,
    /// Order type. Enum: `MARKET`, `LIMIT`
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub order_type: GetQuoteOrderTypeEnum,
    /// Slippage tolerance in basis points. Range 1–10000
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub slippage_bps: i32,
    /// Limit price. Required when `orderType=LIMIT`. Must be > 0
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub price_limit: Option<String>,
    /// Chain ID. Default `56` (BSC)
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub chain_id: Option<String>,
    /// Fee rate in basis points. Default `200`, range 1–10000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub fee_rate_bps: Option<i32>,
    /// Funding source. Enum: `MPC`, `CEX`. Default `MPC`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub funding_source: Option<GetQuoteFundingSourceEnum>,
    /// Auto-transfer amount before order (wei). Must be > 0 if provided
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub fund_transfer_amount: Option<String>,
}

impl GetQuoteParams {
    /// Create a builder for [`get_quote`].
    ///
    /// Required parameters:
    ///
    /// * `wallet_address` — User's prediction wallet address
    /// * `token_id` — Prediction outcome token ID
    /// * `side` — Trade direction. Enum: `BUY`, `SELL`
    /// * `amount_in` — Input amount in wei (18 decimals). Must be > 0. For `MARKET` orders, minimum is approximately 1.5 USDT (varies by market depth). Example: `1000000000000000000` = 1 USDT
    /// * `order_type` — Order type. Enum: `MARKET`, `LIMIT`
    /// * `slippage_bps` — Slippage tolerance in basis points. Range 1–10000
    ///
    #[must_use]
    pub fn builder(
        wallet_address: String,
        token_id: String,
        side: GetQuoteSideEnum,
        amount_in: String,
        order_type: GetQuoteOrderTypeEnum,
        slippage_bps: i32,
    ) -> GetQuoteParamsBuilder {
        GetQuoteParamsBuilder::default()
            .wallet_address(wallet_address)
            .token_id(token_id)
            .side(side)
            .amount_in(amount_in)
            .order_type(order_type)
            .slippage_bps(slippage_bps)
    }
}
/// Request parameters for the [`place_order`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`place_order`](#method.place_order).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct PlaceOrderParams {
    /// User's prediction wallet address
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub wallet_address: String,
    /// Wallet ID
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub wallet_id: String,
    /// Quote ID obtained from `Get Quote`
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub quote_id: String,
    /// Must match `orderType`: `FOK` for `MARKET`, `GTC` for `LIMIT`
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub time_in_force: String,
    /// Payment account type. Enum: `SPOT`, `FUNDING`
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub account_type: PlaceOrderAccountTypeEnum,
    /// Order type. Enum: `MARKET`, `LIMIT`
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub order_type: PlaceOrderOrderTypeEnum,
    /// Slippage tolerance in basis points. Range 1–10000
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub slippage_bps: i32,
    /// Limit price. Required when `orderType=LIMIT`. Must be > 0
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub price_limit: Option<String>,
    /// Funding source. Enum: `MPC`, `CEX`. Default `MPC`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub funding_source: Option<PlaceOrderFundingSourceEnum>,
    /// Auto-transfer amount before order (wei). Must be > 0 if provided
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub fund_transfer_amount: Option<String>,
}

impl PlaceOrderParams {
    /// Create a builder for [`place_order`].
    ///
    /// Required parameters:
    ///
    /// * `wallet_address` — User's prediction wallet address
    /// * `wallet_id` — Wallet ID
    /// * `quote_id` — Quote ID obtained from `Get Quote`
    /// * `time_in_force` — Must match `orderType`: `FOK` for `MARKET`, `GTC` for `LIMIT`
    /// * `account_type` — Payment account type. Enum: `SPOT`, `FUNDING`
    /// * `order_type` — Order type. Enum: `MARKET`, `LIMIT`
    /// * `slippage_bps` — Slippage tolerance in basis points. Range 1–10000
    ///
    #[must_use]
    pub fn builder(
        wallet_address: String,
        wallet_id: String,
        quote_id: String,
        time_in_force: String,
        account_type: PlaceOrderAccountTypeEnum,
        order_type: PlaceOrderOrderTypeEnum,
        slippage_bps: i32,
    ) -> PlaceOrderParamsBuilder {
        PlaceOrderParamsBuilder::default()
            .wallet_address(wallet_address)
            .wallet_id(wallet_id)
            .quote_id(quote_id)
            .time_in_force(time_in_force)
            .account_type(account_type)
            .order_type(order_type)
            .slippage_bps(slippage_bps)
    }
}
/// Request parameters for the [`query_active_orders`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`query_active_orders`](#method.query_active_orders).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct QueryActiveOrdersParams {
    /// User's prediction wallet address
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub wallet_address: String,
    /// Filter by trade side. Enum: `BUY`, `SELL`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub trade_side: Option<QueryActiveOrdersTradeSideEnum>,
    /// Filter by level-1 category
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub l1_category: Option<String>,
    /// Filter by market ID
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub market_id: Option<i64>,
    /// Pagination offset. Default `0`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub offset: Option<i32>,
    /// Page size. Default `20`, range 1–100
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i32>,
    /// Request validity window in milliseconds
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl QueryActiveOrdersParams {
    /// Create a builder for [`query_active_orders`].
    ///
    /// Required parameters:
    ///
    /// * `wallet_address` — User's prediction wallet address
    ///
    #[must_use]
    pub fn builder(wallet_address: String) -> QueryActiveOrdersParamsBuilder {
        QueryActiveOrdersParamsBuilder::default().wallet_address(wallet_address)
    }
}
/// Request parameters for the [`query_order_history`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`query_order_history`](#method.query_order_history).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct QueryOrderHistoryParams {
    /// User's prediction wallet address
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub wallet_address: String,
    /// Filter by level-1 category
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub l1_category: Option<String>,
    /// Filter by order type. Enum: `MARKET`, `LIMIT`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub order_type: Option<QueryOrderHistoryOrderTypeEnum>,
    /// Filter by order status
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub status: Option<String>,
    /// Start date. Format: `yyyy-MM-dd`. Must be ≤ `endDate`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_date: Option<String>,
    /// End date. Format: `yyyy-MM-dd`. Must be ≥ `startDate`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_date: Option<String>,
    /// Pagination offset. Default `0`
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub offset: Option<i32>,
    /// Page size. Default `20`, range 1–100
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub limit: Option<i32>,
    /// Request validity window in milliseconds
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl QueryOrderHistoryParams {
    /// Create a builder for [`query_order_history`].
    ///
    /// Required parameters:
    ///
    /// * `wallet_address` — User's prediction wallet address
    ///
    #[must_use]
    pub fn builder(wallet_address: String) -> QueryOrderHistoryParamsBuilder {
        QueryOrderHistoryParamsBuilder::default().wallet_address(wallet_address)
    }
}

#[async_trait]
impl TradeApi for TradeApiClient {
    async fn batch_cancel_orders(
        &self,
        params: BatchCancelOrdersParams,
    ) -> anyhow::Result<RestApiResponse<models::BatchCancelOrdersResponse>> {
        let BatchCancelOrdersParams {
            wallet_address,
            wallet_id,
            cancel_info_list,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("walletAddress".to_string(), json!(wallet_address));

        query_params.insert("walletId".to_string(), json!(wallet_id));

        if let Some(rw) = cancel_info_list {
            query_params.insert("cancelInfoList".to_string(), json!(rw));
        }

        send_request::<models::BatchCancelOrdersResponse>(
            &self.configuration,
            "/sapi/v1/w3w/wallet/prediction/trade/batch-cancel",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn get_quote(
        &self,
        params: GetQuoteParams,
    ) -> anyhow::Result<RestApiResponse<models::GetQuoteResponse>> {
        let GetQuoteParams {
            wallet_address,
            token_id,
            side,
            amount_in,
            order_type,
            slippage_bps,
            price_limit,
            chain_id,
            fee_rate_bps,
            funding_source,
            fund_transfer_amount,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("walletAddress".to_string(), json!(wallet_address));

        query_params.insert("tokenId".to_string(), json!(token_id));

        query_params.insert("side".to_string(), json!(side));

        query_params.insert("amountIn".to_string(), json!(amount_in));

        query_params.insert("orderType".to_string(), json!(order_type));

        query_params.insert("slippageBps".to_string(), json!(slippage_bps));

        if let Some(rw) = price_limit {
            query_params.insert("priceLimit".to_string(), json!(rw));
        }

        if let Some(rw) = chain_id {
            query_params.insert("chainId".to_string(), json!(rw));
        }

        if let Some(rw) = fee_rate_bps {
            query_params.insert("feeRateBps".to_string(), json!(rw));
        }

        if let Some(rw) = funding_source {
            query_params.insert("fundingSource".to_string(), json!(rw));
        }

        if let Some(rw) = fund_transfer_amount {
            query_params.insert("fundTransferAmount".to_string(), json!(rw));
        }

        send_request::<models::GetQuoteResponse>(
            &self.configuration,
            "/sapi/v1/w3w/wallet/prediction/trade/get-quote",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn place_order(
        &self,
        params: PlaceOrderParams,
    ) -> anyhow::Result<RestApiResponse<models::PlaceOrderResponse>> {
        let PlaceOrderParams {
            wallet_address,
            wallet_id,
            quote_id,
            time_in_force,
            account_type,
            order_type,
            slippage_bps,
            price_limit,
            funding_source,
            fund_transfer_amount,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("walletAddress".to_string(), json!(wallet_address));

        query_params.insert("walletId".to_string(), json!(wallet_id));

        query_params.insert("quoteId".to_string(), json!(quote_id));

        query_params.insert("timeInForce".to_string(), json!(time_in_force));

        query_params.insert("accountType".to_string(), json!(account_type));

        query_params.insert("orderType".to_string(), json!(order_type));

        query_params.insert("slippageBps".to_string(), json!(slippage_bps));

        if let Some(rw) = price_limit {
            query_params.insert("priceLimit".to_string(), json!(rw));
        }

        if let Some(rw) = funding_source {
            query_params.insert("fundingSource".to_string(), json!(rw));
        }

        if let Some(rw) = fund_transfer_amount {
            query_params.insert("fundTransferAmount".to_string(), json!(rw));
        }

        send_request::<models::PlaceOrderResponse>(
            &self.configuration,
            "/sapi/v1/w3w/wallet/prediction/trade/place-order-bundle",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn query_active_orders(
        &self,
        params: QueryActiveOrdersParams,
    ) -> anyhow::Result<RestApiResponse<models::QueryActiveOrdersResponse>> {
        let QueryActiveOrdersParams {
            wallet_address,
            trade_side,
            l1_category,
            market_id,
            offset,
            limit,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("walletAddress".to_string(), json!(wallet_address));

        if let Some(rw) = trade_side {
            query_params.insert("tradeSide".to_string(), json!(rw));
        }

        if let Some(rw) = l1_category {
            query_params.insert("l1Category".to_string(), json!(rw));
        }

        if let Some(rw) = market_id {
            query_params.insert("marketId".to_string(), json!(rw));
        }

        if let Some(rw) = offset {
            query_params.insert("offset".to_string(), json!(rw));
        }

        if let Some(rw) = limit {
            query_params.insert("limit".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::QueryActiveOrdersResponse>(
            &self.configuration,
            "/sapi/v1/w3w/wallet/prediction/order/list",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn query_order_history(
        &self,
        params: QueryOrderHistoryParams,
    ) -> anyhow::Result<RestApiResponse<models::QueryOrderHistoryResponse>> {
        let QueryOrderHistoryParams {
            wallet_address,
            l1_category,
            order_type,
            status,
            start_date,
            end_date,
            offset,
            limit,
            recv_window,
        } = params;

        let mut query_params = BTreeMap::new();
        let body_params = BTreeMap::new();

        query_params.insert("walletAddress".to_string(), json!(wallet_address));

        if let Some(rw) = l1_category {
            query_params.insert("l1Category".to_string(), json!(rw));
        }

        if let Some(rw) = order_type {
            query_params.insert("orderType".to_string(), json!(rw));
        }

        if let Some(rw) = status {
            query_params.insert("status".to_string(), json!(rw));
        }

        if let Some(rw) = start_date {
            query_params.insert("startDate".to_string(), json!(rw));
        }

        if let Some(rw) = end_date {
            query_params.insert("endDate".to_string(), json!(rw));
        }

        if let Some(rw) = offset {
            query_params.insert("offset".to_string(), json!(rw));
        }

        if let Some(rw) = limit {
            query_params.insert("limit".to_string(), json!(rw));
        }

        if let Some(rw) = recv_window {
            query_params.insert("recvWindow".to_string(), json!(rw));
        }

        send_request::<models::QueryOrderHistoryResponse>(
            &self.configuration,
            "/sapi/v1/w3w/wallet/prediction/order/history",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }
}

#[cfg(all(test, feature = "w3w_prediction"))]
mod tests {
    use super::*;
    use crate::TOKIO_SHARED_RT;
    use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
    use async_trait::async_trait;
    use std::collections::HashMap;

    struct DummyRestApiResponse<T> {
        inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
        status: u16,
        headers: HashMap<String, String>,
        rate_limits: Option<Vec<RestApiRateLimit>>,
    }

    impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
        fn from(dummy: DummyRestApiResponse<T>) -> Self {
            Self {
                data_fn: dummy.inner,
                status: dummy.status,
                headers: dummy.headers,
                rate_limits: dummy.rate_limits,
            }
        }
    }

    struct MockTradeApiClient {
        force_error: bool,
    }

    #[async_trait]
    impl TradeApi for MockTradeApiClient {
        async fn batch_cancel_orders(
            &self,
            _params: BatchCancelOrdersParams,
        ) -> anyhow::Result<RestApiResponse<models::BatchCancelOrdersResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"canceled":["54124"],"failed":[{"orderId":"54126","reason":"ORDER_NOT_FOUND"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
            let dummy_response: models::BatchCancelOrdersResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::BatchCancelOrdersResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn get_quote(
            &self,
            _params: GetQuoteParams,
        ) -> anyhow::Result<RestApiResponse<models::GetQuoteResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"quoteId":"q_20260525_abc123xyz","tokenId":"112233","chance":"0.52","vendor":"PREDICT_FUN","marketTitle":"UP","marketExtId":"ext_001","side":"BUY","amountIn":"1000000000000000000","amountOut":"1923070000000000000","isMinAmountOut":false,"feeAmount":"20000000000000000","feeDiscountBps":"0","averagePrice":0.52,"lastPrice":0.52,"priceImpact":0.001,"timestamp":1748131500000,"chainId":"56","userId":100103755893,"walletAddress":"0x12e32db8817e292508c34111cbc4b23340df542c","orderType":"MARKET","slippageBps":1200,"feeRateBps":200,"minReceive":"1900000000000000000","expireAt":1748131800000,"priceLimit":"priceLimit"}"#).unwrap_or_else(|_| serde_json::json!({}));
            let dummy_response: models::GetQuoteResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::GetQuoteResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn place_order(
            &self,
            _params: PlaceOrderParams,
        ) -> anyhow::Result<RestApiResponse<models::PlaceOrderResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"orderId":"54124"}"#)
                .unwrap_or_else(|_| serde_json::json!({}));
            let dummy_response: models::PlaceOrderResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::PlaceOrderResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn query_active_orders(
            &self,
            _params: QueryActiveOrdersParams,
        ) -> anyhow::Result<RestApiResponse<models::QueryActiveOrdersResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"total":2,"offset":0,"limit":20,"orders":[{"orderId":"54124","vendorOrderId":"0x1234abcd...","vendor":"PREDICT_FUN","marketTopicId":4229564,"slug":"btc-price-1h-up-or-down","marketTopicTitle":"BTC Price 1h Up or Down?","marketId":5567895,"marketTitle":"UP","outcome":"YES","outcomeIndex":0,"status":"OPENING","side":"BUY","orderType":"LIMIT","createTime":1748131500000,"modifyTime":1748131500000,"makerUsdtAmount":"1.00","makerShareQty":"2000.00","filledUsdtAmount":"0.00","filledShareQty":"0.00","fillPercentage":"0.00","price":"0.50","marketProviderFee":"0.02","networkFee":"0.000001"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
            let dummy_response: models::QueryActiveOrdersResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::QueryActiveOrdersResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }

        async fn query_order_history(
            &self,
            _params: QueryOrderHistoryParams,
        ) -> anyhow::Result<RestApiResponse<models::QueryOrderHistoryResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"total":15,"offset":0,"limit":20,"orders":[{"orderId":"54100","vendorOrderId":"0xabcd5678...","vendor":"PREDICT_FUN","marketTopicId":4229500,"slug":"btc-price-1h-up-or-down-prev","marketTopicTitle":"BTC Price 1h Up or Down?","marketId":5567800,"marketTitle":"UP","outcome":"YES","outcomeIndex":0,"status":"CLOSED","side":"BUY","orderType":"MARKET","createTime":1748045100000,"modifyTime":1748045101000,"terminalTime":1748045101000,"makerUsdtAmount":"1.00","makerShareQty":"1923.07","filledUsdtAmount":"1.00","filledShareQty":"1923.07","fillPercentage":"1.00","price":"0.52","marketProviderFee":"0.02","networkFee":"0.000001"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
            let dummy_response: models::QueryOrderHistoryResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::QueryOrderHistoryResponse");

            let dummy = DummyRestApiResponse {
                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
                status: 200,
                headers: HashMap::new(),
                rate_limits: None,
            };

            Ok(dummy.into())
        }
    }

    #[test]
    fn batch_cancel_orders_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: false };

            let params = BatchCancelOrdersParams::builder("0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),"5b5c1ec3be4e4416a5872b21c1ca5d20".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"canceled":["54124"],"failed":[{"orderId":"54126","reason":"ORDER_NOT_FOUND"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
            let expected_response : models::BatchCancelOrdersResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::BatchCancelOrdersResponse");

            let resp = client.batch_cancel_orders(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn batch_cancel_orders_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: false };

            let params = BatchCancelOrdersParams::builder("0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),"5b5c1ec3be4e4416a5872b21c1ca5d20".to_string(),).cancel_info_list(vec![]).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"canceled":["54124"],"failed":[{"orderId":"54126","reason":"ORDER_NOT_FOUND"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
            let expected_response : models::BatchCancelOrdersResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::BatchCancelOrdersResponse");

            let resp = client.batch_cancel_orders(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn batch_cancel_orders_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: true };

            let params = BatchCancelOrdersParams::builder(
                "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
                "5b5c1ec3be4e4416a5872b21c1ca5d20".to_string(),
            )
            .build()
            .unwrap();

            match client.batch_cancel_orders(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn get_quote_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: false };

            let params = GetQuoteParams::builder("0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),"112233".to_string(),GetQuoteSideEnum::Buy,"1000000000000000000".to_string(),GetQuoteOrderTypeEnum::Market,1200,).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"quoteId":"q_20260525_abc123xyz","tokenId":"112233","chance":"0.52","vendor":"PREDICT_FUN","marketTitle":"UP","marketExtId":"ext_001","side":"BUY","amountIn":"1000000000000000000","amountOut":"1923070000000000000","isMinAmountOut":false,"feeAmount":"20000000000000000","feeDiscountBps":"0","averagePrice":0.52,"lastPrice":0.52,"priceImpact":0.001,"timestamp":1748131500000,"chainId":"56","userId":100103755893,"walletAddress":"0x12e32db8817e292508c34111cbc4b23340df542c","orderType":"MARKET","slippageBps":1200,"feeRateBps":200,"minReceive":"1900000000000000000","expireAt":1748131800000,"priceLimit":"priceLimit"}"#).unwrap_or_else(|_| serde_json::json!({}));
            let expected_response : models::GetQuoteResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetQuoteResponse");

            let resp = client.get_quote(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn get_quote_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: false };

            let params = GetQuoteParams::builder("0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),"112233".to_string(),GetQuoteSideEnum::Buy,"1000000000000000000".to_string(),GetQuoteOrderTypeEnum::Market,1200,).price_limit("0.5".to_string()).chain_id("56".to_string()).fee_rate_bps(200).funding_source(GetQuoteFundingSourceEnum::Mpc).fund_transfer_amount("1000000000000000000".to_string()).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"quoteId":"q_20260525_abc123xyz","tokenId":"112233","chance":"0.52","vendor":"PREDICT_FUN","marketTitle":"UP","marketExtId":"ext_001","side":"BUY","amountIn":"1000000000000000000","amountOut":"1923070000000000000","isMinAmountOut":false,"feeAmount":"20000000000000000","feeDiscountBps":"0","averagePrice":0.52,"lastPrice":0.52,"priceImpact":0.001,"timestamp":1748131500000,"chainId":"56","userId":100103755893,"walletAddress":"0x12e32db8817e292508c34111cbc4b23340df542c","orderType":"MARKET","slippageBps":1200,"feeRateBps":200,"minReceive":"1900000000000000000","expireAt":1748131800000,"priceLimit":"priceLimit"}"#).unwrap_or_else(|_| serde_json::json!({}));
            let expected_response : models::GetQuoteResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetQuoteResponse");

            let resp = client.get_quote(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn get_quote_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: true };

            let params = GetQuoteParams::builder(
                "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
                "112233".to_string(),
                GetQuoteSideEnum::Buy,
                "1000000000000000000".to_string(),
                GetQuoteOrderTypeEnum::Market,
                1200,
            )
            .build()
            .unwrap();

            match client.get_quote(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn place_order_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: false };

            let params = PlaceOrderParams::builder(
                "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
                "5b5c1ec3be4e4416a5872b21c1ca5d20".to_string(),
                "q_20260525_abc123xyz".to_string(),
                "FOK".to_string(),
                PlaceOrderAccountTypeEnum::Spot,
                PlaceOrderOrderTypeEnum::Market,
                1200,
            )
            .build()
            .unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"orderId":"54124"}"#)
                .unwrap_or_else(|_| serde_json::json!({}));
            let expected_response: models::PlaceOrderResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::PlaceOrderResponse");

            let resp = client
                .place_order(params)
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn place_order_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: false };

            let params = PlaceOrderParams::builder(
                "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
                "5b5c1ec3be4e4416a5872b21c1ca5d20".to_string(),
                "q_20260525_abc123xyz".to_string(),
                "FOK".to_string(),
                PlaceOrderAccountTypeEnum::Spot,
                PlaceOrderOrderTypeEnum::Market,
                1200,
            )
            .price_limit("0.5".to_string())
            .funding_source(PlaceOrderFundingSourceEnum::Mpc)
            .fund_transfer_amount("1000000000000000000".to_string())
            .build()
            .unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"orderId":"54124"}"#)
                .unwrap_or_else(|_| serde_json::json!({}));
            let expected_response: models::PlaceOrderResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::PlaceOrderResponse");

            let resp = client
                .place_order(params)
                .await
                .expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn place_order_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: true };

            let params = PlaceOrderParams::builder(
                "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
                "5b5c1ec3be4e4416a5872b21c1ca5d20".to_string(),
                "q_20260525_abc123xyz".to_string(),
                "FOK".to_string(),
                PlaceOrderAccountTypeEnum::Spot,
                PlaceOrderOrderTypeEnum::Market,
                1200,
            )
            .build()
            .unwrap();

            match client.place_order(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn query_active_orders_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: false };

            let params = QueryActiveOrdersParams::builder("0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"total":2,"offset":0,"limit":20,"orders":[{"orderId":"54124","vendorOrderId":"0x1234abcd...","vendor":"PREDICT_FUN","marketTopicId":4229564,"slug":"btc-price-1h-up-or-down","marketTopicTitle":"BTC Price 1h Up or Down?","marketId":5567895,"marketTitle":"UP","outcome":"YES","outcomeIndex":0,"status":"OPENING","side":"BUY","orderType":"LIMIT","createTime":1748131500000,"modifyTime":1748131500000,"makerUsdtAmount":"1.00","makerShareQty":"2000.00","filledUsdtAmount":"0.00","filledShareQty":"0.00","fillPercentage":"0.00","price":"0.50","marketProviderFee":"0.02","networkFee":"0.000001"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
            let expected_response : models::QueryActiveOrdersResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryActiveOrdersResponse");

            let resp = client.query_active_orders(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn query_active_orders_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: false };

            let params = QueryActiveOrdersParams::builder("0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),).trade_side(QueryActiveOrdersTradeSideEnum::Buy).l1_category("crypto".to_string()).market_id(5567895).offset(0).limit(20).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"total":2,"offset":0,"limit":20,"orders":[{"orderId":"54124","vendorOrderId":"0x1234abcd...","vendor":"PREDICT_FUN","marketTopicId":4229564,"slug":"btc-price-1h-up-or-down","marketTopicTitle":"BTC Price 1h Up or Down?","marketId":5567895,"marketTitle":"UP","outcome":"YES","outcomeIndex":0,"status":"OPENING","side":"BUY","orderType":"LIMIT","createTime":1748131500000,"modifyTime":1748131500000,"makerUsdtAmount":"1.00","makerShareQty":"2000.00","filledUsdtAmount":"0.00","filledShareQty":"0.00","fillPercentage":"0.00","price":"0.50","marketProviderFee":"0.02","networkFee":"0.000001"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
            let expected_response : models::QueryActiveOrdersResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryActiveOrdersResponse");

            let resp = client.query_active_orders(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn query_active_orders_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: true };

            let params = QueryActiveOrdersParams::builder(
                "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
            )
            .build()
            .unwrap();

            match client.query_active_orders(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }

    #[test]
    fn query_order_history_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: false };

            let params = QueryOrderHistoryParams::builder("0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"total":15,"offset":0,"limit":20,"orders":[{"orderId":"54100","vendorOrderId":"0xabcd5678...","vendor":"PREDICT_FUN","marketTopicId":4229500,"slug":"btc-price-1h-up-or-down-prev","marketTopicTitle":"BTC Price 1h Up or Down?","marketId":5567800,"marketTitle":"UP","outcome":"YES","outcomeIndex":0,"status":"CLOSED","side":"BUY","orderType":"MARKET","createTime":1748045100000,"modifyTime":1748045101000,"terminalTime":1748045101000,"makerUsdtAmount":"1.00","makerShareQty":"1923.07","filledUsdtAmount":"1.00","filledShareQty":"1923.07","fillPercentage":"1.00","price":"0.52","marketProviderFee":"0.02","networkFee":"0.000001"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
            let expected_response : models::QueryOrderHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryOrderHistoryResponse");

            let resp = client.query_order_history(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn query_order_history_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: false };

            let params = QueryOrderHistoryParams::builder("0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),).l1_category("crypto".to_string()).order_type(QueryOrderHistoryOrderTypeEnum::Market).status("CLOSED".to_string()).start_date("2026-05-01".to_string()).end_date("2026-05-25".to_string()).offset(0).limit(20).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"total":15,"offset":0,"limit":20,"orders":[{"orderId":"54100","vendorOrderId":"0xabcd5678...","vendor":"PREDICT_FUN","marketTopicId":4229500,"slug":"btc-price-1h-up-or-down-prev","marketTopicTitle":"BTC Price 1h Up or Down?","marketId":5567800,"marketTitle":"UP","outcome":"YES","outcomeIndex":0,"status":"CLOSED","side":"BUY","orderType":"MARKET","createTime":1748045100000,"modifyTime":1748045101000,"terminalTime":1748045101000,"makerUsdtAmount":"1.00","makerShareQty":"1923.07","filledUsdtAmount":"1.00","filledShareQty":"1923.07","fillPercentage":"1.00","price":"0.52","marketProviderFee":"0.02","networkFee":"0.000001"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
            let expected_response : models::QueryOrderHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryOrderHistoryResponse");

            let resp = client.query_order_history(params).await.expect("Expected a response");
            let data_future = resp.data();
            let actual_response = data_future.await.unwrap();
            assert_eq!(actual_response, expected_response);
        });
    }

    #[test]
    fn query_order_history_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockTradeApiClient { force_error: true };

            let params = QueryOrderHistoryParams::builder(
                "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
            )
            .build()
            .unwrap();

            match client.query_order_history(params).await {
                Ok(_) => panic!("Expected an error"),
                Err(err) => {
                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
                }
            }
        });
    }
}
