"""Test Home Assistant scenes."""

from unittest.mock import patch

import pytest
import voluptuous as vol

from homeassistant.components.homeassistant import scene as ha_scene
from homeassistant.components.homeassistant.scene import EVENT_SCENE_RELOADED
from homeassistant.const import STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.setup import async_setup_component

from tests.common import async_capture_events, async_mock_service


async def test_reload_config_service(hass: HomeAssistant) -> None:
    """Test the reload config service."""
    assert await async_setup_component(hass, "scene", {})
    await hass.async_block_till_done()

    test_reloaded_event = async_capture_events(hass, EVENT_SCENE_RELOADED)

    with patch(
        "homeassistant.config.load_yaml_config_file",
        autospec=True,
        return_value={"scene": {"name": "Hallo", "entities": {"light.kitchen": "on"}}},
    ):
        await hass.services.async_call("scene", "reload", blocking=True)
        await hass.async_block_till_done()

    assert hass.states.get("scene.hallo") is not None
    assert len(test_reloaded_event) == 1

    with patch(
        "homeassistant.config.load_yaml_config_file",
        autospec=True,
        return_value={"scene": {"name": "Bye", "entities": {"light.kitchen": "on"}}},
    ):
        await hass.services.async_call("scene", "reload", blocking=True)
        await hass.async_block_till_done()

    assert len(test_reloaded_event) == 2
    assert hass.states.get("scene.hallo") is None
    assert hass.states.get("scene.bye") is not None


async def test_apply_service(hass: HomeAssistant) -> None:
    """Test the apply service."""
    assert await async_setup_component(hass, "scene", {})
    assert await async_setup_component(hass, "light", {"light": {"platform": "demo"}})
    await hass.async_block_till_done()

    await hass.services.async_call(
        "scene", "apply", {"entities": {"light.bed_light": "off"}}, blocking=True
    )

    assert hass.states.get("light.bed_light").state == "off"

    await hass.services.async_call(
        "scene",
        "apply",
        {"entities": {"light.bed_light": {"state": "on", "brightness": 50}}},
        blocking=True,
    )

    state = hass.states.get("light.bed_light")
    assert state.state == "on"
    assert state.attributes["brightness"] == 50

    turn_on_calls = async_mock_service(hass, "light", "turn_on")
    await hass.services.async_call(
        "scene",
        "apply",
        {
            "transition": 42,
            "entities": {"light.bed_light": {"state": "on", "brightness": 50}},
        },
        blocking=True,
    )

    assert len(turn_on_calls) == 1
    assert turn_on_calls[0].domain == "light"
    assert turn_on_calls[0].service == "turn_on"
    assert turn_on_calls[0].data.get("transition") == 42
    assert turn_on_calls[0].data.get("entity_id") == "light.bed_light"
    assert turn_on_calls[0].data.get("brightness") == 50


async def test_create_service(
    hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
    """Test the create service."""
    assert await async_setup_component(
        hass,
        "scene",
        {"scene": {"name": "hallo_2", "entities": {"light.kitchen": "on"}}},
    )
    await hass.async_block_till_done()
    assert hass.states.get("scene.hallo") is None
    assert hass.states.get("scene.hallo_2") is not None

    await hass.services.async_call(
        "scene",
        "create",
        {"scene_id": "hallo", "entities": {}, "snapshot_entities": []},
        blocking=True,
    )
    await hass.async_block_till_done()
    assert "Empty scenes are not allowed" in caplog.text
    assert hass.states.get("scene.hallo") is None

    await hass.services.async_call(
        "scene",
        "create",
        {
            "scene_id": "hallo",
            "entities": {"light.bed_light": {"state": "on", "brightness": 50}},
        },
        blocking=True,
    )
    await hass.async_block_till_done()

    scene = hass.states.get("scene.hallo")
    assert scene is not None
    assert scene.domain == "scene"
    assert scene.name == "hallo"
    assert scene.state == STATE_UNKNOWN
    assert scene.attributes.get("entity_id") == ["light.bed_light"]

    await hass.services.async_call(
        "scene",
        "create",
        {
            "scene_id": "hallo",
            "entities": {"light.kitchen_light": {"state": "on", "brightness": 100}},
        },
        blocking=True,
    )
    await hass.async_block_till_done()

    scene = hass.states.get("scene.hallo")
    assert scene is not None
    assert scene.domain == "scene"
    assert scene.name == "hallo"
    assert scene.state == STATE_UNKNOWN
    assert scene.attributes.get("entity_id") == ["light.kitchen_light"]

    await hass.services.async_call(
        "scene",
        "create",
        {
            "scene_id": "hallo_2",
            "entities": {"light.bed_light": {"state": "on", "brightness": 50}},
        },
        blocking=True,
    )
    await hass.async_block_till_done()

    assert "The scene scene.hallo_2 already exists" in caplog.text
    scene = hass.states.get("scene.hallo_2")
    assert scene is not None
    assert scene.domain == "scene"
    assert scene.name == "hallo_2"
    assert scene.state == STATE_UNKNOWN
    assert scene.attributes.get("entity_id") == ["light.kitchen"]


async def test_delete_service(
    hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
    """Test the delete service."""
    assert await async_setup_component(
        hass,
        "scene",
        {"scene": {"name": "hallo_2", "entities": {"light.kitchen": "on"}}},
    )
    await hass.async_block_till_done()

    await hass.services.async_call(
        "scene",
        "create",
        {
            "scene_id": "hallo",
            "entities": {"light.bed_light": {"state": "on", "brightness": 50}},
        },
        blocking=True,
    )
    await hass.async_block_till_done()

    with pytest.raises(ServiceValidationError):
        await hass.services.async_call(
            "scene",
            "delete",
            {
                "entity_id": "scene.hallo_3",
            },
            blocking=True,
        )

    with pytest.raises(ServiceValidationError):
        await hass.services.async_call(
            "scene",
            "delete",
            {
                "entity_id": "scene.hallo_2",
            },
            blocking=True,
        )
    assert hass.states.get("scene.hallo_2") is not None

    assert hass.states.get("scene.hallo") is not None

    await hass.services.async_call(
        "scene",
        "delete",
        {
            "entity_id": "scene.hallo",
        },
        blocking=True,
    )
    await hass.async_block_till_done()

    assert hass.states.get("state.hallo") is None


async def test_snapshot_service(
    hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
    """Test the snapshot option."""
    assert await async_setup_component(hass, "scene", {"scene": {}})
    await hass.async_block_till_done()
    hass.states.async_set("light.my_light", "on", {"hs_color": (345, 75)})
    assert hass.states.get("scene.hallo") is None

    await hass.services.async_call(
        "scene",
        "create",
        {"scene_id": "hallo", "snapshot_entities": ["light.my_light"]},
        blocking=True,
    )
    await hass.async_block_till_done()
    scene = hass.states.get("scene.hallo")
    assert scene is not None
    assert scene.attributes.get("entity_id") == ["light.my_light"]

    hass.states.async_set("light.my_light", "off", {"hs_color": (123, 45)})
    turn_on_calls = async_mock_service(hass, "light", "turn_on")
    await hass.services.async_call(
        "scene", "turn_on", {"entity_id": "scene.hallo"}, blocking=True
    )
    await hass.async_block_till_done()
    assert len(turn_on_calls) == 1
    assert turn_on_calls[0].data.get("entity_id") == "light.my_light"
    assert turn_on_calls[0].data.get("hs_color") == (345, 75)

    await hass.services.async_call(
        "scene",
        "create",
        {"scene_id": "hallo_2", "snapshot_entities": ["light.not_existent"]},
        blocking=True,
    )
    await hass.async_block_till_done()
    assert hass.states.get("scene.hallo_2") is None
    assert (
        "Entity light.not_existent does not exist and therefore cannot be snapshotted"
        in caplog.text
    )

    await hass.services.async_call(
        "scene",
        "create",
        {
            "scene_id": "hallo_3",
            "entities": {"light.bed_light": {"state": "on", "brightness": 50}},
            "snapshot_entities": ["light.my_light"],
        },
        blocking=True,
    )
    await hass.async_block_till_done()
    scene = hass.states.get("scene.hallo_3")
    assert scene is not None
    assert "light.my_light" in scene.attributes.get("entity_id")
    assert "light.bed_light" in scene.attributes.get("entity_id")


async def test_ensure_no_intersection(hass: HomeAssistant) -> None:
    """Test that entities and snapshot_entities do not overlap."""
    assert await async_setup_component(hass, "scene", {"scene": {}})
    await hass.async_block_till_done()

    with pytest.raises(vol.MultipleInvalid) as ex:
        await hass.services.async_call(
            "scene",
            "create",
            {
                "scene_id": "hallo",
                "entities": {"light.my_light": {"state": "on", "brightness": 50}},
                "snapshot_entities": ["light.my_light"],
            },
            blocking=True,
        )
    assert "entities and snapshot_entities must not overlap" in str(ex.value)
    assert hass.states.get("scene.hallo") is None


async def test_scenes_with_entity(hass: HomeAssistant) -> None:
    """Test finding scenes with a specific entity."""
    assert await async_setup_component(
        hass,
        "scene",
        {
            "scene": [
                {"name": "scene_1", "entities": {"light.kitchen": "on"}},
                {"name": "scene_2", "entities": {"light.living_room": "off"}},
                {
                    "name": "scene_3",
                    "entities": {"light.kitchen": "on", "light.living_room": "off"},
                },
            ]
        },
    )
    await hass.async_block_till_done()

    assert sorted(ha_scene.scenes_with_entity(hass, "light.kitchen")) == [
        "scene.scene_1",
        "scene.scene_3",
    ]


async def test_entities_in_scene(hass: HomeAssistant) -> None:
    """Test finding entities in a scene."""
    assert await async_setup_component(
        hass,
        "scene",
        {
            "scene": [
                {"name": "scene_1", "entities": {"light.kitchen": "on"}},
                {"name": "scene_2", "entities": {"light.living_room": "off"}},
                {
                    "name": "scene_3",
                    "entities": {"light.kitchen": "on", "light.living_room": "off"},
                },
            ]
        },
    )
    await hass.async_block_till_done()

    for scene_id, entities in (
        ("scene.scene_1", ["light.kitchen"]),
        ("scene.scene_2", ["light.living_room"]),
        ("scene.scene_3", ["light.kitchen", "light.living_room"]),
    ):
        assert ha_scene.entities_in_scene(hass, scene_id) == entities


async def test_config(hass: HomeAssistant) -> None:
    """Test passing config in YAML."""
    assert await async_setup_component(
        hass,
        "scene",
        {
            "scene": [
                {
                    "id": "scene_id",
                    "name": "Scene Icon",
                    "icon": "mdi:party",
                    "entities": {"light.kitchen": "on"},
                },
                {
                    "name": "Scene No Icon",
                    "entities": {"light.kitchen": {"state": "on"}},
                },
            ]
        },
    )
    await hass.async_block_till_done()

    icon = hass.states.get("scene.scene_icon")
    assert icon is not None
    assert icon.attributes["icon"] == "mdi:party"

    no_icon = hass.states.get("scene.scene_no_icon")
    assert no_icon is not None
    assert "icon" not in no_icon.attributes


def test_validator() -> None:
    """Test validators."""
    parsed = ha_scene.STATES_SCHEMA({"light.Test": {"state": "on"}})
    assert len(parsed) == 1
    assert "light.test" in parsed
    assert parsed["light.test"].entity_id == "light.test"
    assert parsed["light.test"].state == "on"
