""" Implement the Switch entities of this implementation """

from __future__ import annotations
import logging
from typing import Any

from home_connect_async import Appliance, HomeConnect, HomeConnectError, Events
from homeassistant.components.switch import SwitchEntity
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType

from .common import InteractiveEntityBase, EntityManager, is_boolean_enum, Configuration
from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)

async def async_setup_entry(hass:HomeAssistant , config_entry:ConfigType, async_add_entities:AddEntitiesCallback) -> None:
    """Add sensors for passed config_entry in HA."""
    #homeconnect:HomeConnect = hass.data[DOMAIN]['homeconnect']
    entry_conf:Configuration = hass.data[DOMAIN][config_entry.entry_id]
    homeconnect:HomeConnect = entry_conf["homeconnect"]
    entity_manager = EntityManager(async_add_entities, "Switch")

    def add_appliance(appliance:Appliance) -> None:
        conf = entry_conf.get_config()
        if appliance.available_programs:
            for program in appliance.available_programs.values():
                if program.options:
                    for option in program.options.values():
                        if ( not conf.has_entity_setting(option.key, "type") and (option.type == "Boolean" or isinstance(option.value, bool))) \
                             or conf.get_entity_setting(option.key, "type") == "Boolean" :
                            device = OptionSwitch(appliance, option.key, conf)
                            entity_manager.add(device)

        if appliance.settings:
            for setting in appliance.settings.values():
                if ( (not conf.has_entity_setting(setting.key, "type")
                    and ( setting.type == "Boolean" or isinstance(setting.value, bool) or is_boolean_enum(setting.allowedvalues))) \
                    or conf.get_entity_setting(setting.key, "type") == "Boolean") \
                    and setting.access != "read" :
                    device = SettingsSwitch(appliance, setting.key, conf)
                    entity_manager.add(device)

        entity_manager.register()


    def remove_appliance(appliance:Appliance) -> None:
        entity_manager.remove_appliance(appliance)

    homeconnect.register_callback(add_appliance, [Events.PAIRED, Events.DATA_CHANGED, Events.PROGRAM_STARTED, Events.PROGRAM_SELECTED])
    homeconnect.register_callback(remove_appliance, Events.DEPAIRED)
    for appliance in homeconnect.appliances.values():
        add_appliance(appliance)


class OptionSwitch(InteractiveEntityBase, SwitchEntity):
    """ Switch for binary options """
    @property
    def device_class(self) -> str:
        return f"{DOMAIN}__options"

    @property
    def name_ext(self) -> str|None:
        if self._appliance.available_programs:
            for program in self._appliance.available_programs.values():
                if program.options and self._key in program.options and program.options[self._key].name:
                    return program.options[self._key].name
        return None

    @property
    def icon(self) -> str:
        return self.get_entity_setting('icon', 'mdi:office-building-cog')

    @property
    def available(self) -> bool:
        return self.program_option_available

    @property
    def is_on(self) -> bool:
        """Return True if entity is on."""
        option = self._appliance.get_applied_program_option(self._key)
        if option:
            return option.value
        return None

    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the entity on."""
        try:
            await self._appliance.async_set_option(self._key, True)
        except HomeConnectError as ex:
            if ex.error_description:
                raise HomeAssistantError(f"Failed to set the option: {ex.error_description} ({ex.code})")
            raise HomeAssistantError(f"Failed to set the option: ({ex.code})")

    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the entity off."""
        try:
            await self._appliance.async_set_option(self._key, False)
        except HomeConnectError as ex:
            if ex.error_description:
                raise HomeAssistantError(f"Failed to set the option: {ex.error_description} ({ex.code})")
            raise HomeAssistantError(f"Failed to set the option: ({ex.code})")


    async def async_on_update(self, appliance:Appliance, key:str, value) -> None:
        self.async_write_ha_state()


class SettingsSwitch(InteractiveEntityBase, SwitchEntity):
    """ Switch for binary settings """
    @property
    def device_class(self) -> str:
        return f"{DOMAIN}__settings"

    @property
    def name_ext(self) -> str|None:
        if self._key in self._appliance.settings and self._appliance.settings[self._key].name:
            return self._appliance.settings[self._key].name
        return None

    @property
    def icon(self) -> str:
        return self.get_entity_setting('icon', 'mdi:tune')

    @property
    def available(self) -> bool:
        return self._key in self._appliance.settings \
        and super().available \
        and (
            "BSH.Common.Status.RemoteControlActive" not in self._appliance.status or
            self._appliance.status["BSH.Common.Status.RemoteControlActive"].value
        )

    @property
    def is_on(self) -> bool:
        """Return True if entity is on."""
        if self._key in self._appliance.settings:
            setting = self._appliance.settings[self._key]
            if setting.allowedvalues and setting.value.lower().endswith(".off"):
                return False
            if setting.allowedvalues and setting.value.lower().endswith(".on"):
                return True
            return setting.value
        return None

    def bool_to_enum(self, allowedvalues, val:bool) -> str:
        """ Get the matching enum value for the provided boolean value """
        for av in allowedvalues:
            if (val and av.lower().endswith('.on')) or (not val and av.lower().endswith('.off')) :
                return av
        _LOGGER.error("Unexpected Error: couldn't find a boolean enum value in allowedvalues: %s", allowedvalues)
        return None

    async def async_turn_on(self, **kwargs: Any) -> None:
        try:
            setting = self._appliance.settings[self._key]
            if setting.allowedvalues:
                await self._appliance.async_apply_setting(self._key, self.bool_to_enum(setting.allowedvalues, True))
            else:
                await self._appliance.async_apply_setting(self._key, True)
        except HomeConnectError as ex:
            if ex.error_description:
                raise HomeAssistantError(f"Failed to apply the setting: {ex.error_description} ({ex.code})")
            raise HomeAssistantError(f"Failed to apply the setting: ({ex.code})")


    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the entity off."""
        try:
            setting = self._appliance.settings[self._key]
            if setting.allowedvalues:
                await self._appliance.async_apply_setting(self._key, self.bool_to_enum(setting.allowedvalues, False))
            else:
                await self._appliance.async_apply_setting(self._key, False)
        except HomeConnectError as ex:
            if ex.error_description:
                raise HomeAssistantError(f"Failed to apply the setting: {ex.error_description} ({ex.code})")
            raise HomeAssistantError(f"Failed to apply the setting: ({ex.code})")


    async def async_on_update(self, appliance:Appliance, key:str, value) -> None:
        self.async_write_ha_state()
