"""Unit test for XKNX Module."""
import os
from unittest.mock import AsyncMock, patch

import pytest

from xknx import XKNX
from xknx.exceptions import CommunicationError
from xknx.io import ConnectionConfig, ConnectionType


class TestXknxModule:
    """Test class for XKNX."""

    def test_log_to_file(self):
        """Test logging enable."""
        XKNX(log_directory="/tmp/")
        assert os.path.isfile("/tmp/xknx.log")
        os.remove("/tmp/xknx.log")

    def test_log_to_file_when_dir_does_not_exist(self):
        """Test logging enable with non existent directory."""
        XKNX(log_directory="/xknx/is/fun")
        assert not os.path.isfile("/xknx/is/fun/xknx.log")

    def test_register_telegram_cb(self):
        """Test register telegram callback."""
        xknx = XKNX(telegram_received_cb=AsyncMock())
        assert len(xknx.telegram_queue.telegram_received_cbs) == 1

    def test_register_device_cb(self):
        """Test register telegram callback."""
        xknx = XKNX(device_updated_cb=AsyncMock())
        assert len(xknx.devices.device_updated_cbs) == 1

    def test_register_connection_state_change_cb(self):
        """Test register con state callback."""
        xknx = XKNX(connection_state_changed_cb=AsyncMock())
        assert len(xknx.connection_manager._connection_state_changed_cbs) == 1

    @patch("xknx.io.KNXIPInterface._start", new_callable=AsyncMock)
    async def test_xknx_start(self, start_mock):
        """Test xknx start."""
        xknx = XKNX(state_updater=True)

        await xknx.start()
        start_mock.assert_called_once()
        await xknx.stop()

    @patch("xknx.io.KNXIPInterface._start", new_callable=AsyncMock)
    async def test_xknx_start_as_context_manager(self, ipinterface_mock):
        """Test xknx start."""
        async with XKNX(state_updater=True) as xknx:
            assert xknx.started.is_set()
            ipinterface_mock.assert_called_once()

    @patch("xknx.io.KNXIPInterface._start", new_callable=AsyncMock)
    async def test_xknx_start_and_stop_with_dedicated_connection_config(
        self, start_mock
    ):
        """Test xknx start and stop with connection config."""
        xknx = XKNX()

        connection_config = ConnectionConfig(connection_type=ConnectionType.TUNNELING)
        xknx.connection_config = connection_config

        await xknx.start()

        start_mock.assert_called_once()
        assert xknx.knxip_interface.connection_config == connection_config

        await xknx.stop()
        assert xknx.knxip_interface is None
        assert xknx.telegram_queue._consumer_task.done()
        assert not xknx.state_updater.started

    @pytest.mark.parametrize(
        "connection_config",
        [
            ConnectionConfig(
                connection_type=ConnectionType.ROUTING, local_ip="127.0.0.1"
            ),
            ConnectionConfig(
                connection_type=ConnectionType.TUNNELING, gateway_ip="127.0.0.2"
            ),
        ],
    )
    @patch(
        "xknx.io.transport.UDPTransport.connect",
        new_callable=AsyncMock,
        side_effect=OSError,
    )
    async def test_xknx_start_initial_connection_error(
        self, transport_connect_mock, connection_config
    ):
        """Test xknx start raising when socket can't be set up."""
        xknx = XKNX(
            state_updater=True,
            connection_config=connection_config,
        )
        with pytest.raises(CommunicationError):
            await xknx.start()
        transport_connect_mock.assert_called_once()
        assert xknx.telegram_queue._consumer_task is None  # not started
        assert not xknx.state_updater.started
        assert not xknx.started.is_set()
