"""
Binance Spot WebSocket API

OpenAPI Specifications for the Binance Spot WebSocket API

API documents:
  - [Github web-socket-api documentation file](https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-api.md)
  - [General API information for web-socket-api on website](https://developers.binance.com/docs/binance-spot-api-docs/web-socket-api/general-api-information)

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_spot.websocket_api.api import UserDataStreamApi


from binance_sdk_spot.websocket_api.models import SessionSubscriptionsResponse
from binance_sdk_spot.websocket_api.models import UserDataStreamSubscribeResponse
from binance_sdk_spot.websocket_api.models import (
    UserDataStreamSubscribeSignatureResponse,
)
from binance_sdk_spot.websocket_api.models import UserDataStreamUnsubscribeResponse


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

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

        expected_response = {
            "id": "d3df5a22-88ea-4fe0-9f4e-0fcea5d418b7",
            "status": 200,
            "result": [{"subscriptionId": 1}, {"subscriptionId": 0}],
        }

        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.session_subscriptions()

        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"] == "/session.subscriptions".replace(
            "/", "", 1
        )

        assert result is not None
        assert result.data() == expected_response
        self.mock_websocket_api.send_message.assert_called_once_with(
            payload={
                "method": "/session.subscriptions".replace("/", "", 1),
                "params": {},
            },
            response_model=SessionSubscriptionsResponse,
        )

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

        params = {"id": "e9d6b4349871b40611412680b3445fac"}

        expected_response = {
            "id": "d3df5a22-88ea-4fe0-9f4e-0fcea5d418b7",
            "status": 200,
            "result": [{"subscriptionId": 1}, {"subscriptionId": 0}],
        }

        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.session_subscriptions(**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"] == "/session.subscriptions".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": "/session.subscriptions".replace("/", "", 1),
                "params": params,
            },
            response_model=SessionSubscriptionsResponse,
        )

    @pytest.mark.asyncio
    async def test_session_subscriptions_server_error(self):
        """Test that session_subscriptions() 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.session_subscriptions()

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

        expected_response = {
            "id": "d3df8a21-98ea-4fe0-8f4e-0fcea5d418b7",
            "status": 200,
            "result": {"subscriptionId": 0},
        }

        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.user_data_stream_subscribe()

        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.subscribe".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.subscribe".replace("/", "", 1),
                "params": {},
            },
            response_model=UserDataStreamSubscribeResponse,
        )

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

        params = {"id": "e9d6b4349871b40611412680b3445fac"}

        expected_response = {
            "id": "d3df8a21-98ea-4fe0-8f4e-0fcea5d418b7",
            "status": 200,
            "result": {"subscriptionId": 0},
        }

        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.user_data_stream_subscribe(**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.subscribe".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.subscribe".replace("/", "", 1),
                "params": params,
            },
            response_model=UserDataStreamSubscribeResponse,
        )

    @pytest.mark.asyncio
    async def test_user_data_stream_subscribe_server_error(self):
        """Test that user_data_stream_subscribe() 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.user_data_stream_subscribe()

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

        expected_response = {
            "id": "d3df8a22-98ea-4fe0-9f4e-0fcea5d418b7",
            "status": 200,
            "result": {"subscriptionId": 0},
        }

        self.mock_websocket_api.send_signed_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.user_data_stream_subscribe_signature()

        actual_call_args = self.mock_websocket_api.send_signed_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.subscribe.signature".replace("/", "", 1)

        assert result is not None
        assert result.data() == expected_response
        self.mock_websocket_api.send_signed_message.assert_called_once_with(
            payload={
                "method": "/userDataStream.subscribe.signature".replace("/", "", 1),
                "params": {},
            },
            response_model=UserDataStreamSubscribeSignatureResponse,
            signer=None,
        )

    @pytest.mark.asyncio
    async def test_user_data_stream_subscribe_signature_success_with_optional_params(
        self,
    ):
        """Test user_data_stream_subscribe_signature() successfully with optional parameters."""

        params = {"id": "e9d6b4349871b40611412680b3445fac"}

        expected_response = {
            "id": "d3df8a22-98ea-4fe0-9f4e-0fcea5d418b7",
            "status": 200,
            "result": {"subscriptionId": 0},
        }

        self.mock_websocket_api.send_signed_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.user_data_stream_subscribe_signature(**params)

        actual_call_args = self.mock_websocket_api.send_signed_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.subscribe.signature".replace("/", "", 1)
        assert params["id"] == "e9d6b4349871b40611412680b3445fac"

        assert result is not None
        assert result.data() == expected_response
        self.mock_websocket_api.send_signed_message.assert_called_once_with(
            payload={
                "method": "/userDataStream.subscribe.signature".replace("/", "", 1),
                "params": params,
            },
            response_model=UserDataStreamSubscribeSignatureResponse,
            signer=None,
        )

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

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

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

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

        expected_response = {
            "id": "d3df8a21-98ea-4fe0-8f4e-0fcea5d418b7",
            "status": 200,
            "result": {},
        }

        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.user_data_stream_unsubscribe()

        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.unsubscribe".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.unsubscribe".replace("/", "", 1),
                "params": {},
            },
            response_model=UserDataStreamUnsubscribeResponse,
        )

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

        params = {"id": "e9d6b4349871b40611412680b3445fac", "subscription_id": 1}

        expected_response = {
            "id": "d3df8a21-98ea-4fe0-8f4e-0fcea5d418b7",
            "status": 200,
            "result": {},
        }

        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.user_data_stream_unsubscribe(**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.unsubscribe".replace("/", "", 1)
        assert params["id"] == "e9d6b4349871b40611412680b3445fac"
        assert params["subscription_id"] == 1

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

    @pytest.mark.asyncio
    async def test_user_data_stream_unsubscribe_server_error(self):
        """Test that user_data_stream_unsubscribe() 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.user_data_stream_unsubscribe()
