# Generated by CodiumAI
import asyncio
import time

import pytest
from aiohttp import ClientConnectionError, ClientResponseError

from pyadtpulse.const import (
    ADT_DEFAULT_VERSION,
    ADT_LOGIN_URI,
    ADT_LOGOUT_URI,
    ADT_ORB_URI,
    API_HOST_CA,
    API_PREFIX,
    DEFAULT_API_HOST,
)
from pyadtpulse.pulse_connection import ADTPulseConnection


class TestADTPulseConnection:
    # can initialize ADTPulseConnection with valid service host and user agent
    def test_initialize_with_valid_service_host_and_user_agent(self):
        host = DEFAULT_API_HOST
        user_agent = "Test User Agent"
        connection = ADTPulseConnection(host, user_agent=user_agent)

        assert connection.service_host == host
        assert connection._session.headers["User-Agent"] == user_agent

    # can set and get service host
    @pytest.mark.asyncio
    async def test_set_and_get_service_host(self):
        host = DEFAULT_API_HOST
        connection = ADTPulseConnection(host)

        new_host = API_HOST_CA
        connection.service_host = new_host

        assert connection.service_host == new_host

    # can set and get event loop
    @pytest.mark.asyncio
    async def test_set_and_get_event_loop(self):
        host = DEFAULT_API_HOST
        connection = ADTPulseConnection(host)

        loop = asyncio.get_event_loop()
        connection.loop = loop

        assert connection.loop == loop

    # can get last login time
    @pytest.mark.asyncio
    async def test_get_last_login_time(self):
        host = DEFAULT_API_HOST
        connection = ADTPulseConnection(host)

        assert connection.last_login_time == 0

    # can set and get retry after time
    @pytest.mark.asyncio
    async def test_set_and_get_retry_after_time(self):
        connection = ADTPulseConnection(DEFAULT_API_HOST)

        retry_after = int(time.time()) + 60
        connection.retry_after = retry_after

        assert connection.retry_after == retry_after

    # can get authenticated flag
    @pytest.mark.asyncio
    async def test_get_authenticated_flag(self):
        host = DEFAULT_API_HOST
        connection = ADTPulseConnection(host)

        assert not connection.authenticated_flag.is_set()

    # raises ValueError if service host is None or empty string
    @pytest.mark.asyncio
    async def test_raises_value_error_if_service_host_is_none_or_empty_string(self):
        with pytest.raises(ValueError):
            ADTPulseConnection(None)

        with pytest.raises(ValueError):
            ADTPulseConnection("")

    # raises ValueError if service host is not DEFAULT_API_HOST or API_HOST_CA
    @pytest.mark.asyncio
    async def test_raises_value_error_if_service_host_is_not_default_api_host_or_api_host_ca(
        self,
    ):
        with pytest.raises(ValueError):
            ADTPulseConnection("example.com")

        with pytest.raises(ValueError):
            ADTPulseConnection("api.example.com")

    # raises ValueError if retry_after is less than current time
    @pytest.mark.asyncio
    async def test_raises_value_error_if_retry_after_is_less_than_current_time(self):
        host = DEFAULT_API_HOST
        connection = ADTPulseConnection(host)

        with pytest.raises(ValueError):
            connection.retry_after = int(time.time()) - 60

    # can make url with given uri using a valid host
    @pytest.mark.asyncio
    async def test_make_url_with_given_uri_with_valid_host(self):
        host = DEFAULT_API_HOST
        connection = ADTPulseConnection(host)
        uri = "/api/v1/status"
        expected_url = f"{host}/myhome/{ADTPulseConnection._api_version}{uri}"

        url = connection.make_url(uri)

        assert url == expected_url

    # can set and get detailed debug logging
    @pytest.mark.asyncio
    async def test_set_and_get_detailed_debug_logging(self):
        connection = ADTPulseConnection(DEFAULT_API_HOST)

        assert connection.detailed_debug_logging == False

        connection.detailed_debug_logging = True
        assert connection.detailed_debug_logging == True

        connection.detailed_debug_logging = False
        assert connection.detailed_debug_logging == False

    # can do logout query with valid site_id
    @pytest.mark.asyncio
    async def test_logout_query_with_valid_site_id_fixed(self, mocker):
        # Mock the async_query method
        mocker.patch.object(ADTPulseConnection, "async_query")
        connection = ADTPulseConnection(DEFAULT_API_HOST)

        # Set up mock response
        response = mocker.Mock()
        response.status = 200
        ADTPulseConnection.async_query.return_value = response

        # Call the logout query method
        await connection.async_do_logout_query("site_id")

        # Assert that the async_query method was called with the correct parameters
        ADTPulseConnection.async_query.assert_called_once_with(
            ADT_LOGOUT_URI,
            extra_params={"network": "site_id", "partner": "adt"},
            timeout=10,
            requires_authentication=False,
        )

        # Assert that the authenticated_flag was cleared
        assert not connection.authenticated_flag.is_set()

    # can fetch api version with the recommended fix
    @pytest.mark.asyncio
    async def test_fetch_api_version_with_fix(self, mocker):
        # Mock the session and response objects
        session_mock = mocker.Mock()
        response_mock = mocker.Mock()
        response_mock.status = 200
        response_mock.real_url.path = "/myhome/1.0.0/"
        # Mock the session request method to return the mocked response
        session_mock.request.return_value = response_mock

        # Create an instance of ADTPulseConnection with the mocked session
        connection = ADTPulseConnection(DEFAULT_API_HOST, session=session_mock)

        # Call the async_fetch_version method
        await connection.async_fetch_version()

        # Assert that the api_version is updated correctly
        assert connection.api_version == "1.0.0"
        assert connection.service_host == DEFAULT_API_HOST
        assert connection._session.headers["Host"] == f"{DEFAULT_API_HOST}/myhome/1.0.0"

        # Assert that the session request method was called with the correct arguments
        session_mock.request.assert_called_once_with(
            "GET",
            f"{DEFAULT_API_HOST}/myhome/1.0.0",
            headers={"Accept": "application/json"},
            params=None,
            data=None,
            timeout=1,
        )

    # can do login query with valid credentials with fixed host
    @pytest.mark.asyncio
    async def test_login_query_with_valid_credentials_with_fixed_host(self, mocker):
        # Mock the necessary dependencies
        session_mock = mocker.AsyncMock()
        response_mock = mocker.AsyncMock()
        response_mock.status = 200
        response_mock.headers = {"Retry-After": "10"}
        response_mock.real_url.path = "/myhome/1.0/"
        session_mock.request.return_value.__aenter__.return_value = response_mock

        # Create an instance of ADTPulseConnection with fixed host
        connection = ADTPulseConnection(DEFAULT_API_HOST, session=session_mock)

        # Set the authenticated flag to True
        connection._authenticated_flag.set()

        # Mock the make_soup function
        make_soup_mock = mocker.patch("pyadtpulse.util.make_soup")
        make_soup_mock.return_value = None

        # Call the async_query method with valid credentials
        result = await connection.async_query(
            ADT_LOGIN_URI,
            method="POST",
            extra_params={
                "partner": "adt",
                "e": "ns",
                "usernameForm": "test_user",
                "passwordForm": "test_password",
                "fingerprint": "test_fingerprint",
                "sun": "yes",
            },
            timeout=1,
            requires_authentication=False,
        )

        # Assert that the session request method was called with the correct parameters
        session_mock.request.assert_called_once_with(
            "POST",
            f"{DEFAULT_API_HOST}/myhome/login.jsp",
            headers=None,
            params={
                "partner": "adt",
                "e": "ns",
                "usernameForm": "test_user",
                "passwordForm": "test_password",
                "fingerprint": "test_fingerprint",
                "sun": "yes",
            },
            data={
                "partner": "adt",
                "e": "ns",
                "usernameForm": "test_user",
                "passwordForm": "test_password",
                "fingerprint": "test_fingerprint",
                "sun": "yes",
            },
            timeout=1,
        )

        # Assert that the response status is 200
        assert result.status == 200

        # Assert that the make_soup function was called with the correct parameters
        make_soup_mock.assert_called_once_with(response_mock, mocker.ANY, mocker.ANY)

    # raises ClientConnectionError if async_fetch_version fails
    @pytest.mark.asyncio
    async def test_async_fetch_version_raises_error_fixed_fixed(self, mocker):
        # Mock the session object
        session_mock = mocker.Mock()
        session_mock.get.side_effect = ClientConnectionError()

        # Create an instance of ADTPulseConnection with the mocked session
        connection = ADTPulseConnection(DEFAULT_API_HOST, session=session_mock)

        # Call async_fetch_version
        await connection.async_fetch_version()

        # Assert that the api_version remains at its default value
        assert connection.api_version == ADT_DEFAULT_VERSION

    # Raises RuntimeError if async_query is called from sync context with a valid service host
    @pytest.mark.asyncio
    async def test_raises_runtime_error_if_async_query_called_from_sync_context_with_valid_service_host(
        self, mocker
    ):
        # Mock the check_sync method to return a loop
        mocker.patch.object(
            ADTPulseConnection, "check_sync", return_value=asyncio.get_event_loop()
        )

        # Create an instance of ADTPulseConnection with a valid service host
        connection = ADTPulseConnection(DEFAULT_API_HOST)

        # Call the async_query method from a sync context and assert that it raises a RuntimeError
        with pytest.raises(RuntimeError):
            connection.query("uri")

    # async_do_logout_query does not raise an error with a valid service host
    @pytest.mark.asyncio
    async def test_async_do_logout_query_with_valid_service_host(self, mocker):
        # Mock the async_query method to return None
        mocker.patch.object(ADTPulseConnection, "async_query", return_value=None)

        # Create an instance of ADTPulseConnection with a valid service host
        connection = ADTPulseConnection(DEFAULT_API_HOST)

        # Call async_do_logout_query and assert that it does not raise an error
        await connection.async_do_logout_query("site_id")

    # raises RuntimeError if loop is None with a valid service host
    @pytest.mark.asyncio
    async def test_raises_runtime_error_if_loop_is_none_with_valid_service_host(self):
        connection = ADTPulseConnection(DEFAULT_API_HOST)
        connection.loop = None

        with pytest.raises(RuntimeError):
            connection.check_sync("Test message")

    # can set and get allocated session
    @pytest.mark.asyncio
    async def test_set_and_get_allocated_session(self):
        host = DEFAULT_API_HOST
        connection = ADTPulseConnection(host)

        assert connection._allocated_session == True
        assert connection._session is not None

        session = connection._session
        connection._allocated_session = False

        assert connection._allocated_session == False
        assert connection._session == session

    # raises Exception if async_do_login_query fails
    @pytest.mark.asyncio
    async def test_async_do_login_query_failure(self, mocker):
        # Mock the async_query method to raise an exception
        mocker.patch.object(
            ADTPulseConnection,
            "async_query",
            side_effect=Exception("Async query failed"),
        )

        # Create an instance of ADTPulseConnection
        connection = ADTPulseConnection(DEFAULT_API_HOST)

        # Call async_do_login_query and assert that it returns None
        assert (
            await connection.async_do_login_query("username", "password", "fingerprint")
            is None
        )

    # can make url with given uri
    def test_make_url_with_given_uri(self):
        host = DEFAULT_API_HOST
        connection = ADTPulseConnection(host)

        uri = "/test"
        expected_url = f"{host}{API_PREFIX}{ADTPulseConnection._api_version}{uri}"

        assert connection.make_url(uri) == expected_url

    # can fetch api version with async mock (fixed)
    @pytest.mark.asyncio
    async def test_fetch_api_version_with_async_mock_fixed(self, mocker):
        host = DEFAULT_API_HOST
        connection = ADTPulseConnection(host)

        response_mock = mocker.AsyncMock()
        response_mock.real_url.path = "/myhome/v1/"
        mocker.patch.object(response_mock, "__aenter__", return_value=response_mock)

        session_mock = mocker.Mock()
        session_mock.get.return_value = response_mock
        connection._session = session_mock

        await connection.async_fetch_version()

        assert ADTPulseConnection._api_version == "24.0.0-117"

    # can do login query with valid credentials
    @pytest.mark.asyncio
    async def test_do_login_query_with_valid_credentials(self, mocker):
        host = DEFAULT_API_HOST
        connection = ADTPulseConnection(host)

        response_mock = mocker.MagicMock()
        response_mock.__enter__.return_value = response_mock

        session_mock = mocker.MagicMock()
        session_mock.request.return_value = response_mock
        connection._session = session_mock

        username = "me@example.com"
        password = "test_password"
        fingerprint = "test_fingerprint"

        await connection.async_do_login_query(username, password, fingerprint)

        assert connection.authenticated_flag.is_set()

    # raises RuntimeError if loop is None
    def test_raises_runtime_error_if_loop_is_none(self):
        connection = ADTPulseConnection(DEFAULT_API_HOST)
        connection.loop = None

        with pytest.raises(RuntimeError):
            connection.check_sync("Loop is None")

    # raises ClientResponseError if async_fetch_version fails
    @pytest.mark.asyncio
    async def test_raises_client_response_error_if_async_fetch_version_fails(
        self, mocker
    ):
        connection = ADTPulseConnection(DEFAULT_API_HOST)
        mocker.patch.object(connection, "async_query", side_effect=ClientResponseError)

        with pytest.raises(ClientResponseError):
            await connection.async_fetch_version()

    # raises RuntimeError if async_query is called from sync context
    @pytest.mark.asyncio
    async def test_raises_runtime_error_if_async_query_called_from_sync_context(self):
        connection = ADTPulseConnection(DEFAULT_API_HOST)

        with pytest.raises(RuntimeError):
            connection.query(ADT_ORB_URI)
