"""Binary sensor platform for irrigation_unlimited."""

from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.entity_platform import (
    EntityPlatform,
    current_platform,
    async_get_platforms,
)
from homeassistant.util import dt

from .irrigation_unlimited import IUCoordinator
from .entity import IUEntity
from .service import register_platform_services
from .const import (
    ATTR_ENABLED,
    ATTR_SEQUENCE_STATUS,
    ATTR_STATUS,
    ATTR_INDEX,
    ATTR_CURRENT_SCHEDULE,
    ATTR_CURRENT_NAME,
    ATTR_CURRENT_ADJUSTMENT,
    ATTR_CURRENT_START,
    ATTR_CURRENT_DURATION,
    ATTR_NEXT_SCHEDULE,
    ATTR_NEXT_ZONE,
    ATTR_NEXT_NAME,
    ATTR_NEXT_ADJUSTMENT,
    ATTR_NEXT_START,
    ATTR_NEXT_DURATION,
    ATTR_TIME_REMAINING,
    ATTR_PERCENT_COMPLETE,
    ATTR_ZONE_COUNT,
    ATTR_CURRENT_ZONE,
    ATTR_TOTAL_TODAY,
    ATTR_SCHEDULE_COUNT,
    ATTR_ADJUSTMENT,
    ATTR_CONFIGURATION,
    ATTR_TIMELINE,
    ATTR_SUSPENDED,
    BINARY_SENSOR,
    DOMAIN,
    COORDINATOR,
    CONF_SCHEDULES,
    CONF_ZONE_ID,
    RES_MANUAL,
    RES_NOT_RUNNING,
    RES_NONE,
    ATTR_VOLUME,
    ATTR_FLOW_RATE,
    ATTR_SEQUENCE_COUNT,
    ATTR_ZONES,
)


def find_platform(hass: HomeAssistant, name: str) -> EntityPlatform:
    """Find a platform in our domain"""
    platforms = async_get_platforms(hass, DOMAIN)
    for platform in platforms:
        if platform.domain == name:
            return platform
    return None


async def async_setup_platform(
    hass, config, async_add_entities, discovery_info=None
) -> None:
    """Setup binary_sensor platform."""
    # pylint: disable=unused-argument

    coordinator: IUCoordinator = hass.data[DOMAIN][COORDINATOR]
    entities = []
    for controller in coordinator.controllers:
        entities.append(IUMasterEntity(coordinator, controller, None, None))
        for zone in controller.zones:
            entities.append(IUZoneEntity(coordinator, controller, zone, None))
        for sequence in controller.sequences:
            entities.append(IUSequenceEntity(coordinator, controller, None, sequence))
    async_add_entities(entities)

    platform = current_platform.get()
    register_platform_services(platform)

    return


async def async_reload_platform(
    component: EntityComponent, coordinator: IUCoordinator
) -> bool:
    """Handle the reloading of this platform"""

    def remove_entity(entities: "dict[Entity]", entity_id: str) -> bool:
        entity_id = f"{BINARY_SENSOR}.{DOMAIN}_{entity_id}"
        if entity_id in entities:
            entities.pop(entity_id)
            return True
        return False

    platform: EntityPlatform = find_platform(component.hass, BINARY_SENSOR)
    if platform is None:
        return False

    old_entities: dict[Entity] = platform.entities.copy()
    new_entities: list[Entity] = []

    for controller in coordinator.controllers:
        if not remove_entity(old_entities, controller.unique_id):
            new_entities.append(IUMasterEntity(coordinator, controller, None, None))
        for zone in controller.zones:
            if not remove_entity(old_entities, zone.unique_id):
                new_entities.append(IUZoneEntity(coordinator, controller, zone, None))
        for sequence in controller.sequences:
            if not remove_entity(old_entities, sequence.unique_id):
                new_entities.append(
                    IUSequenceEntity(coordinator, controller, None, sequence)
                )
    if len(new_entities) > 0:
        await platform.async_add_entities(new_entities)
        coordinator.initialise()
    for entity in old_entities:
        await platform.async_remove_entity(entity)

    return True


class IUMasterEntity(IUEntity):
    """irrigation_unlimited controller binary_sensor class."""

    @property
    def unique_id(self):
        """Return a unique ID."""
        return self._controller.unique_id

    @property
    def name(self):
        """Return the friendly name of the binary_sensor."""
        return self._controller.name

    @property
    def is_on(self):
        """Return true if the binary_sensor is on."""
        return self._controller.is_on

    @property
    def should_poll(self):
        """Indicate that we need to poll data"""
        return False

    @property
    def icon(self):
        """Return the icon to use in the frontend."""
        return self._controller.icon

    @property
    def extra_state_attributes(self):
        """Return the state attributes of the device."""
        attr = {}
        attr[ATTR_INDEX] = self._controller.index
        attr[ATTR_ENABLED] = self._controller.enabled
        attr[ATTR_SUSPENDED] = self._controller.suspended
        attr[ATTR_STATUS] = self._controller.status
        attr[ATTR_ZONE_COUNT] = len(self._controller.zones)
        attr[ATTR_SEQUENCE_COUNT] = len(self._controller.sequences)
        attr[ATTR_ZONES] = ""
        attr[ATTR_SEQUENCE_STATUS] = self._controller.sequence_status()
        current = self._controller.runs.current_run
        if current is not None:
            attr[ATTR_CURRENT_ZONE] = current.zone.index + 1
            attr[ATTR_CURRENT_NAME] = current.zone.name
            attr[ATTR_CURRENT_START] = dt.as_local(current.start_time)
            attr[ATTR_CURRENT_DURATION] = str(current.duration)
            attr[ATTR_TIME_REMAINING] = str(current.time_remaining)
            attr[ATTR_PERCENT_COMPLETE] = current.percent_complete
        else:
            attr[ATTR_CURRENT_SCHEDULE] = "deprecated (use current_zone)"
            attr[ATTR_CURRENT_ZONE] = RES_NOT_RUNNING
            attr[ATTR_PERCENT_COMPLETE] = 0

        next_run = self._controller.runs.next_run
        if next_run is not None:
            attr[ATTR_NEXT_ZONE] = next_run.zone.index + 1
            attr[ATTR_NEXT_NAME] = next_run.zone.name
            attr[ATTR_NEXT_START] = dt.as_local(next_run.start_time)
            attr[ATTR_NEXT_DURATION] = str(next_run.duration)
        else:
            attr[ATTR_NEXT_SCHEDULE] = "deprecated (use next_zone)"
            attr[ATTR_NEXT_ZONE] = RES_NONE
        attr[ATTR_VOLUME] = self._controller.volume.total
        attr[ATTR_FLOW_RATE] = self._controller.volume.flow_rate
        attr |= self._controller.user
        return attr


class IUZoneEntity(IUEntity):
    """irrigation_unlimited zone binary_sensor class."""

    @property
    def unique_id(self):
        """Return a unique ID."""
        return self._zone.unique_id

    @property
    def name(self):
        """Return the friendly name of the binary_sensor."""
        return self._zone.name

    @property
    def is_on(self):
        """Return true if the binary_sensor is on."""
        return self._zone.is_on

    @property
    def should_poll(self):
        """Indicate that we need to poll data"""
        return False

    @property
    def icon(self):
        """Return the icon to use in the frontend."""
        return self._zone.icon

    @property
    def extra_state_attributes(self):
        """Return the state attributes of the device."""
        # pylint: disable=too-many-branches

        attr = {}
        attr[CONF_ZONE_ID] = self._zone.zone_id
        attr[ATTR_INDEX] = self._zone.index
        attr[ATTR_ENABLED] = self._zone.enabled
        attr[ATTR_SUSPENDED] = self._zone.suspended
        attr[ATTR_STATUS] = self._zone.status
        attr[ATTR_SCHEDULE_COUNT] = len(self._zone.schedules)
        attr[CONF_SCHEDULES] = ""
        attr[ATTR_ADJUSTMENT] = str(self._zone.adjustment)
        current = self._zone.runs.current_run
        if current is not None:
            attr[ATTR_CURRENT_ADJUSTMENT] = current.adjustment
            if current.schedule is not None:
                attr[ATTR_CURRENT_SCHEDULE] = current.schedule.index + 1
                attr[ATTR_CURRENT_NAME] = current.schedule.name
            else:
                attr[ATTR_CURRENT_SCHEDULE] = 0
                attr[ATTR_CURRENT_NAME] = RES_MANUAL
            attr[ATTR_CURRENT_START] = dt.as_local(current.start_time)
            attr[ATTR_CURRENT_DURATION] = str(current.duration)
            attr[ATTR_TIME_REMAINING] = str(current.time_remaining)
            attr[ATTR_PERCENT_COMPLETE] = current.percent_complete
        else:
            attr[ATTR_CURRENT_SCHEDULE] = None
            attr[ATTR_PERCENT_COMPLETE] = 0

        next_run = self._zone.runs.next_run
        if next_run is not None:
            attr[ATTR_NEXT_ADJUSTMENT] = next_run.adjustment
            if next_run.schedule is not None:
                attr[ATTR_NEXT_SCHEDULE] = next_run.schedule.index + 1
                attr[ATTR_NEXT_NAME] = next_run.schedule.name
            else:
                attr[ATTR_NEXT_SCHEDULE] = 0
                attr[ATTR_NEXT_NAME] = RES_MANUAL
            attr[ATTR_NEXT_START] = dt.as_local(next_run.start_time)
            attr[ATTR_NEXT_DURATION] = str(next_run.duration)
        else:
            attr[ATTR_NEXT_SCHEDULE] = None
        attr[ATTR_TOTAL_TODAY] = round(
            self._zone.today_total.total_seconds() / 60,
            1,
        )
        if self._zone.show_config:
            attr[ATTR_CONFIGURATION] = self._zone.configuration
        if self._zone.show_timeline:
            attr[ATTR_TIMELINE] = self._zone.timeline()
        attr[ATTR_VOLUME] = self._zone.volume.total
        attr[ATTR_FLOW_RATE] = self._zone.volume.flow_rate
        attr |= self._zone.user
        return attr


class IUSequenceEntity(IUEntity):
    """irrigation_unlimited sequence binary_sensor class."""

    @property
    def unique_id(self):
        """Return a unique ID."""
        return self._sequence.unique_id

    @property
    def name(self):
        """Return the friendly name of the binary_sensor."""
        return self._sequence.name

    @property
    def is_on(self):
        """Return true if the binary_sensor is on."""
        return self._sequence.is_on

    @property
    def should_poll(self):
        """Indicate that we need to poll data"""
        return False

    @property
    def icon(self):
        """Return the icon to use in the frontend."""
        return self._sequence.icon

    @property
    def extra_state_attributes(self):
        """Return the state attributes of the device."""
        attr = {}
        attr[ATTR_INDEX] = self._sequence.index
        attr[ATTR_ENABLED] = self._sequence.enabled
        attr[ATTR_SUSPENDED] = (
            dt.as_local(self._sequence.suspended) if self._sequence.suspended else None
        )
        attr[ATTR_STATUS] = self._sequence.status
        attr[ATTR_ZONE_COUNT] = len(self._sequence.zones)
        attr[ATTR_SCHEDULE_COUNT] = len(self._sequence.schedules)
        attr[ATTR_ADJUSTMENT] = str(self._sequence.adjustment)
        attr[ATTR_VOLUME] = self._sequence.volume
        if (current := self._sequence.runs.current_run) is not None:
            if current.active_zone is not None:
                attr[ATTR_CURRENT_ZONE] = current.active_zone.sequence_zone.id1
            else:
                attr[ATTR_CURRENT_ZONE] = None
            attr[ATTR_CURRENT_START] = dt.as_local(current.start_time)
            attr[ATTR_CURRENT_DURATION] = str(current.total_time)
            attr[ATTR_TIME_REMAINING] = str(current.time_remaining)
            attr[ATTR_PERCENT_COMPLETE] = current.percent_complete
            if current.schedule is not None:
                attr[ATTR_CURRENT_SCHEDULE] = current.schedule.id1
                attr[ATTR_CURRENT_NAME] = current.schedule.name
            else:
                attr[ATTR_CURRENT_SCHEDULE] = 0
                attr[ATTR_CURRENT_NAME] = RES_MANUAL
        else:
            attr[ATTR_CURRENT_ZONE] = None
            attr[ATTR_CURRENT_SCHEDULE] = None
            attr[ATTR_PERCENT_COMPLETE] = 0
        if (next_run := self._sequence.runs.next_run) is not None:
            attr[ATTR_NEXT_START] = dt.as_local(next_run.start_time)
            attr[ATTR_NEXT_DURATION] = str(next_run.total_time)
            if next_run.schedule is not None:
                attr[ATTR_NEXT_SCHEDULE] = next_run.schedule.id1
                attr[ATTR_NEXT_NAME] = next_run.schedule.name
            else:
                attr[ATTR_NEXT_SCHEDULE] = 0
                attr[ATTR_NEXT_NAME] = RES_MANUAL
        else:
            attr[ATTR_NEXT_SCHEDULE] = None
        attr[ATTR_ZONES] = self._sequence.ha_zone_attr()
        return attr
