"""Module for cli light control commands."""

import asyncclick as click

from kasa import (
    Device,
    Module,
)
from kasa.iot import (
    IotBulb,
)

from .common import echo, error, pass_dev_or_child


@click.group()
@pass_dev_or_child
def light(dev) -> None:
    """Commands to control light settings."""


@light.command()
@click.argument("brightness", type=click.IntRange(0, 100), default=None, required=False)
@click.option("--transition", type=int, required=False)
@pass_dev_or_child
async def brightness(dev: Device, brightness: int, transition: int):
    """Get or set brightness."""
    if not (light := dev.modules.get(Module.Light)) or not light.has_feature(
        "brightness"
    ):
        error("This device does not support brightness.")
        return

    if brightness is None:
        echo(f"Brightness: {light.brightness}")
        return light.brightness
    else:
        echo(f"Setting brightness to {brightness}")
        return await light.set_brightness(brightness, transition=transition)


@light.command()
@click.argument(
    "temperature", type=click.IntRange(2500, 9000), default=None, required=False
)
@click.option("--transition", type=int, required=False)
@pass_dev_or_child
async def temperature(dev: Device, temperature: int, transition: int):
    """Get or set color temperature."""
    if not (light := dev.modules.get(Module.Light)) or not (
        color_temp_feat := light.get_feature("color_temp")
    ):
        error("Device does not support color temperature")
        return

    if temperature is None:
        echo(f"Color temperature: {light.color_temp}")
        valid_temperature_range = color_temp_feat.range
        if valid_temperature_range != (0, 0):
            echo("(min: {}, max: {})".format(*valid_temperature_range))
        else:
            echo(
                "Temperature range unknown, please open a github issue"
                f" or a pull request for model '{dev.model}'"
            )
        return color_temp_feat.range
    else:
        echo(f"Setting color temperature to {temperature}")
        return await light.set_color_temp(temperature, transition=transition)


@light.command()
@click.argument("effect", type=click.STRING, default=None, required=False)
@click.pass_context
@pass_dev_or_child
async def effect(dev: Device, ctx, effect):
    """Set an effect."""
    if not (light_effect := dev.modules.get(Module.LightEffect)):
        error("Device does not support effects")
        return
    if effect is None:
        echo(
            f"Light effect: {light_effect.effect}\n"
            + f"Available Effects: {light_effect.effect_list}"
        )
        return light_effect.effect

    if effect not in light_effect.effect_list:
        raise click.BadArgumentUsage(
            f"Effect must be one of: {light_effect.effect_list}", ctx
        )

    echo(f"Setting Effect: {effect}")
    return await light_effect.set_effect(effect)


@light.command()
@click.argument("h", type=click.IntRange(0, 360), default=None, required=False)
@click.argument("s", type=click.IntRange(0, 100), default=None, required=False)
@click.argument("v", type=click.IntRange(0, 100), default=None, required=False)
@click.option("--transition", type=int, required=False)
@click.pass_context
@pass_dev_or_child
async def hsv(dev: Device, ctx, h, s, v, transition):
    """Get or set color in HSV."""
    if not (light := dev.modules.get(Module.Light)) or not light.has_feature("hsv"):
        error("Device does not support colors")
        return

    if h is None and s is None and v is None:
        echo(f"Current HSV: {light.hsv}")
        return light.hsv
    elif s is None or v is None:
        raise click.BadArgumentUsage("Setting a color requires 3 values.", ctx)
    else:
        echo(f"Setting HSV: {h} {s} {v}")
        return await light.set_hsv(h, s, v, transition=transition)


@light.group(invoke_without_command=True)
@pass_dev_or_child
@click.pass_context
async def presets(ctx, dev):
    """List and modify bulb setting presets."""
    if ctx.invoked_subcommand is None:
        return await ctx.invoke(presets_list)


@presets.command(name="list")
@pass_dev_or_child
def presets_list(dev: Device):
    """List presets."""
    if not (light_preset := dev.modules.get(Module.LightPreset)):
        error("Device does not support light presets")
        return

    for idx, preset in enumerate(light_preset.preset_states_list):
        echo(
            f"[{idx}] Hue: {preset.hue or '':3}  "
            f"Saturation: {preset.saturation or '':3}  "
            f"Brightness/Value: {preset.brightness or '':3}  "
            f"Temp: {preset.color_temp or '':4}"
        )

    return light_preset.preset_states_list


@presets.command(name="modify")
@click.argument("index", type=int)
@click.option("--brightness", type=int, required=False, default=None)
@click.option("--hue", type=int, required=False, default=None)
@click.option("--saturation", type=int, required=False, default=None)
@click.option("--temperature", type=int, required=False, default=None)
@pass_dev_or_child
async def presets_modify(dev: Device, index, brightness, hue, saturation, temperature):
    """Modify a preset."""
    if not (light_preset := dev.modules.get(Module.LightPreset)):
        error("Device does not support light presets")
        return

    max_index = len(light_preset.preset_states_list) - 1
    if index > len(light_preset.preset_states_list) - 1:
        error(f"Invalid index, must be between 0 and {max_index}")
        return

    if all([val is None for val in {brightness, hue, saturation, temperature}]):
        error("Need to supply at least one option to modify.")
        return

    # Preset names have `Not set`` as the first value
    preset_name = light_preset.preset_list[index + 1]
    preset = light_preset.preset_states_list[index]

    echo(f"Preset {preset_name} currently: {preset}")

    if brightness is not None and preset.brightness is not None:
        preset.brightness = brightness
    if hue is not None and preset.hue is not None:
        preset.hue = hue
    if saturation is not None and preset.saturation is not None:
        preset.saturation = saturation
    if temperature is not None and preset.temperature is not None:
        preset.color_temp = temperature

    echo(f"Updating preset {preset_name} to: {preset}")

    return await light_preset.save_preset(preset_name, preset)


@light.command()
@pass_dev_or_child
@click.option("--type", type=click.Choice(["soft", "hard"], case_sensitive=False))
@click.option("--last", is_flag=True)
@click.option("--preset", type=int)
async def turn_on_behavior(dev: Device, type, last, preset):
    """Modify bulb turn-on behavior."""
    if dev.device_type is not Device.Type.Bulb or not isinstance(dev, IotBulb):
        error("Presets only supported on iot bulbs")
        return
    settings = await dev.get_turn_on_behavior()
    echo(f"Current turn on behavior: {settings}")

    # Return if we are not setting the value
    if not type and not last and not preset:
        return settings

    # If we are setting the value, the type has to be specified
    if (last or preset) and type is None:
        echo("To set the behavior, you need to define --type")
        return

    behavior = getattr(settings, type)

    if last:
        echo(f"Going to set {type} to last")
        behavior.preset = None
    elif preset is not None:
        echo(f"Going to set {type} to preset {preset}")
        behavior.preset = preset

    return await dev.set_turn_on_behavior(settings)