"""Class representing Sonos alarms."""
from __future__ import annotations

from collections.abc import Iterator
import logging
from typing import Any

from soco import SoCo
from soco.alarms import Alarm, get_alarms
from soco.exceptions import SoCoException

from homeassistant.helpers.dispatcher import async_dispatcher_send

from .const import DATA_SONOS, SONOS_ALARMS_UPDATED, SONOS_CREATE_ALARM
from .household_coordinator import SonosHouseholdCoordinator

_LOGGER = logging.getLogger(__name__)


class SonosAlarms(SonosHouseholdCoordinator):
    """Coordinator class for Sonos alarms."""

    def __init__(self, *args: Any) -> None:
        """Initialize the data."""
        super().__init__(*args)
        self._alarms: dict[str, Alarm] = {}

    def __iter__(self) -> Iterator:
        """Return an iterator for the known alarms."""
        alarms = list(self._alarms.values())
        return iter(alarms)

    def get(self, alarm_id: str) -> Alarm | None:
        """Get an Alarm instance."""
        return self._alarms.get(alarm_id)

    async def async_update_entities(self, soco: SoCo) -> bool:
        """Create and update alarms entities, return success."""
        try:
            new_alarms = await self.hass.async_add_executor_job(self.update_cache, soco)
        except (OSError, SoCoException) as err:
            _LOGGER.error("Could not refresh alarms using %s: %s", soco, err)
            return False

        for alarm in new_alarms:
            speaker = self.hass.data[DATA_SONOS].discovered[alarm.zone.uid]
            async_dispatcher_send(
                self.hass, SONOS_CREATE_ALARM, speaker, [alarm.alarm_id]
            )
        async_dispatcher_send(self.hass, f"{SONOS_ALARMS_UPDATED}-{self.household_id}")
        return True

    def update_cache(self, soco: SoCo) -> set[Alarm]:
        """Populate cache of known alarms.

        Prune deleted alarms and return new alarms.
        """
        soco_alarms = get_alarms(soco)
        new_alarms = set()

        for alarm in soco_alarms:
            if alarm.alarm_id not in self._alarms:
                new_alarms.add(alarm)
                self._alarms[alarm.alarm_id] = alarm

        for alarm_id, alarm in list(self._alarms.items()):
            if alarm not in soco_alarms:
                self._alarms.pop(alarm_id)

        return new_alarms
