"""Base class for IKEA TRADFRI."""

from abc import abstractmethod
from collections.abc import Callable, Coroutine
from functools import wraps
from typing import Any, cast, override

from pytradfri.command import Command
from pytradfri.device import Device
from pytradfri.error import RequestError

from homeassistant.core import callback
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN, LOGGER
from .coordinator import TradfriDeviceDataUpdateCoordinator


def handle_error(
    func: Callable[[Command | list[Command]], Any],
) -> Callable[[Command | list[Command]], Coroutine[Any, Any, None]]:
    """Handle tradfri api call error."""

    @wraps(func)
    async def wrapper(command: Command | list[Command]) -> None:
        """Decorate api call."""
        try:
            await func(command)
        except RequestError as err:
            LOGGER.error("Unable to execute command %s: %s", command, err)

    return wrapper


class TradfriBaseEntity(CoordinatorEntity[TradfriDeviceDataUpdateCoordinator]):
    """Base Tradfri device."""

    _attr_has_entity_name = True

    def __init__(
        self,
        device_coordinator: TradfriDeviceDataUpdateCoordinator,
        gateway_id: str,
        api: Callable[[Command | list[Command]], Any],
    ) -> None:
        """Initialize a device."""
        super().__init__(device_coordinator)

        self._gateway_id = gateway_id

        self._device: Device = device_coordinator.data

        self._device_id = self._device.id
        self._api = handle_error(api)

        info = self._device.device_info
        self._attr_device_info = DeviceInfo(
            identifiers={(DOMAIN, f"{gateway_id}-{self._device_id}")},
            manufacturer=info.manufacturer,
            model=info.model_number,
            name=self._device.name,
            sw_version=info.firmware_version,
            via_device_id=dr.async_get_device_id_by_identifier(
                device_coordinator.hass,
                (DOMAIN, gateway_id),
                config_entry_id=device_coordinator.config_entry.entry_id,
            ),
        )
        self._attr_unique_id = f"{gateway_id}-{self._device_id}"

    @abstractmethod
    @callback
    def _refresh(self) -> None:
        """Refresh device data."""

    @callback
    @override
    def _handle_coordinator_update(self) -> None:
        """Handle updated data from the coordinator.

        Tests fails without this method.
        """
        self._refresh()
        super()._handle_coordinator_update()

    @property
    @override
    def available(self) -> bool:
        """Return if entity is available."""
        return cast(bool, self._device.reachable) and super().available
