/*
 * Binance Derivatives Trading Options REST API
 *
 * OpenAPI Specification for the Binance Derivatives Trading Options 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::derivatives_trading_options::rest_api::models;

const HAS_TIME_UNIT: bool = false;

#[async_trait]
pub trait MarketMakerBlockTradeApi: Send + Sync {
    async fn accept_block_trade_order(
        &self,
        params: AcceptBlockTradeOrderParams,
    ) -> anyhow::Result<RestApiResponse<models::AcceptBlockTradeOrderResponse>>;
    async fn account_block_trade_list(
        &self,
        params: AccountBlockTradeListParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::AccountBlockTradeListResponseInner>>>;
    async fn cancel_block_trade_order(
        &self,
        params: CancelBlockTradeOrderParams,
    ) -> anyhow::Result<RestApiResponse<Value>>;
    async fn extend_block_trade_order(
        &self,
        params: ExtendBlockTradeOrderParams,
    ) -> anyhow::Result<RestApiResponse<models::ExtendBlockTradeOrderResponse>>;
    async fn new_block_trade_order(
        &self,
        params: NewBlockTradeOrderParams,
    ) -> anyhow::Result<RestApiResponse<models::NewBlockTradeOrderResponse>>;
    async fn query_block_trade_details(
        &self,
        params: QueryBlockTradeDetailsParams,
    ) -> anyhow::Result<RestApiResponse<models::QueryBlockTradeDetailsResponse>>;
    async fn query_block_trade_order(
        &self,
        params: QueryBlockTradeOrderParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::QueryBlockTradeOrderResponseInner>>>;
}

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

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

/// Request parameters for the [`accept_block_trade_order`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`accept_block_trade_order`](#method.accept_block_trade_order).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct AcceptBlockTradeOrderParams {
    ///
    /// The `block_order_matching_key` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub block_order_matching_key: String,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl AcceptBlockTradeOrderParams {
    /// Create a builder for [`accept_block_trade_order`].
    ///
    /// Required parameters:
    ///
    /// * `block_order_matching_key` — String
    ///
    #[must_use]
    pub fn builder(block_order_matching_key: String) -> AcceptBlockTradeOrderParamsBuilder {
        AcceptBlockTradeOrderParamsBuilder::default()
            .block_order_matching_key(block_order_matching_key)
    }
}
/// Request parameters for the [`account_block_trade_list`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`account_block_trade_list`](#method.account_block_trade_list).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct AccountBlockTradeListParams {
    /// End Time, e.g 1593512200000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_time: Option<i64>,
    /// Start Time, e.g 1593511200000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_time: Option<i64>,
    /// underlying, e.g BTCUSDT
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub underlying: Option<String>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl AccountBlockTradeListParams {
    /// Create a builder for [`account_block_trade_list`].
    ///
    #[must_use]
    pub fn builder() -> AccountBlockTradeListParamsBuilder {
        AccountBlockTradeListParamsBuilder::default()
    }
}
/// Request parameters for the [`cancel_block_trade_order`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`cancel_block_trade_order`](#method.cancel_block_trade_order).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct CancelBlockTradeOrderParams {
    ///
    /// The `block_order_matching_key` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub block_order_matching_key: String,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl CancelBlockTradeOrderParams {
    /// Create a builder for [`cancel_block_trade_order`].
    ///
    /// Required parameters:
    ///
    /// * `block_order_matching_key` — String
    ///
    #[must_use]
    pub fn builder(block_order_matching_key: String) -> CancelBlockTradeOrderParamsBuilder {
        CancelBlockTradeOrderParamsBuilder::default()
            .block_order_matching_key(block_order_matching_key)
    }
}
/// Request parameters for the [`extend_block_trade_order`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`extend_block_trade_order`](#method.extend_block_trade_order).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct ExtendBlockTradeOrderParams {
    ///
    /// The `block_order_matching_key` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub block_order_matching_key: String,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl ExtendBlockTradeOrderParams {
    /// Create a builder for [`extend_block_trade_order`].
    ///
    /// Required parameters:
    ///
    /// * `block_order_matching_key` — String
    ///
    #[must_use]
    pub fn builder(block_order_matching_key: String) -> ExtendBlockTradeOrderParamsBuilder {
        ExtendBlockTradeOrderParamsBuilder::default()
            .block_order_matching_key(block_order_matching_key)
    }
}
/// Request parameters for the [`new_block_trade_order`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`new_block_trade_order`](#method.new_block_trade_order).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct NewBlockTradeOrderParams {
    /// Taker or Maker
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub liquidity: String,
    /// Max 1 (only single leg supported), list of legs parameters in JSON; example: eapi/v1/block/order/create?orders=[{"symbol":"BTC-210115-35000-C", "price":"100","quantity":"0.0002","side":"BUY","type":"LIMIT"}]
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub legs: Vec<serde_json::Value>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl NewBlockTradeOrderParams {
    /// Create a builder for [`new_block_trade_order`].
    ///
    /// Required parameters:
    ///
    /// * `liquidity` — Taker or Maker
    /// * `legs` — Max 1 (only single leg supported), list of legs parameters in JSON; example: eapi/v1/block/order/create?orders=[{\"symbol\":\"BTC-210115-35000-C\", \"price\":\"100\",\"quantity\":\"0.0002\",\"side\":\"BUY\",\"type\":\"LIMIT\"}]
    ///
    #[must_use]
    pub fn builder(
        liquidity: String,
        legs: Vec<serde_json::Value>,
    ) -> NewBlockTradeOrderParamsBuilder {
        NewBlockTradeOrderParamsBuilder::default()
            .liquidity(liquidity)
            .legs(legs)
    }
}
/// Request parameters for the [`query_block_trade_details`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`query_block_trade_details`](#method.query_block_trade_details).
#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct QueryBlockTradeDetailsParams {
    ///
    /// The `block_order_matching_key` parameter.
    ///
    /// This field is **required.
    #[builder(setter(into))]
    pub block_order_matching_key: String,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl QueryBlockTradeDetailsParams {
    /// Create a builder for [`query_block_trade_details`].
    ///
    /// Required parameters:
    ///
    /// * `block_order_matching_key` — String
    ///
    #[must_use]
    pub fn builder(block_order_matching_key: String) -> QueryBlockTradeDetailsParamsBuilder {
        QueryBlockTradeDetailsParamsBuilder::default()
            .block_order_matching_key(block_order_matching_key)
    }
}
/// Request parameters for the [`query_block_trade_order`] operation.
///
/// This struct holds all of the inputs you can pass when calling
/// [`query_block_trade_order`](#method.query_block_trade_order).
#[derive(Clone, Debug, Builder, Default)]
#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
pub struct QueryBlockTradeOrderParams {
    /// If specified, returns the specific block trade associated with the blockOrderMatchingKey
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub block_order_matching_key: Option<String>,
    /// End Time, e.g 1593512200000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub end_time: Option<i64>,
    /// Start Time, e.g 1593511200000
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub start_time: Option<i64>,
    /// underlying, e.g BTCUSDT
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub underlying: Option<String>,
    ///
    /// The `recv_window` parameter.
    ///
    /// This field is **optional.
    #[builder(setter(into), default)]
    pub recv_window: Option<i64>,
}

impl QueryBlockTradeOrderParams {
    /// Create a builder for [`query_block_trade_order`].
    ///
    #[must_use]
    pub fn builder() -> QueryBlockTradeOrderParamsBuilder {
        QueryBlockTradeOrderParamsBuilder::default()
    }
}

#[async_trait]
impl MarketMakerBlockTradeApi for MarketMakerBlockTradeApiClient {
    async fn accept_block_trade_order(
        &self,
        params: AcceptBlockTradeOrderParams,
    ) -> anyhow::Result<RestApiResponse<models::AcceptBlockTradeOrderResponse>> {
        let AcceptBlockTradeOrderParams {
            block_order_matching_key,
            recv_window,
        } = params;

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

        query_params.insert(
            "blockOrderMatchingKey".to_string(),
            json!(block_order_matching_key),
        );

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

        send_request::<models::AcceptBlockTradeOrderResponse>(
            &self.configuration,
            "/eapi/v1/block/order/execute",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn account_block_trade_list(
        &self,
        params: AccountBlockTradeListParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::AccountBlockTradeListResponseInner>>> {
        let AccountBlockTradeListParams {
            end_time,
            start_time,
            underlying,
            recv_window,
        } = params;

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

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

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

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

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

        send_request::<Vec<models::AccountBlockTradeListResponseInner>>(
            &self.configuration,
            "/eapi/v1/block/user-trades",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn cancel_block_trade_order(
        &self,
        params: CancelBlockTradeOrderParams,
    ) -> anyhow::Result<RestApiResponse<Value>> {
        let CancelBlockTradeOrderParams {
            block_order_matching_key,
            recv_window,
        } = params;

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

        query_params.insert(
            "blockOrderMatchingKey".to_string(),
            json!(block_order_matching_key),
        );

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

        send_request::<Value>(
            &self.configuration,
            "/eapi/v1/block/order/create",
            reqwest::Method::DELETE,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn extend_block_trade_order(
        &self,
        params: ExtendBlockTradeOrderParams,
    ) -> anyhow::Result<RestApiResponse<models::ExtendBlockTradeOrderResponse>> {
        let ExtendBlockTradeOrderParams {
            block_order_matching_key,
            recv_window,
        } = params;

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

        query_params.insert(
            "blockOrderMatchingKey".to_string(),
            json!(block_order_matching_key),
        );

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

        send_request::<models::ExtendBlockTradeOrderResponse>(
            &self.configuration,
            "/eapi/v1/block/order/create",
            reqwest::Method::PUT,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn new_block_trade_order(
        &self,
        params: NewBlockTradeOrderParams,
    ) -> anyhow::Result<RestApiResponse<models::NewBlockTradeOrderResponse>> {
        let NewBlockTradeOrderParams {
            liquidity,
            legs,
            recv_window,
        } = params;

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

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

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

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

        send_request::<models::NewBlockTradeOrderResponse>(
            &self.configuration,
            "/eapi/v1/block/order/create",
            reqwest::Method::POST,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn query_block_trade_details(
        &self,
        params: QueryBlockTradeDetailsParams,
    ) -> anyhow::Result<RestApiResponse<models::QueryBlockTradeDetailsResponse>> {
        let QueryBlockTradeDetailsParams {
            block_order_matching_key,
            recv_window,
        } = params;

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

        query_params.insert(
            "blockOrderMatchingKey".to_string(),
            json!(block_order_matching_key),
        );

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

        send_request::<models::QueryBlockTradeDetailsResponse>(
            &self.configuration,
            "/eapi/v1/block/order/execute",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }

    async fn query_block_trade_order(
        &self,
        params: QueryBlockTradeOrderParams,
    ) -> anyhow::Result<RestApiResponse<Vec<models::QueryBlockTradeOrderResponseInner>>> {
        let QueryBlockTradeOrderParams {
            block_order_matching_key,
            end_time,
            start_time,
            underlying,
            recv_window,
        } = params;

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

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

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

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

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

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

        send_request::<Vec<models::QueryBlockTradeOrderResponseInner>>(
            &self.configuration,
            "/eapi/v1/block/order/orders",
            reqwest::Method::GET,
            query_params,
            body_params,
            if HAS_TIME_UNIT {
                self.configuration.time_unit
            } else {
                None
            },
            true,
        )
        .await
    }
}

#[cfg(all(test, feature = "derivatives_trading_options"))]
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 MockMarketMakerBlockTradeApiClient {
        force_error: bool,
    }

    #[async_trait]
    impl MarketMakerBlockTradeApi for MockMarketMakerBlockTradeApiClient {
        async fn accept_block_trade_order(
            &self,
            _params: AcceptBlockTradeOrderParams,
        ) -> anyhow::Result<RestApiResponse<models::AcceptBlockTradeOrderResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"blockTradeSettlementKey":"7d046e6e-a429-4335-ab9d-6a681febcde5","expireTime":1730172115801,"liquidity":"MAKER","status":"ACCEPTED","createTime":1730170315803,"legs":[{"symbol":"BNB-241101-700-C","side":"SELL","quantity":"1.2","price":"2.8"}]}"#).unwrap();
            let dummy_response: models::AcceptBlockTradeOrderResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::AcceptBlockTradeOrderResponse");

            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 account_block_trade_list(
            &self,
            _params: AccountBlockTradeListParams,
        ) -> anyhow::Result<RestApiResponse<Vec<models::AccountBlockTradeListResponseInner>>>
        {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[{"parentOrderId":"4675011431944499201","crossType":"USER_BLOCK","legs":[{"createTime":1730170445600,"updateTime":1730170445600,"symbol":"BNB-241101-700-C","orderId":"4675011431944499203","orderPrice":2.8,"orderQuantity":1.2,"orderStatus":"FILLED","executedQty":1.2,"executedAmount":3.36,"fee":0.336,"orderType":"PREV_QUOTED","orderSide":"BUY","id":"1125899906900937837","tradeId":1,"tradePrice":2.8,"tradeQty":1.2,"tradeTime":1730170445600,"liquidity":"TAKER","commission":0.336}],"blockTradeSettlementKey":"7d085e6e-a229-2335-ab9d-6a581febcd25"}]"#).unwrap();
            let dummy_response: Vec<models::AccountBlockTradeListResponseInner> =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into Vec<models::AccountBlockTradeListResponseInner>");

            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 cancel_block_trade_order(
            &self,
            _params: CancelBlockTradeOrderParams,
        ) -> anyhow::Result<RestApiResponse<Value>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let dummy_response = Value::Null;

            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 extend_block_trade_order(
            &self,
            _params: ExtendBlockTradeOrderParams,
        ) -> anyhow::Result<RestApiResponse<models::ExtendBlockTradeOrderResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"blockTradeSettlementKey":"3668822b8-1baa-6a2f-adb8-d3de6289b361","expireTime":1730172007000,"liquidity":"TAKER","status":"RECEIVED","createTime":1730170088111,"legs":[{"symbol":"BNB-241101-700-C","side":"BUY","quantity":"1.2","price":"2.8"}]}"#).unwrap();
            let dummy_response: models::ExtendBlockTradeOrderResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::ExtendBlockTradeOrderResponse");

            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 new_block_trade_order(
            &self,
            _params: NewBlockTradeOrderParams,
        ) -> anyhow::Result<RestApiResponse<models::NewBlockTradeOrderResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"blockTradeSettlementKey":"3668822b8-1baa-6a2f-adb8-d3de6289b361","expireTime":1730171888109,"liquidity":"TAKER","status":"RECEIVED","legs":[{"symbol":"BNB-241101-700-C","side":"BUY","quantity":"1.2","price":"2.8"}]}"#).unwrap();
            let dummy_response: models::NewBlockTradeOrderResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::NewBlockTradeOrderResponse");

            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_block_trade_details(
            &self,
            _params: QueryBlockTradeDetailsParams,
        ) -> anyhow::Result<RestApiResponse<models::QueryBlockTradeDetailsResponse>> {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"{"blockTradeSettlementKey":"12b96c28-ba05-8906-c89t-703215cfb2e6","expireTime":1730171860460,"liquidity":"MAKER","status":"RECEIVED","createTime":1730170060462,"legs":[{"symbol":"BNB-241101-700-C","side":"SELL","quantity":"1.66","price":"20"}]}"#).unwrap();
            let dummy_response: models::QueryBlockTradeDetailsResponse =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into models::QueryBlockTradeDetailsResponse");

            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_block_trade_order(
            &self,
            _params: QueryBlockTradeOrderParams,
        ) -> anyhow::Result<RestApiResponse<Vec<models::QueryBlockTradeOrderResponseInner>>>
        {
            if self.force_error {
                return Err(ConnectorError::ConnectorClientError {
                    msg: "ResponseError".to_string(),
                    code: None,
                }
                .into());
            }

            let resp_json: Value = serde_json::from_str(r#"[{"blockTradeSettlementKey":"7d046e6e-a429-4335-ab9d-6a681febcde5","expireTime":1730172115801,"liquidity":"TAKER","status":"RECEIVED","createTime":1730170315803,"legs":[{"symbol":"BNB-241101-700-C","side":"BUY","quantity":"1.2","price":"2.8"}]},{"blockTradeSettlementKey":"28b96c28-ba05-6906-a47c-703215cfbfe6","expireTime":1730171860460,"liquidity":"TAKER","status":"RECEIVED","createTime":1730170060462,"legs":[{"symbol":"BNB-241101-700-C","side":"BUY","quantity":"1.66","price":"20"}]}]"#).unwrap();
            let dummy_response: Vec<models::QueryBlockTradeOrderResponseInner> =
                serde_json::from_value(resp_json.clone())
                    .expect("should parse into Vec<models::QueryBlockTradeOrderResponseInner>");

            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 accept_block_trade_order_required_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: false };

            let params = AcceptBlockTradeOrderParams::builder("block_order_matching_key_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"blockTradeSettlementKey":"7d046e6e-a429-4335-ab9d-6a681febcde5","expireTime":1730172115801,"liquidity":"MAKER","status":"ACCEPTED","createTime":1730170315803,"legs":[{"symbol":"BNB-241101-700-C","side":"SELL","quantity":"1.2","price":"2.8"}]}"#).unwrap();
            let expected_response : models::AcceptBlockTradeOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AcceptBlockTradeOrderResponse");

            let resp = client.accept_block_trade_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 accept_block_trade_order_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: false };

            let params = AcceptBlockTradeOrderParams::builder("block_order_matching_key_example".to_string(),).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"blockTradeSettlementKey":"7d046e6e-a429-4335-ab9d-6a681febcde5","expireTime":1730172115801,"liquidity":"MAKER","status":"ACCEPTED","createTime":1730170315803,"legs":[{"symbol":"BNB-241101-700-C","side":"SELL","quantity":"1.2","price":"2.8"}]}"#).unwrap();
            let expected_response : models::AcceptBlockTradeOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::AcceptBlockTradeOrderResponse");

            let resp = client.accept_block_trade_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 accept_block_trade_order_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: true };

            let params = AcceptBlockTradeOrderParams::builder(
                "block_order_matching_key_example".to_string(),
            )
            .build()
            .unwrap();

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

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

            let params = AccountBlockTradeListParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"parentOrderId":"4675011431944499201","crossType":"USER_BLOCK","legs":[{"createTime":1730170445600,"updateTime":1730170445600,"symbol":"BNB-241101-700-C","orderId":"4675011431944499203","orderPrice":2.8,"orderQuantity":1.2,"orderStatus":"FILLED","executedQty":1.2,"executedAmount":3.36,"fee":0.336,"orderType":"PREV_QUOTED","orderSide":"BUY","id":"1125899906900937837","tradeId":1,"tradePrice":2.8,"tradeQty":1.2,"tradeTime":1730170445600,"liquidity":"TAKER","commission":0.336}],"blockTradeSettlementKey":"7d085e6e-a229-2335-ab9d-6a581febcd25"}]"#).unwrap();
            let expected_response : Vec<models::AccountBlockTradeListResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::AccountBlockTradeListResponseInner>");

            let resp = client.account_block_trade_list(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 account_block_trade_list_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: false };

            let params = AccountBlockTradeListParams::builder().end_time(1641782889000).start_time(1623319461670).underlying("underlying_example".to_string()).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"parentOrderId":"4675011431944499201","crossType":"USER_BLOCK","legs":[{"createTime":1730170445600,"updateTime":1730170445600,"symbol":"BNB-241101-700-C","orderId":"4675011431944499203","orderPrice":2.8,"orderQuantity":1.2,"orderStatus":"FILLED","executedQty":1.2,"executedAmount":3.36,"fee":0.336,"orderType":"PREV_QUOTED","orderSide":"BUY","id":"1125899906900937837","tradeId":1,"tradePrice":2.8,"tradeQty":1.2,"tradeTime":1730170445600,"liquidity":"TAKER","commission":0.336}],"blockTradeSettlementKey":"7d085e6e-a229-2335-ab9d-6a581febcd25"}]"#).unwrap();
            let expected_response : Vec<models::AccountBlockTradeListResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::AccountBlockTradeListResponseInner>");

            let resp = client.account_block_trade_list(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 account_block_trade_list_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: true };

            let params = AccountBlockTradeListParams::builder().build().unwrap();

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

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

            let params = CancelBlockTradeOrderParams::builder(
                "block_order_matching_key_example".to_string(),
            )
            .build()
            .unwrap();

            let expected_response = Value::Null;

            let resp = client
                .cancel_block_trade_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 cancel_block_trade_order_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: false };

            let params = CancelBlockTradeOrderParams::builder(
                "block_order_matching_key_example".to_string(),
            )
            .recv_window(5000)
            .build()
            .unwrap();

            let expected_response = Value::Null;

            let resp = client
                .cancel_block_trade_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 cancel_block_trade_order_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: true };

            let params = CancelBlockTradeOrderParams::builder(
                "block_order_matching_key_example".to_string(),
            )
            .build()
            .unwrap();

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

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

            let params = ExtendBlockTradeOrderParams::builder("block_order_matching_key_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"blockTradeSettlementKey":"3668822b8-1baa-6a2f-adb8-d3de6289b361","expireTime":1730172007000,"liquidity":"TAKER","status":"RECEIVED","createTime":1730170088111,"legs":[{"symbol":"BNB-241101-700-C","side":"BUY","quantity":"1.2","price":"2.8"}]}"#).unwrap();
            let expected_response : models::ExtendBlockTradeOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::ExtendBlockTradeOrderResponse");

            let resp = client.extend_block_trade_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 extend_block_trade_order_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: false };

            let params = ExtendBlockTradeOrderParams::builder("block_order_matching_key_example".to_string(),).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"blockTradeSettlementKey":"3668822b8-1baa-6a2f-adb8-d3de6289b361","expireTime":1730172007000,"liquidity":"TAKER","status":"RECEIVED","createTime":1730170088111,"legs":[{"symbol":"BNB-241101-700-C","side":"BUY","quantity":"1.2","price":"2.8"}]}"#).unwrap();
            let expected_response : models::ExtendBlockTradeOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::ExtendBlockTradeOrderResponse");

            let resp = client.extend_block_trade_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 extend_block_trade_order_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: true };

            let params = ExtendBlockTradeOrderParams::builder(
                "block_order_matching_key_example".to_string(),
            )
            .build()
            .unwrap();

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

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

            let params = NewBlockTradeOrderParams::builder("liquidity_example".to_string(),[].to_vec(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"blockTradeSettlementKey":"3668822b8-1baa-6a2f-adb8-d3de6289b361","expireTime":1730171888109,"liquidity":"TAKER","status":"RECEIVED","legs":[{"symbol":"BNB-241101-700-C","side":"BUY","quantity":"1.2","price":"2.8"}]}"#).unwrap();
            let expected_response : models::NewBlockTradeOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::NewBlockTradeOrderResponse");

            let resp = client.new_block_trade_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 new_block_trade_order_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: false };

            let params = NewBlockTradeOrderParams::builder("liquidity_example".to_string(),[].to_vec(),).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"blockTradeSettlementKey":"3668822b8-1baa-6a2f-adb8-d3de6289b361","expireTime":1730171888109,"liquidity":"TAKER","status":"RECEIVED","legs":[{"symbol":"BNB-241101-700-C","side":"BUY","quantity":"1.2","price":"2.8"}]}"#).unwrap();
            let expected_response : models::NewBlockTradeOrderResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::NewBlockTradeOrderResponse");

            let resp = client.new_block_trade_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 new_block_trade_order_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: true };

            let params =
                NewBlockTradeOrderParams::builder("liquidity_example".to_string(), [].to_vec())
                    .build()
                    .unwrap();

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

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

            let params = QueryBlockTradeDetailsParams::builder("block_order_matching_key_example".to_string(),).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"blockTradeSettlementKey":"12b96c28-ba05-8906-c89t-703215cfb2e6","expireTime":1730171860460,"liquidity":"MAKER","status":"RECEIVED","createTime":1730170060462,"legs":[{"symbol":"BNB-241101-700-C","side":"SELL","quantity":"1.66","price":"20"}]}"#).unwrap();
            let expected_response : models::QueryBlockTradeDetailsResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryBlockTradeDetailsResponse");

            let resp = client.query_block_trade_details(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_block_trade_details_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: false };

            let params = QueryBlockTradeDetailsParams::builder("block_order_matching_key_example".to_string(),).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"{"blockTradeSettlementKey":"12b96c28-ba05-8906-c89t-703215cfb2e6","expireTime":1730171860460,"liquidity":"MAKER","status":"RECEIVED","createTime":1730170060462,"legs":[{"symbol":"BNB-241101-700-C","side":"SELL","quantity":"1.66","price":"20"}]}"#).unwrap();
            let expected_response : models::QueryBlockTradeDetailsResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryBlockTradeDetailsResponse");

            let resp = client.query_block_trade_details(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_block_trade_details_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: true };

            let params = QueryBlockTradeDetailsParams::builder(
                "block_order_matching_key_example".to_string(),
            )
            .build()
            .unwrap();

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

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

            let params = QueryBlockTradeOrderParams::builder().build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"blockTradeSettlementKey":"7d046e6e-a429-4335-ab9d-6a681febcde5","expireTime":1730172115801,"liquidity":"TAKER","status":"RECEIVED","createTime":1730170315803,"legs":[{"symbol":"BNB-241101-700-C","side":"BUY","quantity":"1.2","price":"2.8"}]},{"blockTradeSettlementKey":"28b96c28-ba05-6906-a47c-703215cfbfe6","expireTime":1730171860460,"liquidity":"TAKER","status":"RECEIVED","createTime":1730170060462,"legs":[{"symbol":"BNB-241101-700-C","side":"BUY","quantity":"1.66","price":"20"}]}]"#).unwrap();
            let expected_response : Vec<models::QueryBlockTradeOrderResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryBlockTradeOrderResponseInner>");

            let resp = client.query_block_trade_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 query_block_trade_order_optional_params_success() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: false };

            let params = QueryBlockTradeOrderParams::builder().block_order_matching_key("block_order_matching_key_example".to_string()).end_time(1641782889000).start_time(1623319461670).underlying("underlying_example".to_string()).recv_window(5000).build().unwrap();

            let resp_json: Value = serde_json::from_str(r#"[{"blockTradeSettlementKey":"7d046e6e-a429-4335-ab9d-6a681febcde5","expireTime":1730172115801,"liquidity":"TAKER","status":"RECEIVED","createTime":1730170315803,"legs":[{"symbol":"BNB-241101-700-C","side":"BUY","quantity":"1.2","price":"2.8"}]},{"blockTradeSettlementKey":"28b96c28-ba05-6906-a47c-703215cfbfe6","expireTime":1730171860460,"liquidity":"TAKER","status":"RECEIVED","createTime":1730170060462,"legs":[{"symbol":"BNB-241101-700-C","side":"BUY","quantity":"1.66","price":"20"}]}]"#).unwrap();
            let expected_response : Vec<models::QueryBlockTradeOrderResponseInner> = serde_json::from_value(resp_json.clone()).expect("should parse into Vec<models::QueryBlockTradeOrderResponseInner>");

            let resp = client.query_block_trade_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 query_block_trade_order_response_error() {
        TOKIO_SHARED_RT.block_on(async {
            let client = MockMarketMakerBlockTradeApiClient { force_error: true };

            let params = QueryBlockTradeOrderParams::builder().build().unwrap();

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