"""Support for HomematicIP Cloud lights."""

from __future__ import annotations

import logging
from typing import Any

from homematicip.base.enums import (
    DeviceType,
    FunctionalChannelType,
    OpticalSignalBehaviour,
    RGBColorState,
)
from homematicip.base.functionalChannels import NotificationLightChannel
from homematicip.device import (
    BrandDimmer,
    BrandSwitchNotificationLight,
    Device,
    Dimmer,
    DinRailDimmer3,
    FullFlushDimmer,
    PluggableDimmer,
    SwitchMeasuring,
    WiredDimmer3,
)
from packaging.version import Version

from homeassistant.components.light import (
    ATTR_BRIGHTNESS,
    ATTR_COLOR_NAME,
    ATTR_EFFECT,
    ATTR_HS_COLOR,
    ATTR_TRANSITION,
    ColorMode,
    LightEntity,
    LightEntityFeature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .entity import HomematicipGenericEntity
from .hap import HomematicIPConfigEntry, HomematicipHAP

_logger = logging.getLogger(__name__)


async def async_setup_entry(
    hass: HomeAssistant,
    config_entry: HomematicIPConfigEntry,
    async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
    """Set up the HomematicIP Cloud lights from a config entry."""
    hap = config_entry.runtime_data
    entities: list[HomematicipGenericEntity] = []

    entities.extend(
        HomematicipLightHS(hap, d, ch.index)
        for d in hap.home.devices
        for ch in d.functionalChannels
        if ch.functionalChannelType == FunctionalChannelType.UNIVERSAL_LIGHT_CHANNEL
    )

    for device in hap.home.devices:
        if (
            isinstance(device, SwitchMeasuring)
            and getattr(device, "deviceType", None) == DeviceType.BRAND_SWITCH_MEASURING
        ):
            entities.append(HomematicipLightMeasuring(hap, device))
        if isinstance(device, BrandSwitchNotificationLight):
            device_version = Version(device.firmwareVersion)
            entities.append(HomematicipLight(hap, device))

            entity_class = (
                HomematicipNotificationLightV2
                if device_version > Version("2.0.0")
                else HomematicipNotificationLight
            )

            entities.append(
                entity_class(hap, device, device.topLightChannelIndex, "Top")
            )
            entities.append(
                entity_class(hap, device, device.bottomLightChannelIndex, "Bottom")
            )

        elif isinstance(device, (WiredDimmer3, DinRailDimmer3)):
            entities.extend(
                HomematicipMultiDimmer(hap, device, channel=channel)
                for channel in range(1, 4)
            )
        elif isinstance(
            device,
            (Dimmer, PluggableDimmer, BrandDimmer, FullFlushDimmer),
        ):
            entities.append(HomematicipDimmer(hap, device))

    async_add_entities(entities)


class HomematicipLight(HomematicipGenericEntity, LightEntity):
    """Representation of the HomematicIP light."""

    _attr_color_mode = ColorMode.ONOFF
    _attr_supported_color_modes = {ColorMode.ONOFF}

    def __init__(self, hap: HomematicipHAP, device) -> None:
        """Initialize the light entity."""
        super().__init__(hap, device)

    @property
    def is_on(self) -> bool:
        """Return true if light is on."""
        return self._device.on

    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the light on."""
        await self._device.turn_on_async()

    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the light off."""
        await self._device.turn_off_async()


class HomematicipLightHS(HomematicipGenericEntity, LightEntity):
    """Representation of the HomematicIP light with HS color mode."""

    _attr_color_mode = ColorMode.HS
    _attr_supported_color_modes = {ColorMode.HS}

    def __init__(self, hap: HomematicipHAP, device: Device, channel_index: int) -> None:
        """Initialize the light entity."""
        super().__init__(hap, device, channel=channel_index, is_multi_channel=True)

    @property
    def is_on(self) -> bool:
        """Return true if light is on."""
        channel = self.get_channel_or_raise()
        return channel.on

    @property
    def brightness(self) -> int | None:
        """Return the current brightness."""
        channel = self.get_channel_or_raise()
        return int(channel.dimLevel * 255.0)

    @property
    def hs_color(self) -> tuple[float, float] | None:
        """Return the hue and saturation color value [float, float]."""
        channel = self.get_channel_or_raise()
        if channel.hue is None or channel.saturationLevel is None:
            return None
        return (
            channel.hue,
            channel.saturationLevel * 100.0,
        )

    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the light on."""
        channel = self.get_channel_or_raise()
        hs_color = kwargs.get(ATTR_HS_COLOR, (0.0, 0.0))
        hue = hs_color[0] % 360.0
        saturation = hs_color[1] / 100.0
        dim_level = round(kwargs.get(ATTR_BRIGHTNESS, 255) / 255.0, 2)

        if ATTR_HS_COLOR not in kwargs:
            hue = channel.hue
            saturation = channel.saturationLevel

        if ATTR_BRIGHTNESS not in kwargs:
            # If no brightness is set, use the current brightness
            dim_level = channel.dimLevel or 1.0
        await channel.set_hue_saturation_dim_level_async(
            hue=hue, saturation_level=saturation, dim_level=dim_level
        )

    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the light off."""
        channel = self.get_channel_or_raise()
        await channel.set_switch_state_async(on=False)


class HomematicipLightMeasuring(HomematicipLight):
    """Representation of the HomematicIP measuring light."""


class HomematicipMultiDimmer(HomematicipGenericEntity, LightEntity):
    """Representation of HomematicIP Cloud dimmer."""

    _attr_color_mode = ColorMode.BRIGHTNESS
    _attr_supported_color_modes = {ColorMode.BRIGHTNESS}

    def __init__(
        self,
        hap: HomematicipHAP,
        device,
        channel=1,
        is_multi_channel=True,
    ) -> None:
        """Initialize the dimmer light entity."""
        super().__init__(
            hap, device, channel=channel, is_multi_channel=is_multi_channel
        )

    @property
    def is_on(self) -> bool:
        """Return true if dimmer is on."""
        func_channel = self._device.functionalChannels[self._channel]
        return func_channel.dimLevel is not None and func_channel.dimLevel > 0.0

    @property
    def brightness(self) -> int:
        """Return the brightness of this light between 0..255."""
        return int(
            (self._device.functionalChannels[self._channel].dimLevel or 0.0) * 255
        )

    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the dimmer on."""
        if ATTR_BRIGHTNESS in kwargs:
            await self._device.set_dim_level_async(
                kwargs[ATTR_BRIGHTNESS] / 255.0, self._channel
            )
        else:
            await self._device.set_dim_level_async(1, self._channel)

    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the dimmer off."""
        await self._device.set_dim_level_async(0, self._channel)


class HomematicipDimmer(HomematicipMultiDimmer, LightEntity):
    """Representation of HomematicIP Cloud dimmer."""

    def __init__(self, hap: HomematicipHAP, device) -> None:
        """Initialize the dimmer light entity."""
        super().__init__(hap, device, is_multi_channel=False)


class HomematicipNotificationLight(HomematicipGenericEntity, LightEntity):
    """Representation of HomematicIP Cloud notification light."""

    _attr_color_mode = ColorMode.HS
    _attr_supported_color_modes = {ColorMode.HS}
    _attr_supported_features = LightEntityFeature.TRANSITION

    def __init__(self, hap: HomematicipHAP, device, channel: int, post: str) -> None:
        """Initialize the notification light entity."""
        super().__init__(hap, device, post=post, channel=channel, is_multi_channel=True)

        self._color_switcher: dict[str, tuple[float, float]] = {
            RGBColorState.WHITE: (0.0, 0.0),
            RGBColorState.RED: (0.0, 100.0),
            RGBColorState.YELLOW: (60.0, 100.0),
            RGBColorState.GREEN: (120.0, 100.0),
            RGBColorState.TURQUOISE: (180.0, 100.0),
            RGBColorState.BLUE: (240.0, 100.0),
            RGBColorState.PURPLE: (300.0, 100.0),
        }

    @property
    def _func_channel(self) -> NotificationLightChannel:
        return self._device.functionalChannels[self._channel]

    @property
    def is_on(self) -> bool:
        """Return true if light is on."""
        return (
            self._func_channel.dimLevel is not None
            and self._func_channel.dimLevel > 0.0
        )

    @property
    def brightness(self) -> int:
        """Return the brightness of this light between 0..255."""
        return int((self._func_channel.dimLevel or 0.0) * 255)

    @property
    def hs_color(self) -> tuple[float, float]:
        """Return the hue and saturation color value [float, float]."""
        simple_rgb_color = self._func_channel.simpleRGBColorState
        return self._color_switcher.get(simple_rgb_color, (0.0, 0.0))

    @property
    def extra_state_attributes(self) -> dict[str, Any]:
        """Return the state attributes of the notification light sensor."""
        state_attr = super().extra_state_attributes

        if self.is_on:
            state_attr[ATTR_COLOR_NAME] = self._func_channel.simpleRGBColorState

        return state_attr

    @property
    def unique_id(self) -> str:
        """Return a unique ID."""
        return f"{self.__class__.__name__}_{self._post}_{self._device.id}"

    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the light on."""
        # Use hs_color from kwargs,
        # if not applicable use current hs_color.
        hs_color = kwargs.get(ATTR_HS_COLOR, self.hs_color)
        simple_rgb_color = _convert_color(hs_color)

        # Use brightness from kwargs,
        # if not applicable use current brightness.
        brightness = kwargs.get(ATTR_BRIGHTNESS, self.brightness)

        # If no kwargs, use default value.
        if not kwargs:
            brightness = 255

        # Minimum brightness is 10, otherwise the led is disabled
        brightness = max(10, brightness)
        dim_level = brightness / 255.0
        transition = kwargs.get(ATTR_TRANSITION, 0.5)

        await self._device.set_rgb_dim_level_with_time_async(
            channelIndex=self._channel,
            rgb=simple_rgb_color,
            dimLevel=dim_level,
            onTime=0,
            rampTime=transition,
        )

    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the light off."""
        simple_rgb_color = self._func_channel.simpleRGBColorState
        transition = kwargs.get(ATTR_TRANSITION, 0.5)

        await self._device.set_rgb_dim_level_with_time_async(
            channelIndex=self._channel,
            rgb=simple_rgb_color,
            dimLevel=0.0,
            onTime=0,
            rampTime=transition,
        )


class HomematicipNotificationLightV2(HomematicipNotificationLight, LightEntity):
    """Representation of HomematicIP Cloud notification light."""

    _effect_list = [
        OpticalSignalBehaviour.BILLOW_MIDDLE,
        OpticalSignalBehaviour.BLINKING_MIDDLE,
        OpticalSignalBehaviour.FLASH_MIDDLE,
        OpticalSignalBehaviour.OFF,
        OpticalSignalBehaviour.ON,
    ]

    def __init__(self, hap: HomematicipHAP, device, channel: int, post: str) -> None:
        """Initialize the notification light entity."""
        super().__init__(hap, device, post=post, channel=channel)
        self._attr_supported_features |= LightEntityFeature.EFFECT

    @property
    def effect_list(self) -> list[str] | None:
        """Return the list of supported effects."""
        return self._effect_list

    @property
    def effect(self) -> str | None:
        """Return the current effect."""
        return self._func_channel.opticalSignalBehaviour

    @property
    def is_on(self) -> bool:
        """Return true if light is on."""
        return self._func_channel.on

    async def async_turn_on(self, **kwargs: Any) -> None:
        """Turn the light on."""
        # Use hs_color from kwargs,
        # if not applicable use current hs_color.
        hs_color = kwargs.get(ATTR_HS_COLOR, self.hs_color)
        simple_rgb_color = _convert_color(hs_color)

        # If no kwargs, use default value.
        brightness = 255
        if ATTR_BRIGHTNESS in kwargs:
            brightness = kwargs[ATTR_BRIGHTNESS]

        # Minimum brightness is 10, otherwise the led is disabled
        brightness = max(10, brightness)
        dim_level = round(brightness / 255.0, 2)

        effect = self.effect
        if ATTR_EFFECT in kwargs:
            effect = kwargs[ATTR_EFFECT]

        await self._func_channel.async_set_optical_signal(
            opticalSignalBehaviour=effect, rgb=simple_rgb_color, dimLevel=dim_level
        )

    async def async_turn_off(self, **kwargs: Any) -> None:
        """Turn the light off."""
        await self._func_channel.async_turn_off()


def _convert_color(color: tuple) -> RGBColorState:
    """Convert the given color to the reduced RGBColorState color.

    RGBColorStat contains only 8 colors including white and black,
    so a conversion is required.
    """
    if color is None:
        return RGBColorState.WHITE

    hue = int(color[0])
    saturation = int(color[1])
    if saturation < 5:
        return RGBColorState.WHITE
    if 30 < hue <= 90:
        return RGBColorState.YELLOW
    if 90 < hue <= 160:
        return RGBColorState.GREEN
    if 150 < hue <= 210:
        return RGBColorState.TURQUOISE
    if 210 < hue <= 270:
        return RGBColorState.BLUE
    if 270 < hue <= 330:
        return RGBColorState.PURPLE
    return RGBColorState.RED
