"""
Binance VIP Loan REST API

OpenAPI Specification for the Binance VIP Loan REST 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 json
import pytest
import requests

from unittest.mock import MagicMock, patch
from urllib.parse import parse_qs

from binance_common.configuration import ConfigurationRestAPI
from binance_common.utils import is_one_of_model

from binance_sdk_vip_loan.rest_api.api import UserInformationApi
from binance_sdk_vip_loan.rest_api.models import CheckVIPLoanCollateralAccountResponse
from binance_sdk_vip_loan.rest_api.models import GetVIPLoanOngoingOrdersResponse
from binance_sdk_vip_loan.rest_api.models import QueryApplicationStatusResponse


class TestUserInformationApi:
    @pytest.fixture(autouse=True)
    def setup_client(self):
        """Setup a client instance with mocked session before each test method."""
        self.mock_session = MagicMock(spec=requests.Session)
        config = ConfigurationRestAPI(
            api_key="test-api-key",
            api_secret="test-api-secret",
        )
        self.client = UserInformationApi(
            configuration=config, session=self.mock_session
        )

    def set_mock_response(self, data: dict = {}, status_code=200, headers=None):
        """Helper method to setup mock response for the client's session request."""
        if headers is None:
            headers = {}

        mock_response = MagicMock()
        mock_response.status_code = status_code
        mock_response.json.return_value = data
        mock_response.text = json.dumps(data)
        mock_response.headers = headers

        self.mock_session.request.return_value = mock_response

    @patch("binance_common.utils.get_signature")
    def test_check_vip_loan_collateral_account_success(self, mock_get_signature):
        """Test check_vip_loan_collateral_account() successfully with required parameters only."""

        expected_response = {
            "rows": [
                {"collateralAccountId": "12345678", "collateralCoin": "BNB,BTC,ETH"},
                {"collateralAccountId": "23456789", "collateralCoin": "BNB,BTC,ETH"},
            ],
            "total": 2,
        }
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

        response = self.client.check_vip_loan_collateral_account()

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs

        self.mock_session.request.assert_called_once()
        mock_get_signature.assert_called_once()

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/sapi/v1/loan/vip/collateral/account" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"

        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(CheckVIPLoanCollateralAccountResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif (
            is_oneof
            or is_list
            or hasattr(CheckVIPLoanCollateralAccountResponse, "from_dict")
        ):
            expected = CheckVIPLoanCollateralAccountResponse.from_dict(
                expected_response
            )
        else:
            expected = CheckVIPLoanCollateralAccountResponse.model_validate_json(
                json.dumps(expected_response)
            )

        assert response.data() == expected

    @patch("binance_common.utils.get_signature")
    def test_check_vip_loan_collateral_account_success_with_optional_params(
        self, mock_get_signature
    ):
        """Test check_vip_loan_collateral_account() successfully with optional parameters."""

        params = {"order_id": 1, "collateral_account_id": 1, "recv_window": 5000}

        expected_response = {
            "rows": [
                {"collateralAccountId": "12345678", "collateralCoin": "BNB,BTC,ETH"},
                {"collateralAccountId": "23456789", "collateralCoin": "BNB,BTC,ETH"},
            ],
            "total": 2,
        }
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

        response = self.client.check_vip_loan_collateral_account(**params)

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/sapi/v1/loan/vip/collateral/account" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"

        self.mock_session.request.assert_called_once()
        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(CheckVIPLoanCollateralAccountResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif (
            is_oneof
            or is_list
            or hasattr(CheckVIPLoanCollateralAccountResponse, "from_dict")
        ):
            expected = CheckVIPLoanCollateralAccountResponse.from_dict(
                expected_response
            )
        else:
            expected = CheckVIPLoanCollateralAccountResponse.model_validate_json(
                json.dumps(expected_response)
            )

        assert response.data() == expected

    def test_check_vip_loan_collateral_account_server_error(self):
        """Test that check_vip_loan_collateral_account() raises an error when the server returns an error."""

        mock_error = Exception("ResponseError")
        self.client.check_vip_loan_collateral_account = MagicMock(
            side_effect=mock_error
        )

        with pytest.raises(Exception, match="ResponseError"):
            self.client.check_vip_loan_collateral_account()

    @patch("binance_common.utils.get_signature")
    def test_get_vip_loan_ongoing_orders_success(self, mock_get_signature):
        """Test get_vip_loan_ongoing_orders() successfully with required parameters only."""

        expected_response = {
            "rows": [
                {
                    "orderId": 100000001,
                    "loanCoin": "BUSD",
                    "totalDebt": "10000",
                    "residualInterest": "10.27687923",
                    "collateralAccountId": "12345678,23456789",
                    "collateralCoin": "BNB,BTC,ETH",
                    "totalCollateralValueAfterHaircut": "25000.27565492",
                    "lockedCollateralValue": "25000.27565492",
                    "currentLTV": "0.57",
                    "expirationTime": 1575018510000,
                    "loanDate": "1676851200000",
                    "loanTerm": "30days",
                }
            ],
            "total": 1,
        }
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

        response = self.client.get_vip_loan_ongoing_orders()

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs

        self.mock_session.request.assert_called_once()
        mock_get_signature.assert_called_once()

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/sapi/v1/loan/vip/ongoing/orders" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"

        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(GetVIPLoanOngoingOrdersResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif (
            is_oneof or is_list or hasattr(GetVIPLoanOngoingOrdersResponse, "from_dict")
        ):
            expected = GetVIPLoanOngoingOrdersResponse.from_dict(expected_response)
        else:
            expected = GetVIPLoanOngoingOrdersResponse.model_validate_json(
                json.dumps(expected_response)
            )

        assert response.data() == expected

    @patch("binance_common.utils.get_signature")
    def test_get_vip_loan_ongoing_orders_success_with_optional_params(
        self, mock_get_signature
    ):
        """Test get_vip_loan_ongoing_orders() successfully with optional parameters."""

        params = {
            "order_id": 1,
            "collateral_account_id": 1,
            "loan_coin": "loan_coin_example",
            "collateral_coin": "collateral_coin_example",
            "current": 1,
            "limit": 10,
            "recv_window": 5000,
        }

        expected_response = {
            "rows": [
                {
                    "orderId": 100000001,
                    "loanCoin": "BUSD",
                    "totalDebt": "10000",
                    "residualInterest": "10.27687923",
                    "collateralAccountId": "12345678,23456789",
                    "collateralCoin": "BNB,BTC,ETH",
                    "totalCollateralValueAfterHaircut": "25000.27565492",
                    "lockedCollateralValue": "25000.27565492",
                    "currentLTV": "0.57",
                    "expirationTime": 1575018510000,
                    "loanDate": "1676851200000",
                    "loanTerm": "30days",
                }
            ],
            "total": 1,
        }
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

        response = self.client.get_vip_loan_ongoing_orders(**params)

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/sapi/v1/loan/vip/ongoing/orders" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"

        self.mock_session.request.assert_called_once()
        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(GetVIPLoanOngoingOrdersResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif (
            is_oneof or is_list or hasattr(GetVIPLoanOngoingOrdersResponse, "from_dict")
        ):
            expected = GetVIPLoanOngoingOrdersResponse.from_dict(expected_response)
        else:
            expected = GetVIPLoanOngoingOrdersResponse.model_validate_json(
                json.dumps(expected_response)
            )

        assert response.data() == expected

    def test_get_vip_loan_ongoing_orders_server_error(self):
        """Test that get_vip_loan_ongoing_orders() raises an error when the server returns an error."""

        mock_error = Exception("ResponseError")
        self.client.get_vip_loan_ongoing_orders = MagicMock(side_effect=mock_error)

        with pytest.raises(Exception, match="ResponseError"):
            self.client.get_vip_loan_ongoing_orders()

    @patch("binance_common.utils.get_signature")
    def test_query_application_status_success(self, mock_get_signature):
        """Test query_application_status() successfully with required parameters only."""

        expected_response = {
            "rows": [
                {
                    "loanAccountId": "12345678",
                    "orderId": "12345678",
                    "requestId": "12345678",
                    "loanCoin": "BTC",
                    "loanAmount": "100.55",
                    "collateralAccountId": "12345678,12345678,12345678",
                    "collateralCoin": "BUSD,USDT,ETH",
                    "loanTerm": "30",
                    "status": "Repaid",
                    "loanDate": "1676851200000",
                }
            ],
            "total": 1,
        }
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

        response = self.client.query_application_status()

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs

        self.mock_session.request.assert_called_once()
        mock_get_signature.assert_called_once()

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/sapi/v1/loan/vip/request/data" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"

        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(QueryApplicationStatusResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif (
            is_oneof or is_list or hasattr(QueryApplicationStatusResponse, "from_dict")
        ):
            expected = QueryApplicationStatusResponse.from_dict(expected_response)
        else:
            expected = QueryApplicationStatusResponse.model_validate_json(
                json.dumps(expected_response)
            )

        assert response.data() == expected

    @patch("binance_common.utils.get_signature")
    def test_query_application_status_success_with_optional_params(
        self, mock_get_signature
    ):
        """Test query_application_status() successfully with optional parameters."""

        params = {"current": 1, "limit": 10, "recv_window": 5000}

        expected_response = {
            "rows": [
                {
                    "loanAccountId": "12345678",
                    "orderId": "12345678",
                    "requestId": "12345678",
                    "loanCoin": "BTC",
                    "loanAmount": "100.55",
                    "collateralAccountId": "12345678,12345678,12345678",
                    "collateralCoin": "BUSD,USDT,ETH",
                    "loanTerm": "30",
                    "status": "Repaid",
                    "loanDate": "1676851200000",
                }
            ],
            "total": 1,
        }
        mock_get_signature.return_value = "mocked_signature"
        self.set_mock_response(expected_response)

        response = self.client.query_application_status(**params)

        actual_call_args = self.mock_session.request.call_args
        request_kwargs = actual_call_args.kwargs

        assert "url" in request_kwargs
        assert "signature" in parse_qs(request_kwargs["params"])
        assert "/sapi/v1/loan/vip/request/data" in request_kwargs["url"]
        assert request_kwargs["method"] == "GET"

        self.mock_session.request.assert_called_once()
        assert response is not None
        is_list = isinstance(expected_response, list)
        is_flat_list = (
            is_list and not isinstance(expected_response[0], list) if is_list else False
        )
        is_oneof = is_one_of_model(QueryApplicationStatusResponse)

        if is_list and not is_flat_list:
            expected = expected_response
        elif (
            is_oneof or is_list or hasattr(QueryApplicationStatusResponse, "from_dict")
        ):
            expected = QueryApplicationStatusResponse.from_dict(expected_response)
        else:
            expected = QueryApplicationStatusResponse.model_validate_json(
                json.dumps(expected_response)
            )

        assert response.data() == expected

    def test_query_application_status_server_error(self):
        """Test that query_application_status() raises an error when the server returns an error."""

        mock_error = Exception("ResponseError")
        self.client.query_application_status = MagicMock(side_effect=mock_error)

        with pytest.raises(Exception, match="ResponseError"):
            self.client.query_application_status()
