"""
Binance Derivatives Trading USDS Futures WebSocket API

OpenAPI Specification for the Binance Derivatives Trading USDS Futures WebSocket API
The version of the OpenAPI document: 1.0.0
Generated by OpenAPI Generator (https://openapi-generator.tech)

Do not edit the class manually.
"""

import pytest

from unittest.mock import AsyncMock, MagicMock

from binance_common.models import WebsocketApiResponse
from binance_common.utils import parse_ws_rate_limit_headers
from binance_sdk_derivatives_trading_usds_futures.websocket_api.api import (
    UserDataStreamsApi,
)


from binance_sdk_derivatives_trading_usds_futures.websocket_api.models import (
    CloseUserDataStreamResponse,
)
from binance_sdk_derivatives_trading_usds_futures.websocket_api.models import (
    KeepaliveUserDataStreamResponse,
)
from binance_sdk_derivatives_trading_usds_futures.websocket_api.models import (
    StartUserDataStreamResponse,
)


class TestWebSocketUserDataStreamsApi:
    @pytest.fixture(autouse=True)
    def setup_method(self):
        self.mock_websocket_api = MagicMock()
        self.websocket_api = UserDataStreamsApi(websocket_api=self.mock_websocket_api)

    @pytest.mark.asyncio
    async def test_close_user_data_stream_success(self):
        """Test close_user_data_stream() successfully with required parameters only."""

        expected_response = {
            "id": "819e1b1b-8c06-485b-a13e-131326c69599",
            "status": 200,
            "result": {},
            "rateLimits": [
                {
                    "rateLimitType": "REQUEST_WEIGHT",
                    "interval": "MINUTE",
                    "intervalNum": 1,
                    "limit": 2400,
                    "count": 2,
                }
            ],
        }

        self.mock_websocket_api.send_message = AsyncMock(
            return_value=WebsocketApiResponse(
                data_function=lambda: expected_response,
                rate_limits=(
                    parse_ws_rate_limit_headers(expected_response["rateLimits"])
                    if "rateLimits" in expected_response
                    else None
                ),
            )
        )
        result = await self.websocket_api.close_user_data_stream()

        actual_call_args = self.mock_websocket_api.send_message.call_args
        request_kwargs = actual_call_args.kwargs

        assert "payload" in request_kwargs
        assert "method" in request_kwargs["payload"]
        assert request_kwargs["payload"]["method"] == "/userDataStream.stop".replace(
            "/", "", 1
        )

        assert result is not None
        assert result.data() == expected_response
        self.mock_websocket_api.send_message.assert_called_once_with(
            payload={
                "method": "/userDataStream.stop".replace("/", "", 1),
                "params": {},
            },
            response_model=CloseUserDataStreamResponse,
            api_key=True,
        )

    @pytest.mark.asyncio
    async def test_close_user_data_stream_success_with_optional_params(self):
        """Test close_user_data_stream() successfully with optional parameters."""

        params = {"id": "e9d6b4349871b40611412680b3445fac"}

        expected_response = {
            "id": "819e1b1b-8c06-485b-a13e-131326c69599",
            "status": 200,
            "result": {},
            "rateLimits": [
                {
                    "rateLimitType": "REQUEST_WEIGHT",
                    "interval": "MINUTE",
                    "intervalNum": 1,
                    "limit": 2400,
                    "count": 2,
                }
            ],
        }

        self.mock_websocket_api.send_message = AsyncMock(
            return_value=WebsocketApiResponse(
                data_function=lambda: expected_response,
                rate_limits=(
                    parse_ws_rate_limit_headers(expected_response["rateLimits"])
                    if "rateLimits" in expected_response
                    else None
                ),
            )
        )

        result = await self.websocket_api.close_user_data_stream(**params)

        actual_call_args = self.mock_websocket_api.send_message.call_args
        request_kwargs = actual_call_args.kwargs

        assert "payload" in request_kwargs
        assert "method" in request_kwargs["payload"]
        assert request_kwargs["payload"]["method"] == "/userDataStream.stop".replace(
            "/", "", 1
        )
        assert "params" in request_kwargs["payload"]
        params = request_kwargs["payload"]["params"]
        assert params["id"] == "e9d6b4349871b40611412680b3445fac"

        assert result is not None
        assert result.data() == expected_response
        self.mock_websocket_api.send_message.assert_called_once_with(
            payload={
                "method": "/userDataStream.stop".replace("/", "", 1),
                "params": params,
            },
            response_model=CloseUserDataStreamResponse,
            api_key=True,
        )

    @pytest.mark.asyncio
    async def test_close_user_data_stream_server_error(self):
        """Test that close_user_data_stream() raises an error when the server returns an error."""

        mock_error = Exception("ResponseError")
        self.mock_websocket_api.send_message.side_effect = mock_error

        with pytest.raises(Exception, match="ResponseError"):
            await self.websocket_api.close_user_data_stream()

    @pytest.mark.asyncio
    async def test_keepalive_user_data_stream_success(self):
        """Test keepalive_user_data_stream() successfully with required parameters only."""

        expected_response = {
            "id": "815d5fce-0880-4287-a567-80badf004c74",
            "status": 200,
            "result": {
                "listenKey": "3HBntNTepshgEdjIwSUIBgB9keLyOCg5qv3n6bYAtktG8ejcaW5HXz9Vx1JgIieg"
            },
            "rateLimits": [
                {
                    "rateLimitType": "REQUEST_WEIGHT",
                    "interval": "MINUTE",
                    "intervalNum": 1,
                    "limit": 2400,
                    "count": 2,
                }
            ],
        }

        self.mock_websocket_api.send_message = AsyncMock(
            return_value=WebsocketApiResponse(
                data_function=lambda: expected_response,
                rate_limits=(
                    parse_ws_rate_limit_headers(expected_response["rateLimits"])
                    if "rateLimits" in expected_response
                    else None
                ),
            )
        )
        result = await self.websocket_api.keepalive_user_data_stream()

        actual_call_args = self.mock_websocket_api.send_message.call_args
        request_kwargs = actual_call_args.kwargs

        assert "payload" in request_kwargs
        assert "method" in request_kwargs["payload"]
        assert request_kwargs["payload"]["method"] == "/userDataStream.ping".replace(
            "/", "", 1
        )

        assert result is not None
        assert result.data() == expected_response
        self.mock_websocket_api.send_message.assert_called_once_with(
            payload={
                "method": "/userDataStream.ping".replace("/", "", 1),
                "params": {},
            },
            response_model=KeepaliveUserDataStreamResponse,
            api_key=True,
        )

    @pytest.mark.asyncio
    async def test_keepalive_user_data_stream_success_with_optional_params(self):
        """Test keepalive_user_data_stream() successfully with optional parameters."""

        params = {"id": "e9d6b4349871b40611412680b3445fac"}

        expected_response = {
            "id": "815d5fce-0880-4287-a567-80badf004c74",
            "status": 200,
            "result": {
                "listenKey": "3HBntNTepshgEdjIwSUIBgB9keLyOCg5qv3n6bYAtktG8ejcaW5HXz9Vx1JgIieg"
            },
            "rateLimits": [
                {
                    "rateLimitType": "REQUEST_WEIGHT",
                    "interval": "MINUTE",
                    "intervalNum": 1,
                    "limit": 2400,
                    "count": 2,
                }
            ],
        }

        self.mock_websocket_api.send_message = AsyncMock(
            return_value=WebsocketApiResponse(
                data_function=lambda: expected_response,
                rate_limits=(
                    parse_ws_rate_limit_headers(expected_response["rateLimits"])
                    if "rateLimits" in expected_response
                    else None
                ),
            )
        )

        result = await self.websocket_api.keepalive_user_data_stream(**params)

        actual_call_args = self.mock_websocket_api.send_message.call_args
        request_kwargs = actual_call_args.kwargs

        assert "payload" in request_kwargs
        assert "method" in request_kwargs["payload"]
        assert request_kwargs["payload"]["method"] == "/userDataStream.ping".replace(
            "/", "", 1
        )
        assert params["id"] == "e9d6b4349871b40611412680b3445fac"

        assert result is not None
        assert result.data() == expected_response
        self.mock_websocket_api.send_message.assert_called_once_with(
            payload={
                "method": "/userDataStream.ping".replace("/", "", 1),
                "params": params,
            },
            response_model=KeepaliveUserDataStreamResponse,
            api_key=True,
        )

    @pytest.mark.asyncio
    async def test_keepalive_user_data_stream_server_error(self):
        """Test that keepalive_user_data_stream() raises an error when the server returns an error."""

        mock_error = Exception("ResponseError")
        self.mock_websocket_api.send_message.side_effect = mock_error

        with pytest.raises(Exception, match="ResponseError"):
            await self.websocket_api.keepalive_user_data_stream()

    @pytest.mark.asyncio
    async def test_start_user_data_stream_success(self):
        """Test start_user_data_stream() successfully with required parameters only."""

        expected_response = {
            "id": "d3df8a61-98ea-4fe0-8f4e-0fcea5d418b0",
            "status": 200,
            "result": {
                "listenKey": "xs0mRXdAKlIPDRFrlPcw0qI41Eh3ixNntmymGyhrhgqo7L6FuLaWArTD7RLP"
            },
            "rateLimits": [
                {
                    "rateLimitType": "REQUEST_WEIGHT",
                    "interval": "MINUTE",
                    "intervalNum": 1,
                    "limit": 2400,
                    "count": 2,
                }
            ],
        }

        self.mock_websocket_api.send_message = AsyncMock(
            return_value=WebsocketApiResponse(
                data_function=lambda: expected_response,
                rate_limits=(
                    parse_ws_rate_limit_headers(expected_response["rateLimits"])
                    if "rateLimits" in expected_response
                    else None
                ),
            )
        )
        result = await self.websocket_api.start_user_data_stream()

        actual_call_args = self.mock_websocket_api.send_message.call_args
        request_kwargs = actual_call_args.kwargs

        assert "payload" in request_kwargs
        assert "method" in request_kwargs["payload"]
        assert request_kwargs["payload"]["method"] == "/userDataStream.start".replace(
            "/", "", 1
        )

        assert result is not None
        assert result.data() == expected_response
        self.mock_websocket_api.send_message.assert_called_once_with(
            payload={
                "method": "/userDataStream.start".replace("/", "", 1),
                "params": {},
            },
            response_model=StartUserDataStreamResponse,
            api_key=True,
        )

    @pytest.mark.asyncio
    async def test_start_user_data_stream_success_with_optional_params(self):
        """Test start_user_data_stream() successfully with optional parameters."""

        params = {"id": "e9d6b4349871b40611412680b3445fac"}

        expected_response = {
            "id": "d3df8a61-98ea-4fe0-8f4e-0fcea5d418b0",
            "status": 200,
            "result": {
                "listenKey": "xs0mRXdAKlIPDRFrlPcw0qI41Eh3ixNntmymGyhrhgqo7L6FuLaWArTD7RLP"
            },
            "rateLimits": [
                {
                    "rateLimitType": "REQUEST_WEIGHT",
                    "interval": "MINUTE",
                    "intervalNum": 1,
                    "limit": 2400,
                    "count": 2,
                }
            ],
        }

        self.mock_websocket_api.send_message = AsyncMock(
            return_value=WebsocketApiResponse(
                data_function=lambda: expected_response,
                rate_limits=(
                    parse_ws_rate_limit_headers(expected_response["rateLimits"])
                    if "rateLimits" in expected_response
                    else None
                ),
            )
        )

        result = await self.websocket_api.start_user_data_stream(**params)

        actual_call_args = self.mock_websocket_api.send_message.call_args
        request_kwargs = actual_call_args.kwargs

        assert "payload" in request_kwargs
        assert "method" in request_kwargs["payload"]
        assert request_kwargs["payload"]["method"] == "/userDataStream.start".replace(
            "/", "", 1
        )
        assert params["id"] == "e9d6b4349871b40611412680b3445fac"

        assert result is not None
        assert result.data() == expected_response
        self.mock_websocket_api.send_message.assert_called_once_with(
            payload={
                "method": "/userDataStream.start".replace("/", "", 1),
                "params": params,
            },
            response_model=StartUserDataStreamResponse,
            api_key=True,
        )

    @pytest.mark.asyncio
    async def test_start_user_data_stream_server_error(self):
        """Test that start_user_data_stream() raises an error when the server returns an error."""

        mock_error = Exception("ResponseError")
        self.mock_websocket_api.send_message.side_effect = mock_error

        with pytest.raises(Exception, match="ResponseError"):
            await self.websocket_api.start_user_data_stream()
