"""Support for iBeacon device sensors."""
from __future__ import annotations

from abc import abstractmethod

from ibeacon_ble import iBeaconAdvertisement

from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import DeviceInfo, Entity

from .const import ATTR_MAJOR, ATTR_MINOR, ATTR_SOURCE, ATTR_UUID, DOMAIN
from .coordinator import IBeaconCoordinator, signal_seen, signal_unavailable


class IBeaconEntity(Entity):
    """An iBeacon entity."""

    _attr_should_poll = False
    _attr_has_entity_name = True

    def __init__(
        self,
        coordinator: IBeaconCoordinator,
        identifier: str,
        device_unique_id: str,
        ibeacon_advertisement: iBeaconAdvertisement,
    ) -> None:
        """Initialize an iBeacon sensor entity."""
        self._device_unique_id = device_unique_id
        self._coordinator = coordinator
        self._ibeacon_advertisement = ibeacon_advertisement
        self._attr_device_info = DeviceInfo(
            name=identifier,
            identifiers={(DOMAIN, device_unique_id)},
        )

    @property
    def extra_state_attributes(
        self,
    ) -> dict[str, str | int]:
        """Return the device state attributes."""
        ibeacon_advertisement = self._ibeacon_advertisement
        return {
            ATTR_UUID: str(ibeacon_advertisement.uuid),
            ATTR_MAJOR: ibeacon_advertisement.major,
            ATTR_MINOR: ibeacon_advertisement.minor,
            ATTR_SOURCE: ibeacon_advertisement.source,
        }

    @abstractmethod
    @callback
    def _async_seen(
        self,
        ibeacon_advertisement: iBeaconAdvertisement,
    ) -> None:
        """Update state."""

    @abstractmethod
    @callback
    def _async_unavailable(self) -> None:
        """Set unavailable."""

    async def async_added_to_hass(self) -> None:
        """Register state update callbacks."""
        await super().async_added_to_hass()
        self.async_on_remove(
            async_dispatcher_connect(
                self.hass,
                signal_seen(self._device_unique_id),
                self._async_seen,
            )
        )
        self.async_on_remove(
            async_dispatcher_connect(
                self.hass,
                signal_unavailable(self._device_unique_id),
                self._async_unavailable,
            )
        )
