"""Common stuff for Fritz!Tools tests."""

from collections.abc import Generator
import logging
from unittest.mock import MagicMock, patch

from fritzconnection.core.processor import Service
from fritzconnection.lib.fritzhosts import FritzHosts
from fritzconnection.lib.fritzstatus import FritzStatus
from fritzconnection.lib.fritztools import ArgumentNamespace
import pytest

from homeassistant.components.fritz.coordinator import FritzConnectionCached

from .const import (
    MOCK_FB_SERVICES,
    MOCK_HOST_ATTRIBUTES_DATA,
    MOCK_MESH_DATA,
    MOCK_MODELNAME,
    MOCK_STATUS_AVM_DEVICE_LOG_DATA,
    MOCK_STATUS_CONNECTION_DATA,
    MOCK_STATUS_DEVICE_INFO_DATA,
)

LOGGER = logging.getLogger(__name__)


class FritzServiceMock(Service):
    """Service mocking."""

    def __init__(self, serviceId: str, actions: dict) -> None:
        """Init Service mock."""
        super().__init__()
        self._actions = actions
        self.serviceId = serviceId


class FritzConnectionMock:
    """FritzConnection mocking."""

    def __init__(self, services) -> None:
        """Init Mocking class."""
        self.modelname = MOCK_MODELNAME
        self.call_action = self._call_action
        self._services = services
        self.services = {
            srv: FritzServiceMock(serviceId=srv, actions=actions)
            for srv, actions in services.items()
        }
        LOGGER.debug("-" * 80)
        LOGGER.debug("FritzConnectionMock - services: %s", self.services)

    def call_action_side_effect(self, side_effect=None) -> None:
        """Set or unset a side_effect for call_action."""
        if side_effect is not None:
            self.call_action = MagicMock(side_effect=side_effect)
        else:
            self.call_action = self._call_action

    def override_services(self, services) -> None:
        """Overrire services data."""
        self._services = services

    def clear_cache(self) -> None:
        """Mock clear_cache method."""
        return FritzConnectionCached.clear_cache(self)

    def _call_action(self, service: str, action: str, **kwargs):
        LOGGER.debug(
            "_call_action service: %s, action: %s, **kwargs: %s",
            service,
            action,
            {**kwargs},
        )
        if ":" in service:
            service, number = service.split(":", 1)
            service = service + number
        elif not service[-1].isnumeric():
            service = service + "1"

        if kwargs:
            if (index := kwargs.get("NewIndex")) is None:
                index = next(iter(kwargs.values()))

            return self._services[service][action][index]
        return self._services[service][action]


@pytest.fixture(name="fc_data")
def fc_data_mock() -> dict[str, dict]:
    """Fixture for default fc_data."""
    return MOCK_FB_SERVICES


@pytest.fixture
def fc_class_mock(fc_data: dict[str, dict]) -> Generator[FritzConnectionMock]:
    """Fixture that sets up a mocked FritzConnection class."""
    with patch(
        "homeassistant.components.fritz.coordinator.FritzConnectionCached",
        autospec=True,
    ) as result:
        result.return_value = FritzConnectionMock(fc_data)
        yield result


@pytest.fixture
def fh_class_mock() -> Generator[type[FritzHosts]]:
    """Fixture that sets up a mocked FritzHosts class."""
    with (
        patch(
            "homeassistant.components.fritz.coordinator.FritzHosts",
            new=FritzHosts,
        ) as result,
        patch.object(
            FritzHosts,
            "get_mesh_topology",
            MagicMock(return_value=MOCK_MESH_DATA),
        ),
        patch.object(
            FritzHosts,
            "get_hosts_attributes",
            MagicMock(return_value=MOCK_HOST_ATTRIBUTES_DATA),
        ),
    ):
        yield result


@pytest.fixture
def fs_class_mock() -> Generator[type[FritzStatus]]:
    """Fixture that sets up a mocked FritzStatus class."""
    with (
        patch(
            "homeassistant.components.fritz.coordinator.FritzStatus",
            new=FritzStatus,
        ) as result,
        patch.object(
            FritzStatus,
            "get_default_connection_service",
            MagicMock(return_value=MOCK_STATUS_CONNECTION_DATA),
        ),
        patch.object(
            FritzStatus,
            "get_device_info",
            MagicMock(return_value=ArgumentNamespace(MOCK_STATUS_DEVICE_INFO_DATA)),
        ),
        patch.object(FritzStatus, "get_monitor_data", MagicMock(return_value={})),
        patch.object(
            FritzStatus, "get_cpu_temperatures", MagicMock(return_value=[42, 38])
        ),
        patch.object(
            FritzStatus,
            "get_avm_device_log",
            MagicMock(return_value=MOCK_STATUS_AVM_DEVICE_LOG_DATA),
        ),
        patch.object(FritzStatus, "has_wan_enabled", True),
    ):
        yield result
