"""Utils for Magic Home."""
from __future__ import annotations

from flux_led.aio import AIOWifiLedBulb
from flux_led.const import COLOR_MODE_DIM as FLUX_COLOR_MODE_DIM, MultiColorEffects

from homeassistant.components.light import (
    COLOR_MODE_BRIGHTNESS,
    COLOR_MODE_ONOFF,
    COLOR_MODE_WHITE,
)
from homeassistant.util.color import color_hsv_to_RGB, color_RGB_to_hsv

from .const import FLUX_COLOR_MODE_TO_HASS, MIN_RGB_BRIGHTNESS


def _hass_color_modes(device: AIOWifiLedBulb) -> set[str]:
    color_modes = device.color_modes
    return {_flux_color_mode_to_hass(mode, color_modes) for mode in color_modes}


def format_as_flux_mac(mac: str | None) -> str | None:
    """Convert a device registry formatted mac to flux mac."""
    return None if mac is None else mac.replace(":", "").upper()


def _human_readable_option(const_option: str) -> str:
    return const_option.replace("_", " ").title()


def mac_matches_by_one(formatted_mac_1: str, formatted_mac_2: str) -> bool:
    """Check if a mac address is only one digit off.

    Some of the devices have two mac addresses which are
    one off from each other. We need to treat them as the same
    since its the same device.
    """
    mac_int_1 = int(formatted_mac_1.replace(":", ""), 16)
    mac_int_2 = int(formatted_mac_2.replace(":", ""), 16)
    return abs(mac_int_1 - mac_int_2) < 2


def _flux_color_mode_to_hass(
    flux_color_mode: str | None, flux_color_modes: set[str]
) -> str:
    """Map the flux color mode to Home Assistant color mode."""
    if flux_color_mode is None:
        return COLOR_MODE_ONOFF
    if flux_color_mode == FLUX_COLOR_MODE_DIM:
        if len(flux_color_modes) > 1:
            return COLOR_MODE_WHITE
        return COLOR_MODE_BRIGHTNESS
    return FLUX_COLOR_MODE_TO_HASS.get(flux_color_mode, COLOR_MODE_ONOFF)


def _effect_brightness(brightness: int) -> int:
    """Convert hass brightness to effect brightness."""
    return round(brightness / 255 * 100)


def _str_to_multi_color_effect(effect_str: str) -> MultiColorEffects:
    """Convert an multicolor effect string to MultiColorEffects."""
    for effect in MultiColorEffects:
        if effect.name.lower() == effect_str:
            return effect
    # unreachable due to schema validation
    assert False  # pragma: no cover


def _is_zero_rgb_brightness(rgb: tuple[int, int, int]) -> bool:
    """RGB brightness is zero."""
    return all(byte == 0 for byte in rgb)


def _min_rgb_brightness(rgb: tuple[int, int, int]) -> tuple[int, int, int]:
    """Ensure the RGB value will not turn off the device from a turn on command."""
    if _is_zero_rgb_brightness(rgb):
        return (MIN_RGB_BRIGHTNESS, MIN_RGB_BRIGHTNESS, MIN_RGB_BRIGHTNESS)
    return rgb


def _min_scaled_rgb_brightness(rgb: tuple[int, int, int]) -> tuple[int, int, int]:
    """Scale an RGB tuple to minimum brightness."""
    return color_hsv_to_RGB(*color_RGB_to_hsv(*rgb)[:2], 1)


def _min_rgbw_brightness(
    rgbw: tuple[int, int, int, int], current_rgbw: tuple[int, int, int, int]
) -> tuple[int, int, int, int]:
    """Ensure the RGBW value will not turn off the device from a turn on command.

    For RGBW, we also need to ensure that there is at least one
    value in the RGB fields or the device will switch to CCT mode unexpectedly.

    If the new value being set is all zeros, scale the current
    color to brightness of 1 so we do not unexpected switch to white
    """
    if _is_zero_rgb_brightness(rgbw[:3]):
        return (*_min_scaled_rgb_brightness(current_rgbw[:3]), rgbw[3])
    return (*_min_rgb_brightness(rgbw[:3]), rgbw[3])


def _min_rgbwc_brightness(
    rgbwc: tuple[int, int, int, int, int], current_rgbwc: tuple[int, int, int, int, int]
) -> tuple[int, int, int, int, int]:
    """Ensure the RGBWC value will not turn off the device from a turn on command.

    For RGBWC, we also need to ensure that there is at least one
    value in the RGB fields or the device will switch to CCT mode unexpectedly

    If the new value being set is all zeros, scale the current
    color to brightness of 1 so we do not unexpected switch to white
    """
    if _is_zero_rgb_brightness(rgbwc[:3]):
        return (*_min_scaled_rgb_brightness(current_rgbwc[:3]), rgbwc[3], rgbwc[4])
    return (*_min_rgb_brightness(rgbwc[:3]), rgbwc[3], rgbwc[4])
