"""Test for the SmartThings light platform."""

from typing import Any
from unittest.mock import AsyncMock, call

from pysmartthings import Attribute, Capability, Command
from pysmartthings.models import HealthStatus
import pytest
from syrupy.assertion import SnapshotAssertion

from homeassistant.components.light import (
    ATTR_BRIGHTNESS,
    ATTR_COLOR_MODE,
    ATTR_COLOR_TEMP_KELVIN,
    ATTR_HS_COLOR,
    ATTR_MAX_COLOR_TEMP_KELVIN,
    ATTR_MIN_COLOR_TEMP_KELVIN,
    ATTR_RGB_COLOR,
    ATTR_SUPPORTED_COLOR_MODES,
    ATTR_TRANSITION,
    ATTR_XY_COLOR,
    DOMAIN as LIGHT_DOMAIN,
    ColorMode,
)
from homeassistant.components.smartthings.const import MAIN
from homeassistant.const import (
    ATTR_ENTITY_ID,
    SERVICE_TURN_OFF,
    SERVICE_TURN_ON,
    STATE_OFF,
    STATE_ON,
    STATE_UNAVAILABLE,
    STATE_UNKNOWN,
    Platform,
)
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers import entity_registry as er

from . import (
    set_attribute_value,
    setup_integration,
    snapshot_smartthings_entities,
    trigger_health_update,
    trigger_update,
)

from tests.common import MockConfigEntry, mock_restore_cache_with_extra_data


async def test_all_entities(
    hass: HomeAssistant,
    snapshot: SnapshotAssertion,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
    entity_registry: er.EntityRegistry,
) -> None:
    """Test all entities."""
    await setup_integration(hass, mock_config_entry)

    snapshot_smartthings_entities(hass, entity_registry, snapshot, Platform.LIGHT)


@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
@pytest.mark.parametrize(
    ("data", "calls"),
    [
        (
            {},
            [
                call(
                    "cb958955-b015-498c-9e62-fc0c51abd054",
                    Capability.SWITCH,
                    Command.ON,
                    MAIN,
                )
            ],
        ),
        (
            {ATTR_COLOR_TEMP_KELVIN: 4000},
            [
                call(
                    "cb958955-b015-498c-9e62-fc0c51abd054",
                    Capability.COLOR_TEMPERATURE,
                    Command.SET_COLOR_TEMPERATURE,
                    MAIN,
                    argument=4000,
                ),
                call(
                    "cb958955-b015-498c-9e62-fc0c51abd054",
                    Capability.SWITCH,
                    Command.ON,
                    MAIN,
                ),
            ],
        ),
        (
            {ATTR_HS_COLOR: [350, 90]},
            [
                call(
                    "cb958955-b015-498c-9e62-fc0c51abd054",
                    Capability.COLOR_CONTROL,
                    Command.SET_COLOR,
                    MAIN,
                    argument={"hue": 97.2222, "saturation": 90.0},
                ),
                call(
                    "cb958955-b015-498c-9e62-fc0c51abd054",
                    Capability.SWITCH,
                    Command.ON,
                    MAIN,
                ),
            ],
        ),
        (
            {ATTR_BRIGHTNESS: 50},
            [
                call(
                    "cb958955-b015-498c-9e62-fc0c51abd054",
                    Capability.SWITCH_LEVEL,
                    Command.SET_LEVEL,
                    MAIN,
                    argument=[20, 0],
                )
            ],
        ),
        (
            {ATTR_BRIGHTNESS: 50, ATTR_TRANSITION: 3},
            [
                call(
                    "cb958955-b015-498c-9e62-fc0c51abd054",
                    Capability.SWITCH_LEVEL,
                    Command.SET_LEVEL,
                    MAIN,
                    argument=[20, 3],
                )
            ],
        ),
    ],
)
async def test_turn_on_light(
    hass: HomeAssistant,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
    data: dict[str, Any],
    calls: list[call],
) -> None:
    """Test light turn on command."""
    await setup_integration(hass, mock_config_entry)

    await hass.services.async_call(
        LIGHT_DOMAIN,
        SERVICE_TURN_ON,
        {ATTR_ENTITY_ID: "light.standing_light"} | data,
        blocking=True,
    )
    assert devices.execute_device_command.mock_calls == calls


@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
@pytest.mark.parametrize(
    ("data", "calls"),
    [
        (
            {},
            [
                call(
                    "cb958955-b015-498c-9e62-fc0c51abd054",
                    Capability.SWITCH,
                    Command.OFF,
                    MAIN,
                )
            ],
        ),
        (
            {ATTR_TRANSITION: 3},
            [
                call(
                    "cb958955-b015-498c-9e62-fc0c51abd054",
                    Capability.SWITCH_LEVEL,
                    Command.SET_LEVEL,
                    MAIN,
                    argument=[0, 3],
                )
            ],
        ),
    ],
)
async def test_turn_off_light(
    hass: HomeAssistant,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
    data: dict[str, Any],
    calls: list[call],
) -> None:
    """Test light turn off command."""
    await setup_integration(hass, mock_config_entry)

    await hass.services.async_call(
        LIGHT_DOMAIN,
        SERVICE_TURN_OFF,
        {ATTR_ENTITY_ID: "light.standing_light"} | data,
        blocking=True,
    )
    assert devices.execute_device_command.mock_calls == calls


@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
async def test_state_update(
    hass: HomeAssistant,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test state update."""
    await setup_integration(hass, mock_config_entry)

    assert hass.states.get("light.standing_light").state == STATE_OFF

    await trigger_update(
        hass,
        devices,
        "cb958955-b015-498c-9e62-fc0c51abd054",
        Capability.SWITCH,
        Attribute.SWITCH,
        "on",
    )

    assert hass.states.get("light.standing_light").state == STATE_ON


@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
async def test_updating_brightness(
    hass: HomeAssistant,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test brightness update."""
    set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on")
    await setup_integration(hass, mock_config_entry)

    await trigger_update(
        hass,
        devices,
        "cb958955-b015-498c-9e62-fc0c51abd054",
        Capability.COLOR_CONTROL,
        Attribute.HUE,
        40,
    )

    assert hass.states.get("light.standing_light").attributes[ATTR_BRIGHTNESS] == 178

    await trigger_update(
        hass,
        devices,
        "cb958955-b015-498c-9e62-fc0c51abd054",
        Capability.SWITCH_LEVEL,
        Attribute.LEVEL,
        20,
    )

    assert hass.states.get("light.standing_light").attributes[ATTR_BRIGHTNESS] == 51


@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
async def test_updating_hs(
    hass: HomeAssistant,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test hue/saturation update."""
    set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on")
    await setup_integration(hass, mock_config_entry)

    await trigger_update(
        hass,
        devices,
        "cb958955-b015-498c-9e62-fc0c51abd054",
        Capability.COLOR_CONTROL,
        Attribute.HUE,
        40,
    )

    assert hass.states.get("light.standing_light").attributes[ATTR_HS_COLOR] == (
        144.0,
        60,
    )

    await trigger_update(
        hass,
        devices,
        "cb958955-b015-498c-9e62-fc0c51abd054",
        Capability.COLOR_CONTROL,
        Attribute.HUE,
        20,
    )

    assert hass.states.get("light.standing_light").attributes[ATTR_HS_COLOR] == (
        72.0,
        60,
    )


@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
async def test_updating_color_temp(
    hass: HomeAssistant,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test color temperature update."""
    set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on")
    await setup_integration(hass, mock_config_entry)

    await trigger_update(
        hass,
        devices,
        "cb958955-b015-498c-9e62-fc0c51abd054",
        Capability.COLOR_TEMPERATURE,
        Attribute.COLOR_TEMPERATURE,
        3000,
    )

    assert (
        hass.states.get("light.standing_light").attributes[ATTR_COLOR_MODE]
        is ColorMode.COLOR_TEMP
    )
    assert (
        hass.states.get("light.standing_light").attributes[ATTR_COLOR_TEMP_KELVIN]
        == 3000
    )

    await trigger_update(
        hass,
        devices,
        "cb958955-b015-498c-9e62-fc0c51abd054",
        Capability.COLOR_TEMPERATURE,
        Attribute.COLOR_TEMPERATURE,
        2000,
    )

    assert (
        hass.states.get("light.standing_light").attributes[ATTR_COLOR_TEMP_KELVIN]
        == 2000
    )


@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
async def test_color_modes(
    hass: HomeAssistant,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test color mode changes."""
    set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on")
    set_attribute_value(devices, Capability.COLOR_CONTROL, Attribute.SATURATION, 50)
    await setup_integration(hass, mock_config_entry)

    assert (
        hass.states.get("light.standing_light").attributes[ATTR_COLOR_MODE]
        is ColorMode.HS
    )

    await trigger_update(
        hass,
        devices,
        "cb958955-b015-498c-9e62-fc0c51abd054",
        Capability.COLOR_TEMPERATURE,
        Attribute.COLOR_TEMPERATURE,
        2000,
    )

    assert (
        hass.states.get("light.standing_light").attributes[ATTR_COLOR_MODE]
        is ColorMode.COLOR_TEMP
    )

    await trigger_update(
        hass,
        devices,
        "cb958955-b015-498c-9e62-fc0c51abd054",
        Capability.COLOR_CONTROL,
        Attribute.HUE,
        20,
    )

    assert (
        hass.states.get("light.standing_light").attributes[ATTR_COLOR_MODE]
        is ColorMode.HS
    )


@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
async def test_color_mode_after_startup(
    hass: HomeAssistant,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test color mode after startup."""
    set_attribute_value(devices, Capability.SWITCH, Attribute.SWITCH, "on")

    RESTORE_DATA = {
        ATTR_BRIGHTNESS: 178,
        ATTR_COLOR_MODE: ColorMode.COLOR_TEMP,
        ATTR_COLOR_TEMP_KELVIN: 3000,
        ATTR_HS_COLOR: (144.0, 60),
        ATTR_MAX_COLOR_TEMP_KELVIN: 9000,
        ATTR_MIN_COLOR_TEMP_KELVIN: 2000,
        ATTR_RGB_COLOR: (255, 128, 0),
        ATTR_SUPPORTED_COLOR_MODES: [ColorMode.COLOR_TEMP, ColorMode.HS],
        ATTR_XY_COLOR: (0.61, 0.35),
    }

    mock_restore_cache_with_extra_data(
        hass, ((State("light.standing_light", STATE_ON), RESTORE_DATA),)
    )
    await setup_integration(hass, mock_config_entry)

    assert (
        hass.states.get("light.standing_light").attributes[ATTR_COLOR_MODE]
        is ColorMode.COLOR_TEMP
    )


@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
async def test_availability(
    hass: HomeAssistant,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test availability."""
    await setup_integration(hass, mock_config_entry)

    assert hass.states.get("light.standing_light").state == STATE_OFF

    await trigger_health_update(
        hass, devices, "cb958955-b015-498c-9e62-fc0c51abd054", HealthStatus.OFFLINE
    )

    assert hass.states.get("light.standing_light").state == STATE_UNAVAILABLE

    await trigger_health_update(
        hass, devices, "cb958955-b015-498c-9e62-fc0c51abd054", HealthStatus.ONLINE
    )

    assert hass.states.get("light.standing_light").state == STATE_OFF


@pytest.mark.parametrize("device_fixture", ["hue_rgbw_color_bulb"])
async def test_availability_at_start(
    hass: HomeAssistant,
    unavailable_device: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test unavailable at boot."""
    await setup_integration(hass, mock_config_entry)
    assert hass.states.get("light.standing_light").state == STATE_UNAVAILABLE


@pytest.mark.parametrize("device_fixture", ["da_ks_hood_01001"])
@pytest.mark.parametrize(
    ("service", "command"),
    [
        (SERVICE_TURN_ON, Command.ON),
        (SERVICE_TURN_OFF, Command.OFF),
    ],
)
async def test_lamp_with_switch(
    hass: HomeAssistant,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
    service: str,
    command: Command,
) -> None:
    """Test samsungce.lamp on/off with switch capability."""
    await setup_integration(hass, mock_config_entry)

    await hass.services.async_call(
        LIGHT_DOMAIN,
        service,
        {ATTR_ENTITY_ID: "light.range_hood_light"},
        blocking=True,
    )
    devices.execute_device_command.assert_called_once_with(
        "fa5fca25-fa7a-1807-030a-2f72ee0f7bff",
        Capability.SWITCH,
        command,
        "lamp",
    )


@pytest.mark.parametrize(
    ("brightness", "brightness_level"),
    [(128, "low"), (129, "high"), (240, "high")],
)
@pytest.mark.parametrize(
    ("device_fixture", "entity_id", "device_id", "component"),
    [
        (
            "da_ks_hood_01001",
            "light.range_hood_light",
            "fa5fca25-fa7a-1807-030a-2f72ee0f7bff",
            "lamp",
        ),
        (
            "da_ks_microwave_0101x",
            "light.microwave_light",
            "2bad3237-4886-e699-1b90-4a51a3d55c8a",
            "hood",
        ),
    ],
)
async def test_lamp_component_with_brightness(
    hass: HomeAssistant,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
    entity_id: str,
    device_id: str,
    component: str,
    brightness: int,
    brightness_level: str,
) -> None:
    """Test samsungce.lamp on/off with switch capability."""
    await setup_integration(hass, mock_config_entry)

    await hass.services.async_call(
        LIGHT_DOMAIN,
        SERVICE_TURN_ON,
        {ATTR_ENTITY_ID: entity_id, ATTR_BRIGHTNESS: brightness},
        blocking=True,
    )
    devices.execute_device_command.assert_called_once_with(
        device_id,
        Capability.SAMSUNG_CE_LAMP,
        Command.SET_BRIGHTNESS_LEVEL,
        component,
        argument=brightness_level,
    )


@pytest.mark.parametrize("device_fixture", ["da_ks_range_0101x"])
@pytest.mark.parametrize(
    ("service", "argument"),
    [(SERVICE_TURN_ON, "extraHigh"), (SERVICE_TURN_OFF, "off")],
)
async def test_lamp_without_switch(
    hass: HomeAssistant,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
    service: str,
    argument: str,
) -> None:
    """Test samsungce.lamp on/off without switch capability."""
    await setup_integration(hass, mock_config_entry)

    await hass.services.async_call(
        LIGHT_DOMAIN,
        service,
        {ATTR_ENTITY_ID: "light.vulcan_light"},
        blocking=True,
    )
    devices.execute_device_command.assert_called_once_with(
        "2c3cbaa0-1899-5ddc-7b58-9d657bd48f18",
        Capability.SAMSUNG_CE_LAMP,
        Command.SET_BRIGHTNESS_LEVEL,
        MAIN,
        argument=argument,
    )


@pytest.mark.parametrize("device_fixture", ["da_ks_hood_01001"])
async def test_lamp_from_off(
    hass: HomeAssistant, devices: AsyncMock, mock_config_entry: MockConfigEntry
) -> None:
    """Test samsungce.lamp on with brightness level from off state."""
    set_attribute_value(
        devices, Capability.SWITCH, Attribute.SWITCH, "off", component="lamp"
    )
    await setup_integration(hass, mock_config_entry)

    assert hass.states.get("light.range_hood_light").state == STATE_OFF

    await hass.services.async_call(
        LIGHT_DOMAIN,
        SERVICE_TURN_ON,
        {ATTR_ENTITY_ID: "light.range_hood_light", ATTR_BRIGHTNESS: 255},
        blocking=True,
    )
    assert devices.execute_device_command.mock_calls == [
        call(
            "fa5fca25-fa7a-1807-030a-2f72ee0f7bff",
            Capability.SAMSUNG_CE_LAMP,
            Command.SET_BRIGHTNESS_LEVEL,
            "lamp",
            argument="high",
        ),
        call(
            "fa5fca25-fa7a-1807-030a-2f72ee0f7bff",
            Capability.SWITCH,
            Command.ON,
            "lamp",
        ),
    ]


@pytest.mark.parametrize("device_fixture", ["da_ks_hood_01001"])
async def test_lamp_unknown_switch(
    hass: HomeAssistant, devices: AsyncMock, mock_config_entry: MockConfigEntry
) -> None:
    """Test lamp state becomes unknown when switch state is unknown."""
    await setup_integration(hass, mock_config_entry)

    assert hass.states.get("light.range_hood_light").state == STATE_ON

    await trigger_update(
        hass,
        devices,
        "fa5fca25-fa7a-1807-030a-2f72ee0f7bff",
        Capability.SWITCH,
        Attribute.SWITCH,
        None,
        component="lamp",
    )

    assert hass.states.get("light.range_hood_light").state == STATE_UNKNOWN


@pytest.mark.parametrize("device_fixture", ["da_ks_range_0101x"])
async def test_lamp_unknown_brightness(
    hass: HomeAssistant, devices: AsyncMock, mock_config_entry: MockConfigEntry
) -> None:
    """Test lamp state becomes unknown when brightness level is unknown."""

    await setup_integration(hass, mock_config_entry)

    assert hass.states.get("light.vulcan_light").state == STATE_ON

    await trigger_update(
        hass,
        devices,
        "2c3cbaa0-1899-5ddc-7b58-9d657bd48f18",
        Capability.SAMSUNG_CE_LAMP,
        Attribute.BRIGHTNESS_LEVEL,
        None,
    )

    assert hass.states.get("light.vulcan_light").state == STATE_UNKNOWN


@pytest.mark.parametrize("device_fixture", ["da_ks_hood_01001"])
@pytest.mark.parametrize(
    ("service", "argument"),
    [(SERVICE_TURN_ON, "high"), (SERVICE_TURN_OFF, "off")],
)
async def test_lamp_switch_not_used_when_off_in_supported_levels(
    hass: HomeAssistant,
    devices: AsyncMock,
    mock_config_entry: MockConfigEntry,
    service: str,
    argument: str,
) -> None:
    """Test samsungce.lamp uses brightness when 'off' in levels."""
    # Modify the device status to include "off" in supported brightness levels
    # This simulates a device where the switch doesn't control the lamp
    set_attribute_value(
        devices,
        Capability.SAMSUNG_CE_LAMP,
        Attribute.SUPPORTED_BRIGHTNESS_LEVEL,
        ["off", "low", "high"],
        component="lamp",
    )
    await setup_integration(hass, mock_config_entry)

    await hass.services.async_call(
        LIGHT_DOMAIN,
        service,
        {ATTR_ENTITY_ID: "light.range_hood_light"},
        blocking=True,
    )
    devices.execute_device_command.assert_called_once_with(
        "fa5fca25-fa7a-1807-030a-2f72ee0f7bff",
        Capability.SAMSUNG_CE_LAMP,
        Command.SET_BRIGHTNESS_LEVEL,
        "lamp",
        argument=argument,
    )
