""" Implements the VersatileThermostat binary sensors component """
# pylint: disable=unused-argument, line-too-long

import logging
from vtherm_api.log_collector import get_vtherm_logger

from homeassistant.core import (
    HomeAssistant,
    callback,
    Event,
    # CoreState,
)

from homeassistant.const import STATE_ON, STATE_OFF

from homeassistant.helpers.device_registry import DeviceInfo

from homeassistant.components.binary_sensor import (
    BinarySensorEntity,
    BinarySensorDeviceClass,
)
from homeassistant.config_entries import ConfigEntry

from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .vtherm_central_api import VersatileThermostatAPI
from .base_entity import VersatileThermostatBaseEntity
from .const import (
    DOMAIN,
    DEVICE_MANUFACTURER,
    CONF_NAME,
    CONF_USE_POWER_FEATURE,
    CONF_USE_PRESENCE_FEATURE,
    CONF_USE_MOTION_FEATURE,
    CONF_USE_WINDOW_FEATURE,
    CONF_USE_HEATING_FAILURE_DETECTION_FEATURE,
    CONF_THERMOSTAT_TYPE,
    CONF_THERMOSTAT_CENTRAL_CONFIG,
    CONF_USE_CENTRAL_BOILER_FEATURE,
    overrides,
    EventType,
)

_LOGGER = get_vtherm_logger(__name__)


async def async_setup_entry(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Set up the VersatileThermostat binary sensors with config flow."""
    unique_id = entry.entry_id
    name = entry.data.get(CONF_NAME)
    _LOGGER.debug("%s - Calling async_setup_entry entry=%s, data=%s", name, entry.entry_id, entry.data)

    vt_type = entry.data.get(CONF_THERMOSTAT_TYPE)

    entities = None

    if vt_type == CONF_THERMOSTAT_CENTRAL_CONFIG:
        if entry.data.get(CONF_USE_CENTRAL_BOILER_FEATURE):
            # we capture here the configuration for central boiler feature
            api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(hass)
            api.central_boiler_manager.post_init(entry.data)
            entities = [
                CentralBoilerBinarySensor(hass, unique_id, name, entry.data),
            ]
    else:
        entities = [
            SafetyBinarySensor(hass, unique_id, name, entry.data),
            WindowByPassBinarySensor(hass, unique_id, name, entry.data),
        ]
        if entry.data.get(CONF_USE_MOTION_FEATURE):
            entities.append(MotionBinarySensor(hass, unique_id, name, entry.data))
        if entry.data.get(CONF_USE_WINDOW_FEATURE):
            entities.append(WindowBinarySensor(hass, unique_id, name, entry.data))
        if entry.data.get(CONF_USE_PRESENCE_FEATURE):
            entities.append(PresenceBinarySensor(hass, unique_id, name, entry.data))
        if entry.data.get(CONF_USE_POWER_FEATURE):
            entities.append(OverpoweringBinarySensor(hass, unique_id, name, entry.data))
        if entry.data.get(CONF_USE_HEATING_FAILURE_DETECTION_FEATURE):
            entities.append(HeatingFailureBinarySensor(hass, unique_id, name, entry.data))

    if entities:
        async_add_entities(entities, True)


class SafetyBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
    """Representation of a BinarySensor which exposes the security state"""

    _attr_has_entity_name = True
    _attr_translation_key = "safety_state"

    def __init__(
        self,
        hass: HomeAssistant,
        unique_id,
        name,  # pylint: disable=unused-argument
        entry_infos,
    ) -> None:
        """Initialize the SafetyState Binary sensor"""
        super().__init__(hass, unique_id, name)
        self._attr_unique_id = f"{self._device_name}_safety_state"
        self._attr_is_on = False

    @callback
    async def async_my_climate_changed(self, event: Event = None):
        """Called when my climate have change"""
        # _LOGGER.debug("%s - climate state change", self._attr_unique_id)

        old_state = self._attr_is_on
        self._attr_is_on = self.my_climate.safety_manager.is_safety_detected
        if old_state != self._attr_is_on:
            self.async_write_ha_state()
        return

    @property
    def device_class(self) -> BinarySensorDeviceClass | None:
        return BinarySensorDeviceClass.SAFETY

    @property
    def icon(self) -> str | None:
        if self._attr_is_on:
            return "mdi:shield-alert"
        else:
            return "mdi:shield-check-outline"


class HeatingFailureBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
    """Representation of a BinarySensor which exposes the heating failure state"""

    _attr_has_entity_name = True
    _attr_translation_key = "heating_failure_state"

    def __init__(
        self,
        hass: HomeAssistant,
        unique_id,
        name,  # pylint: disable=unused-argument
        entry_infos,
    ) -> None:
        """Initialize the HeatingFailure Binary sensor"""
        super().__init__(hass, unique_id, name)
        self._attr_unique_id = f"{self._device_name}_heating_failure_state"
        self._attr_is_on = False

    @callback
    async def async_my_climate_changed(self, event: Event = None):
        """Called when my climate have change"""

        old_state = self._attr_is_on
        self._attr_is_on = self.my_climate.heating_failure_detection_manager.is_failure_detected
        if old_state != self._attr_is_on:
            self.async_write_ha_state()
        return

    @property
    def device_class(self) -> BinarySensorDeviceClass | None:
        return BinarySensorDeviceClass.PROBLEM

    @property
    def icon(self) -> str | None:
        if self._attr_is_on:
            return "mdi:radiator-off"
        else:
            return "mdi:radiator"


class OverpoweringBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
    """Representation of a BinarySensor which exposes the overpowering state"""

    _attr_has_entity_name = True
    _attr_translation_key = "overpowering_state"

    def __init__(
        self,
        hass: HomeAssistant,
        unique_id,
        name,  # pylint: disable=unused-argument
        entry_infos,
    ) -> None:
        """Initialize the OverpoweringState Binary sensor"""
        super().__init__(hass, unique_id, entry_infos.get(CONF_NAME))
        self._attr_unique_id = f"{self._device_name}_overpowering_state"
        self._attr_is_on = False

    @callback
    async def async_my_climate_changed(self, event: Event = None):
        """Called when my climate have change"""
        # _LOGGER.debug("%s - climate state change", self._attr_unique_id)

        old_state = self._attr_is_on
        self._attr_is_on = self.my_climate.overpowering_state is STATE_ON
        if old_state != self._attr_is_on:
            self.async_write_ha_state()
        return

    @property
    def device_class(self) -> BinarySensorDeviceClass | None:
        return BinarySensorDeviceClass.POWER

    @property
    def icon(self) -> str | None:
        if self._attr_is_on:
            return "mdi:flash-alert-outline"
        else:
            return "mdi:flash-outline"


class WindowBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
    """Representation of a BinarySensor which exposes the window state"""

    _attr_has_entity_name = True
    _attr_translation_key = "window_state"

    def __init__(
        self,
        hass: HomeAssistant,
        unique_id,
        name,  # pylint: disable=unused-argument
        entry_infos,
    ) -> None:
        """Initialize the WindowState Binary sensor"""
        super().__init__(hass, unique_id, entry_infos.get(CONF_NAME))
        self._attr_unique_id = f"{self._device_name}_window_state"
        self._attr_is_on = False

    @callback
    async def async_my_climate_changed(self, event: Event = None):
        """Called when my climate have change"""
        # _LOGGER.debug("%s - climate state change", self._attr_unique_id)

        old_state = self._attr_is_on
        # Issue 120 - only take defined presence value
        if self.my_climate.window_state in [
            STATE_ON,
            STATE_OFF,
        ] or self.my_climate.window_auto_state in [STATE_ON, STATE_OFF]:
            self._attr_is_on = (
                self.my_climate.window_state == STATE_ON
                or self.my_climate.window_auto_state == STATE_ON
            )
            if old_state != self._attr_is_on:
                self.async_write_ha_state()
        return

    @property
    def device_class(self) -> BinarySensorDeviceClass | None:
        return BinarySensorDeviceClass.WINDOW

    @property
    def icon(self) -> str | None:
        if self._attr_is_on:
            if self.my_climate.window_state == STATE_ON:
                return "mdi:window-open-variant"
            else:
                return "mdi:window-open"
        else:
            return "mdi:window-closed-variant"


class MotionBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
    """Representation of a BinarySensor which exposes the motion state"""

    _attr_has_entity_name = True
    _attr_translation_key = "motion_state"

    def __init__(
        self,
        hass: HomeAssistant,
        unique_id,
        name,  # pylint: disable=unused-argument
        entry_infos,
    ) -> None:
        """Initialize the MotionState Binary sensor"""
        super().__init__(hass, unique_id, entry_infos.get(CONF_NAME))
        self._attr_unique_id = f"{self._device_name}_motion_state"
        self._attr_is_on = False

    @callback
    async def async_my_climate_changed(self, event: Event = None):
        """Called when my climate have change"""
        # _LOGGER.debug("%s - climate state change", self._attr_unique_id)
        old_state = self._attr_is_on
        # Issue 120 - only take defined presence value
        if self.my_climate.motion_state in [STATE_ON, STATE_OFF]:
            self._attr_is_on = self.my_climate.motion_state == STATE_ON
            if old_state != self._attr_is_on:
                self.async_write_ha_state()
        return

    @property
    def device_class(self) -> BinarySensorDeviceClass | None:
        return BinarySensorDeviceClass.MOTION

    @property
    def icon(self) -> str | None:
        if self._attr_is_on:
            return "mdi:motion-sensor"
        else:
            return "mdi:motion-sensor-off"


class PresenceBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
    """Representation of a BinarySensor which exposes the presence state"""

    _attr_has_entity_name = True
    _attr_translation_key = "presence_state"

    def __init__(
        self,
        hass: HomeAssistant,
        unique_id,
        name,  # pylint: disable=unused-argument
        entry_infos,
    ) -> None:
        """Initialize the PresenceState Binary sensor"""
        super().__init__(hass, unique_id, entry_infos.get(CONF_NAME))
        self._attr_unique_id = f"{self._device_name}_presence_state"
        self._attr_is_on = False

    @callback
    async def async_my_climate_changed(self, event: Event = None):
        """Called when my climate have change"""

        # _LOGGER.debug("%s - climate state change", self._attr_unique_id)
        old_state = self._attr_is_on
        # Issue 120 - only take defined presence value
        if self.my_climate.presence_state in [STATE_ON, STATE_OFF]:
            self._attr_is_on = self.my_climate.presence_state == STATE_ON
            if old_state != self._attr_is_on:
                self.async_write_ha_state()
        return

    @property
    def device_class(self) -> BinarySensorDeviceClass | None:
        return BinarySensorDeviceClass.PRESENCE

    @property
    def icon(self) -> str | None:
        if self._attr_is_on:
            return "mdi:home-account"
        else:
            return "mdi:nature-people"


class WindowByPassBinarySensor(VersatileThermostatBaseEntity, BinarySensorEntity):
    """Representation of a BinarySensor which exposes the Window ByPass state"""

    _attr_has_entity_name = True
    _attr_translation_key = "window_bypass_state"

    def __init__(
        self,
        hass: HomeAssistant,
        unique_id,
        name,  # pylint: disable=unused-argument
        entry_infos,
    ) -> None:
        """Initialize the WindowByPass Binary sensor"""
        super().__init__(hass, unique_id, entry_infos.get(CONF_NAME))
        self._attr_unique_id = f"{self._device_name}_window_bypass_state"
        self._attr_is_on = False

    @callback
    async def async_my_climate_changed(self, event: Event = None):
        """Called when my climate have change"""
        # _LOGGER.debug("%s - climate state change", self._attr_unique_id)
        old_state = self._attr_is_on
        if self.my_climate.is_window_bypass in [True, False]:
            self._attr_is_on = self.my_climate.is_window_bypass
            if old_state != self._attr_is_on:
                self.async_write_ha_state()
        return

    @property
    def device_class(self) -> BinarySensorDeviceClass | None:
        return BinarySensorDeviceClass.RUNNING

    @property
    def icon(self) -> str | None:
        if self._attr_is_on:
            return "mdi:window-shutter-cog"
        else:
            return "mdi:window-shutter-auto"


class CentralBoilerBinarySensor(BinarySensorEntity):
    """Representation of a BinarySensor which exposes the Central Boiler state"""

    _attr_has_entity_name = True
    _attr_translation_key = "central_boiler_state"

    _entity_component_unrecorded_attributes = BinarySensorEntity._entity_component_unrecorded_attributes.union(  # pylint: disable=protected-access
        frozenset({"is_central_boiler_configured", "is_central_boiler_ready", "central_boiler_manager"})
    )

    def __init__(
        self,
        hass: HomeAssistant,
        unique_id,
        name,  # pylint: disable=unused-argument
        entry_infos,
    ) -> None:
        """Initialize the CentralBoiler Binary sensor"""
        self._config_id = unique_id
        self._attr_unique_id = "central_boiler_state"
        self._attr_is_on = False
        self._device_name = entry_infos.get(CONF_NAME)
        self._entry_infos = entry_infos
        self._hass = hass

    @property
    def device_info(self) -> DeviceInfo:
        """Return the device info."""
        return DeviceInfo(
            entry_type=None,
            identifiers={(DOMAIN, self._config_id)},
            name=self._device_name,
            manufacturer=DEVICE_MANUFACTURER,
            model=DOMAIN,
        )

    @property
    def device_class(self) -> BinarySensorDeviceClass | None:
        return BinarySensorDeviceClass.RUNNING

    @property
    def icon(self) -> str | None:
        if self._attr_is_on:
            return "mdi:water-boiler"
        else:
            return "mdi:water-boiler-off"

    @overrides
    async def async_added_to_hass(self) -> None:
        await super().async_added_to_hass()

        api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api(self._hass)
        api.central_boiler_manager.register_central_boiler(self)

        # Listen to central boiler events
        self.async_on_remove(
            self._hass.bus.async_listen(
                EventType.CENTRAL_BOILER_EVENT.value,
                self._handle_central_boiler_event,
            )
        )

        self.update_custom_attributes()
        self.async_write_ha_state()

    @callback
    def _handle_central_boiler_event(self, event):
        """Handle central boiler event to update internal state."""
        _LOGGER.debug("%s - Received central boiler event: %s", self, event.data)
        if "central_boiler" in event.data:
            new_state = event.data["central_boiler"]
            if self._attr_is_on != new_state:
                self._attr_is_on = new_state
                self.refresh_custom_attributes()

    def refresh_custom_attributes(self):
        """Refresh the custom attributes"""
        self.update_custom_attributes()
        self.async_write_ha_state()

    def update_custom_attributes(self):
        """Update the custom extra attributes for the entity"""
        self._attr_extra_state_attributes = {"central_boiler_state": STATE_ON if self._attr_is_on else STATE_OFF}
        api: VersatileThermostatAPI = VersatileThermostatAPI.get_vtherm_api()
        cb_manager = api.central_boiler_manager
        cb_manager.add_custom_attributes(self._attr_extra_state_attributes)

    def __str__(self):
        return f"VersatileThermostat-{self.name}"
