/*
 * Binance Derivatives Trading Options WebSocket Market Streams
 *
 * OpenAPI Specification for the Binance Derivatives Trading Options WebSocket Market Streams
 *
 * 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 serde_json::Value;
use std::sync::{Arc, atomic::Ordering};
use tokio::spawn;

use crate::common::config::ConfigurationWebsocketStreams;
use crate::common::websocket::{
    Subscription, WebsocketBase, WebsocketStream, WebsocketStreams as WebsocketStreamsBase,
    create_stream_handler,
};
use crate::models::{StreamId, WebsocketEvent, WebsocketMode};

mod apis;
mod handle;
mod models;

pub use apis::*;
pub use handle::*;
pub use models::*;

const HAS_TIME_UNIT: bool = false;

pub struct WebsocketStreams {
    websocket_streams_base: Arc<WebsocketStreamsBase>,
    market_api_client: MarketApiClient,
    public_api_client: PublicApiClient,
}

impl WebsocketStreams {
    pub(crate) async fn connect(
        config: ConfigurationWebsocketStreams,
        streams: Vec<String>,
        mode: Option<WebsocketMode>,
    ) -> anyhow::Result<Self> {
        let mut cfg = config;
        if let Some(m) = mode {
            cfg.mode = m;
        }

        if !HAS_TIME_UNIT {
            cfg.time_unit = None;
        }

        let websocket_streams_base = WebsocketStreamsBase::new(
            cfg,
            vec![],
            vec![
                "market".to_string(),
                "public".to_string(),
                "private".to_string(),
            ],
        );
        websocket_streams_base
            .stream_id_is_strictly_number
            .store(true, Ordering::Relaxed);
        websocket_streams_base.clone().connect(streams).await?;

        Ok(Self {
            websocket_streams_base: websocket_streams_base.clone(),
            market_api_client: MarketApiClient::new(websocket_streams_base.clone()),
            public_api_client: PublicApiClient::new(websocket_streams_base.clone()),
        })
    }

    /// Subscribes to WebSocket events with a provided callback function.
    ///
    /// # Arguments
    ///
    /// * `callback` - A mutable function that takes a `WebsocketEvent` and is `Send` and `'static`.
    ///
    /// # Returns
    ///
    /// A `Subscription` that can be used to manage the event subscription.
    ///
    /// # Examples
    ///
    ///
    /// let subscription = `websocket_streams.subscribe_on_ws_events(|event`| {
    ///     // Handle WebSocket event
    /// });
    ///
    pub fn subscribe_on_ws_events<F>(&self, callback: F) -> Subscription
    where
        F: FnMut(WebsocketEvent) + Send + 'static,
    {
        let base = Arc::clone(&self.websocket_streams_base);
        base.common.events.subscribe(callback)
    }

    /// Unsubscribes from WebSocket events for a given `Subscription`.
    ///
    /// # Arguments
    ///
    /// * `subscription` - The `Subscription` to unsubscribe from WebSocket events.
    ///
    /// # Examples
    ///
    ///
    /// let subscription = `websocket_streams.subscribe_on_ws_events(|event`| {
    ///     // Handle WebSocket event
    /// });
    /// `websocket_streams.unsubscribe_from_ws_events(subscription)`;
    ///
    pub fn unsubscribe_from_ws_events(&self, subscription: Subscription) {
        subscription.unsubscribe();
    }

    /// Disconnects the WebSocket connection.
    ///
    /// # Returns
    ///
    /// A `Result` indicating whether the disconnection was successful.
    /// Returns an error if the disconnection fails.
    ///
    /// # Errors
    ///
    /// Returns an [`anyhow::Error`] if the connection fails.
    ///
    /// # Examples
    ///
    ///
    /// let `websocket_streams` = `WebSocketStreams::new`(...);
    /// `websocket_streams.disconnect().await`?;
    ///
    pub async fn disconnect(&self) -> anyhow::Result<()> {
        self.websocket_streams_base
            .disconnect()
            .await
            .map_err(anyhow::Error::msg)
    }

    /// Checks if the WebSocket connection is currently active.
    ///
    /// # Returns
    ///
    /// A `bool` indicating whether the WebSocket connection is established and connected.
    ///
    /// # Examples
    ///
    ///
    /// let `is_active` = `websocket_streams.is_connected().await`;
    /// if `is_active` {
    ///     // WebSocket connection is active
    /// }
    ///
    pub async fn is_connected(&self) -> bool {
        self.websocket_streams_base.is_connected().await
    }

    /// Sends a ping to the WebSocket server to maintain the connection.
    ///
    /// # Examples
    ///
    ///
    /// `websocket_streams.ping_server().await`;
    ///
    ///
    /// This method sends a ping request to the WebSocket server to keep the connection alive
    /// and check the server's responsiveness.
    pub async fn ping_server(&self) {
        self.websocket_streams_base.ping_server().await;
    }

    /// Subscribes to specified WebSocket streams.
    ///
    /// # Arguments
    ///
    /// * `streams` - A vector of stream names to subscribe to
    /// * `id` - An optional identifier for the subscription request
    ///
    /// # Examples
    ///
    ///
    /// `websocket_streams.subscribe(vec`!["`btcusdt@trade".to_string()`], None).await;
    ///
    ///
    /// This method initiates an asynchronous subscription to the specified WebSocket streams.
    /// The subscription is performed in a separate task using `spawn`.
    pub fn subscribe(&self, streams: Vec<String>, id: Option<u32>) {
        let base = Arc::clone(&self.websocket_streams_base);
        spawn(async move { base.subscribe(streams, id.map(StreamId::from), None).await });
    }

    /// Unsubscribes from specified WebSocket streams.
    ///
    /// # Arguments
    ///
    /// * `streams` - A vector of stream names to unsubscribe from
    /// * `id` - An optional identifier for the unsubscription request
    ///
    /// # Examples
    ///
    ///
    /// `websocket_streams.unsubscribe(vec`!["`btcusdt@trade".to_string()`], None).await;
    ///
    ///
    /// This method initiates an asynchronous unsubscription from the specified WebSocket streams.
    /// The unsubscription is performed in a separate task using `spawn`.
    pub fn unsubscribe(&self, streams: Vec<String>, id: Option<u32>) {
        let base = Arc::clone(&self.websocket_streams_base);
        spawn(async move {
            base.unsubscribe(streams, id.map(StreamId::from), None)
                .await;
        });
    }

    /// Checks if the current WebSocket stream is subscribed to a specific stream.
    ///
    /// # Arguments
    ///
    /// * `stream` - The name of the stream to check for subscription
    ///
    /// # Returns
    ///
    /// A boolean indicating whether the stream is currently subscribed
    ///
    /// # Examples
    ///
    ///
    /// let `is_subscribed` = `websocket_streams.is_subscribed("btcusdt@trade").await`;
    ///
    ///
    /// This method checks the subscription status of a specific WebSocket stream.
    pub async fn is_subscribed(&self, stream: &str) -> bool {
        self.websocket_streams_base.is_subscribed(stream).await
    }

    /// User Data Stream
    ///
    /// Establishes a WebSocket stream for user-specific data events.
    ///
    /// # Arguments
    ///
    /// - `listen_key`: A unique key for identifying the user's data stream
    /// - `id`: An optional identifier for the stream request
    ///
    /// # Returns
    ///
    /// [`Arc<WebsocketStream<UserDataStreamEventsResponse>>`] on success.
    ///
    /// # Errors
    ///
    /// Returns an [`anyhow::Error`] if the stream creation fails or if parsing the response encounters issues.
    ///
    /// # Examples
    ///
    ///
    /// let `user_stream` = `websocket_streams.user_data(listen_key`, None).await?;
    ///
    pub async fn user_data(
        &self,
        listen_key: String,
        id: Option<String>,
    ) -> anyhow::Result<Arc<WebsocketStream<UserDataStreamEventsResponse>>> {
        Ok(create_stream_handler::<UserDataStreamEventsResponse>(
            WebsocketBase::WebsocketStreams(self.websocket_streams_base.clone()),
            listen_key,
            id.map(StreamId::from),
            Some("private".to_string()),
        )
        .await)
    }

    /// Index Price Streams
    ///
    /// Underlying(e.g ETHUSDT) index stream.
    ///
    /// Update Speed: 1000ms
    ///
    /// # Arguments
    ///
    /// - `params`: [`IndexPriceStreamsParams`]
    ///   The parameters for this operation.
    ///
    /// # Returns
    ///
    /// [`Arc<WebsocketStream<Vec<models::IndexPriceStreamsResponseInner>>>`] on success.
    ///
    /// # Errors
    ///
    /// Returns an [`anyhow::Error`] if the stream request fails, if parameters are invalid, or if parsing the response fails.
    ///
    ///
    /// For full API details, see the [Binance API Documentation](https://developers.binance.com/docs/derivatives/options-trading/websocket-market-streams/Index-Price-Streams).
    ///
    pub async fn index_price_streams(
        &self,
        params: IndexPriceStreamsParams,
    ) -> anyhow::Result<Arc<WebsocketStream<Vec<models::IndexPriceStreamsResponseInner>>>> {
        self.market_api_client.index_price_streams(params).await
    }

    /// Kline/Candlestick Streams
    ///
    /// The Kline/Candlestick Stream push updates to the current klines/candlestick every 1000 milliseconds (if existing).
    ///
    /// Update Speed: 1000ms
    ///
    /// # Arguments
    ///
    /// - `params`: [`KlineCandlestickStreamsParams`]
    ///   The parameters for this operation.
    ///
    /// # Returns
    ///
    /// [`Arc<WebsocketStream<models::KlineCandlestickStreamsResponse>>`] on success.
    ///
    /// # Errors
    ///
    /// Returns an [`anyhow::Error`] if the stream request fails, if parameters are invalid, or if parsing the response fails.
    ///
    ///
    /// For full API details, see the [Binance API Documentation](https://developers.binance.com/docs/derivatives/options-trading/websocket-market-streams/Kline-Candlestick-Streams).
    ///
    pub async fn kline_candlestick_streams(
        &self,
        params: KlineCandlestickStreamsParams,
    ) -> anyhow::Result<Arc<WebsocketStream<models::KlineCandlestickStreamsResponse>>> {
        self.market_api_client
            .kline_candlestick_streams(params)
            .await
    }

    /// Mark Price
    ///
    /// The mark price for all option symbols on specific underlying asset. E.g.[btcusdt@optionMarkPrice](wss://fstream.binance.com/market/stream?streams=btcusdt@optionMarkPrice)
    ///
    /// Update Speed: 1000ms
    ///
    /// # Arguments
    ///
    /// - `params`: [`MarkPriceParams`]
    ///   The parameters for this operation.
    ///
    /// # Returns
    ///
    /// [`Arc<WebsocketStream<Vec<models::MarkPriceResponseInner>>>`] on success.
    ///
    /// # Errors
    ///
    /// Returns an [`anyhow::Error`] if the stream request fails, if parameters are invalid, or if parsing the response fails.
    ///
    ///
    /// For full API details, see the [Binance API Documentation](https://developers.binance.com/docs/derivatives/options-trading/websocket-market-streams/Mark-Price).
    ///
    pub async fn mark_price(
        &self,
        params: MarkPriceParams,
    ) -> anyhow::Result<Arc<WebsocketStream<Vec<models::MarkPriceResponseInner>>>> {
        self.market_api_client.mark_price(params).await
    }

    /// New Symbol Info
    ///
    /// New symbol listing stream.
    ///
    /// Update Speed: 50ms
    ///
    /// # Arguments
    ///
    /// - `params`: [`NewSymbolInfoParams`]
    ///   The parameters for this operation.
    ///
    /// # Returns
    ///
    /// [`Arc<WebsocketStream<models::NewSymbolInfoResponse>>`] on success.
    ///
    /// # Errors
    ///
    /// Returns an [`anyhow::Error`] if the stream request fails, if parameters are invalid, or if parsing the response fails.
    ///
    ///
    /// For full API details, see the [Binance API Documentation](https://developers.binance.com/docs/derivatives/options-trading/websocket-market-streams/New-Symbol-Info).
    ///
    pub async fn new_symbol_info(
        &self,
        params: NewSymbolInfoParams,
    ) -> anyhow::Result<Arc<WebsocketStream<models::NewSymbolInfoResponse>>> {
        self.market_api_client.new_symbol_info(params).await
    }

    /// Open Interest
    ///
    /// Option open interest for specific underlying asset on specific expiration date. E.g.[ethusdt@openInterest@221125](wss://fstream.binance.com/market/stream?streams=ethusdt@openInterest@221125)
    ///
    /// Update Speed: 60s
    ///
    /// # Arguments
    ///
    /// - `params`: [`OpenInterestParams`]
    ///   The parameters for this operation.
    ///
    /// # Returns
    ///
    /// [`Arc<WebsocketStream<Vec<models::OpenInterestResponseInner>>>`] on success.
    ///
    /// # Errors
    ///
    /// Returns an [`anyhow::Error`] if the stream request fails, if parameters are invalid, or if parsing the response fails.
    ///
    ///
    /// For full API details, see the [Binance API Documentation](https://developers.binance.com/docs/derivatives/options-trading/websocket-market-streams/Open-Interest).
    ///
    pub async fn open_interest(
        &self,
        params: OpenInterestParams,
    ) -> anyhow::Result<Arc<WebsocketStream<Vec<models::OpenInterestResponseInner>>>> {
        self.market_api_client.open_interest(params).await
    }

    /// Diff Book Depth Streams
    ///
    /// Bids and asks, pushed every 500 milliseconds, 100 milliseconds (if existing)
    ///
    /// Update Speed: 100ms or 500ms
    ///
    /// # Arguments
    ///
    /// - `params`: [`DiffBookDepthStreamsParams`]
    ///   The parameters for this operation.
    ///
    /// # Returns
    ///
    /// [`Arc<WebsocketStream<models::DiffBookDepthStreamsResponse>>`] on success.
    ///
    /// # Errors
    ///
    /// Returns an [`anyhow::Error`] if the stream request fails, if parameters are invalid, or if parsing the response fails.
    ///
    ///
    /// For full API details, see the [Binance API Documentation](https://developers.binance.com/docs/derivatives/options-trading/websocket-market-streams/Diff-Book-Depth-Streams).
    ///
    pub async fn diff_book_depth_streams(
        &self,
        params: DiffBookDepthStreamsParams,
    ) -> anyhow::Result<Arc<WebsocketStream<models::DiffBookDepthStreamsResponse>>> {
        self.public_api_client.diff_book_depth_streams(params).await
    }

    /// Individual Symbol Book Ticker Streams
    ///
    /// Pushes any update to the best bid or ask's price or quantity in real-time for a specified symbol.
    ///
    /// Update Speed: Real-Time
    ///
    /// # Arguments
    ///
    /// - `params`: [`IndividualSymbolBookTickerStreamsParams`]
    ///   The parameters for this operation.
    ///
    /// # Returns
    ///
    /// [`Arc<WebsocketStream<models::IndividualSymbolBookTickerStreamsResponse>>`] on success.
    ///
    /// # Errors
    ///
    /// Returns an [`anyhow::Error`] if the stream request fails, if parameters are invalid, or if parsing the response fails.
    ///
    ///
    /// For full API details, see the [Binance API Documentation](https://developers.binance.com/docs/derivatives/options-trading/websocket-market-streams/Individual-Symbol-Book-Ticker-Streams).
    ///
    pub async fn individual_symbol_book_ticker_streams(
        &self,
        params: IndividualSymbolBookTickerStreamsParams,
    ) -> anyhow::Result<Arc<WebsocketStream<models::IndividualSymbolBookTickerStreamsResponse>>>
    {
        self.public_api_client
            .individual_symbol_book_ticker_streams(params)
            .await
    }

    /// Partial Book Depth Streams
    ///
    /// Top **<levels\>** bids and asks, Valid levels are **<levels\>** are 5, 10, 20.
    ///
    /// Update Speed: 100ms or 500ms
    ///
    /// # Arguments
    ///
    /// - `params`: [`PartialBookDepthStreamsParams`]
    ///   The parameters for this operation.
    ///
    /// # Returns
    ///
    /// [`Arc<WebsocketStream<models::PartialBookDepthStreamsResponse>>`] on success.
    ///
    /// # Errors
    ///
    /// Returns an [`anyhow::Error`] if the stream request fails, if parameters are invalid, or if parsing the response fails.
    ///
    ///
    /// For full API details, see the [Binance API Documentation](https://developers.binance.com/docs/derivatives/options-trading/websocket-market-streams/Partial-Book-Depth-Streams).
    ///
    pub async fn partial_book_depth_streams(
        &self,
        params: PartialBookDepthStreamsParams,
    ) -> anyhow::Result<Arc<WebsocketStream<models::PartialBookDepthStreamsResponse>>> {
        self.public_api_client
            .partial_book_depth_streams(params)
            .await
    }

    /// 24-hour TICKER
    ///
    /// 24hr ticker info for all symbols. Only symbols whose ticker info changed will be sent.
    ///
    /// Update Speed: 1000ms
    ///
    /// # Arguments
    ///
    /// - `params`: [`Ticker24HourParams`]
    ///   The parameters for this operation.
    ///
    /// # Returns
    ///
    /// [`Arc<WebsocketStream<models::Ticker24HourResponse>>`] on success.
    ///
    /// # Errors
    ///
    /// Returns an [`anyhow::Error`] if the stream request fails, if parameters are invalid, or if parsing the response fails.
    ///
    ///
    /// For full API details, see the [Binance API Documentation](https://developers.binance.com/docs/derivatives/options-trading/websocket-market-streams/24-hour-TICKER).
    ///
    pub async fn ticker24_hour(
        &self,
        params: Ticker24HourParams,
    ) -> anyhow::Result<Arc<WebsocketStream<models::Ticker24HourResponse>>> {
        self.public_api_client.ticker24_hour(params).await
    }

    /// Trade Streams
    ///
    /// The Trade Streams push raw trade information for specific symbol or underlying asset. E.g.[btcusdt@optionTrade](wss://fstream.binance.com/public/stream?streams=btcusdt@optionTrade)
    ///
    /// Update Speed: 50ms
    ///
    /// # Arguments
    ///
    /// - `params`: [`TradeStreamsParams`]
    ///   The parameters for this operation.
    ///
    /// # Returns
    ///
    /// [`Arc<WebsocketStream<models::TradeStreamsResponse>>`] on success.
    ///
    /// # Errors
    ///
    /// Returns an [`anyhow::Error`] if the stream request fails, if parameters are invalid, or if parsing the response fails.
    ///
    ///
    /// For full API details, see the [Binance API Documentation](https://developers.binance.com/docs/derivatives/options-trading/websocket-market-streams/Trade-Streams).
    ///
    pub async fn trade_streams(
        &self,
        params: TradeStreamsParams,
    ) -> anyhow::Result<Arc<WebsocketStream<models::TradeStreamsResponse>>> {
        self.public_api_client.trade_streams(params).await
    }
}
