"""The tests for the Template select platform."""
from homeassistant import setup
from homeassistant.components.input_select import (
    ATTR_OPTION as INPUT_SELECT_ATTR_OPTION,
    ATTR_OPTIONS as INPUT_SELECT_ATTR_OPTIONS,
    DOMAIN as INPUT_SELECT_DOMAIN,
    SERVICE_SELECT_OPTION as INPUT_SELECT_SERVICE_SELECT_OPTION,
    SERVICE_SET_OPTIONS,
)
from homeassistant.components.select.const import (
    ATTR_OPTION as SELECT_ATTR_OPTION,
    ATTR_OPTIONS as SELECT_ATTR_OPTIONS,
    DOMAIN as SELECT_DOMAIN,
    SERVICE_SELECT_OPTION as SELECT_SERVICE_SELECT_OPTION,
)
from homeassistant.const import ATTR_ICON, CONF_ENTITY_ID, STATE_UNKNOWN
from homeassistant.core import Context
from homeassistant.helpers.entity_registry import async_get

from tests.common import assert_setup_component, async_capture_events

_TEST_SELECT = "select.template_select"
# Represent for select's current_option
_OPTION_INPUT_SELECT = "input_select.option"


async def test_missing_optional_config(hass):
    """Test: missing optional template is ok."""
    with assert_setup_component(1, "template"):
        assert await setup.async_setup_component(
            hass,
            "template",
            {
                "template": {
                    "select": {
                        "state": "{{ 'a' }}",
                        "select_option": {"service": "script.select_option"},
                        "options": "{{ ['a', 'b'] }}",
                    }
                }
            },
        )

    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    _verify(hass, "a", ["a", "b"])


async def test_multiple_configs(hass):
    """Test: multiple select entities get created."""
    with assert_setup_component(1, "template"):
        assert await setup.async_setup_component(
            hass,
            "template",
            {
                "template": {
                    "select": [
                        {
                            "state": "{{ 'a' }}",
                            "select_option": {"service": "script.select_option"},
                            "options": "{{ ['a', 'b'] }}",
                        },
                        {
                            "state": "{{ 'a' }}",
                            "select_option": {"service": "script.select_option"},
                            "options": "{{ ['a', 'b'] }}",
                        },
                    ]
                }
            },
        )

    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    _verify(hass, "a", ["a", "b"])
    _verify(hass, "a", ["a", "b"], f"{_TEST_SELECT}_2")


async def test_missing_required_keys(hass):
    """Test: missing required fields will fail."""
    with assert_setup_component(0, "template"):
        assert await setup.async_setup_component(
            hass,
            "template",
            {
                "template": {
                    "select": {
                        "select_option": {"service": "script.select_option"},
                        "options": "{{ ['a', 'b'] }}",
                    }
                }
            },
        )

    with assert_setup_component(0, "select"):
        assert await setup.async_setup_component(
            hass,
            "select",
            {
                "template": {
                    "select": {
                        "state": "{{ 'a' }}",
                        "select_option": {"service": "script.select_option"},
                    }
                }
            },
        )

    with assert_setup_component(0, "select"):
        assert await setup.async_setup_component(
            hass,
            "select",
            {
                "template": {
                    "select": {
                        "state": "{{ 'a' }}",
                        "options": "{{ ['a', 'b'] }}",
                    }
                }
            },
        )

    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    assert hass.states.async_all("select") == []


async def test_templates_with_entities(hass, calls):
    """Test templates with values from other entities."""
    with assert_setup_component(1, "input_select"):
        assert await setup.async_setup_component(
            hass,
            "input_select",
            {
                "input_select": {
                    "option": {
                        "options": ["a", "b"],
                        "initial": "a",
                        "name": "Option",
                    },
                }
            },
        )

    with assert_setup_component(1, "template"):
        assert await setup.async_setup_component(
            hass,
            "template",
            {
                "template": {
                    "unique_id": "b",
                    "select": {
                        "state": f"{{{{ states('{_OPTION_INPUT_SELECT}') }}}}",
                        "options": f"{{{{ state_attr('{_OPTION_INPUT_SELECT}', '{INPUT_SELECT_ATTR_OPTIONS}') }}}}",
                        "select_option": [
                            {
                                "service": "input_select.select_option",
                                "data_template": {
                                    "entity_id": _OPTION_INPUT_SELECT,
                                    "option": "{{ option }}",
                                },
                            },
                            {
                                "service": "test.automation",
                                "data_template": {
                                    "action": "select_option",
                                    "caller": "{{ this.entity_id }}",
                                    "option": "{{ option }}",
                                },
                            },
                        ],
                        "optimistic": True,
                        "unique_id": "a",
                    },
                }
            },
        )

    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    ent_reg = async_get(hass)
    entry = ent_reg.async_get(_TEST_SELECT)
    assert entry
    assert entry.unique_id == "b-a"

    _verify(hass, "a", ["a", "b"])

    await hass.services.async_call(
        INPUT_SELECT_DOMAIN,
        INPUT_SELECT_SERVICE_SELECT_OPTION,
        {CONF_ENTITY_ID: _OPTION_INPUT_SELECT, INPUT_SELECT_ATTR_OPTION: "b"},
        blocking=True,
    )
    await hass.async_block_till_done()
    _verify(hass, "b", ["a", "b"])

    await hass.services.async_call(
        INPUT_SELECT_DOMAIN,
        SERVICE_SET_OPTIONS,
        {
            CONF_ENTITY_ID: _OPTION_INPUT_SELECT,
            INPUT_SELECT_ATTR_OPTIONS: ["a", "b", "c"],
        },
        blocking=True,
    )
    await hass.async_block_till_done()
    _verify(hass, "b", ["a", "b", "c"])

    await hass.services.async_call(
        SELECT_DOMAIN,
        SELECT_SERVICE_SELECT_OPTION,
        {CONF_ENTITY_ID: _TEST_SELECT, SELECT_ATTR_OPTION: "c"},
        blocking=True,
    )
    _verify(hass, "c", ["a", "b", "c"])

    # Check this variable can be used in set_value script
    assert len(calls) == 1
    assert calls[-1].data["action"] == "select_option"
    assert calls[-1].data["caller"] == _TEST_SELECT
    assert calls[-1].data["option"] == "c"


async def test_trigger_select(hass):
    """Test trigger based template select."""
    events = async_capture_events(hass, "test_number_event")
    assert await setup.async_setup_component(
        hass,
        "template",
        {
            "template": [
                {"invalid": "config"},
                # Config after invalid should still be set up
                {
                    "unique_id": "listening-test-event",
                    "trigger": {"platform": "event", "event_type": "test_event"},
                    "select": [
                        {
                            "name": "Hello Name",
                            "unique_id": "hello_name-id",
                            "state": "{{ trigger.event.data.beer }}",
                            "options": "{{ trigger.event.data.beers }}",
                            "select_option": {"event": "test_number_event"},
                            "optimistic": True,
                        },
                    ],
                },
            ],
        },
    )

    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    state = hass.states.get("select.hello_name")
    assert state is not None
    assert state.state == STATE_UNKNOWN

    context = Context()
    hass.bus.async_fire(
        "test_event", {"beer": "duff", "beers": ["duff", "alamo"]}, context=context
    )
    await hass.async_block_till_done()

    state = hass.states.get("select.hello_name")
    assert state is not None
    assert state.state == "duff"
    assert state.attributes["options"] == ["duff", "alamo"]

    await hass.services.async_call(
        SELECT_DOMAIN,
        SELECT_SERVICE_SELECT_OPTION,
        {CONF_ENTITY_ID: "select.hello_name", SELECT_ATTR_OPTION: "alamo"},
        blocking=True,
    )
    assert len(events) == 1
    assert events[0].event_type == "test_number_event"


def _verify(hass, expected_current_option, expected_options, entity_name=_TEST_SELECT):
    """Verify select's state."""
    state = hass.states.get(entity_name)
    attributes = state.attributes
    assert state.state == str(expected_current_option)
    assert attributes.get(SELECT_ATTR_OPTIONS) == expected_options


async def test_template_icon_with_entities(hass):
    """Test templates with values from other entities."""
    with assert_setup_component(1, "input_select"):
        assert await setup.async_setup_component(
            hass,
            "input_select",
            {
                "input_select": {
                    "option": {
                        "options": ["a", "b"],
                        "initial": "a",
                        "name": "Option",
                    },
                }
            },
        )

    with assert_setup_component(1, "template"):
        assert await setup.async_setup_component(
            hass,
            "template",
            {
                "template": {
                    "unique_id": "b",
                    "select": {
                        "state": f"{{{{ states('{_OPTION_INPUT_SELECT}') }}}}",
                        "options": f"{{{{ state_attr('{_OPTION_INPUT_SELECT}', '{INPUT_SELECT_ATTR_OPTIONS}') }}}}",
                        "select_option": {
                            "service": "input_select.select_option",
                            "data": {
                                "entity_id": _OPTION_INPUT_SELECT,
                                "option": "{{ option }}",
                            },
                        },
                        "optimistic": True,
                        "unique_id": "a",
                        "icon": f"{{% if (states('{_OPTION_INPUT_SELECT}') == 'a') %}}mdi:greater{{% else %}}mdi:less{{% endif %}}",
                    },
                }
            },
        )

    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    state = hass.states.get(_TEST_SELECT)
    assert state.state == "a"
    assert state.attributes[ATTR_ICON] == "mdi:greater"

    await hass.services.async_call(
        INPUT_SELECT_DOMAIN,
        INPUT_SELECT_SERVICE_SELECT_OPTION,
        {CONF_ENTITY_ID: _OPTION_INPUT_SELECT, INPUT_SELECT_ATTR_OPTION: "b"},
        blocking=True,
    )
    await hass.async_block_till_done()

    state = hass.states.get(_TEST_SELECT)
    assert state.state == "b"
    assert state.attributes[ATTR_ICON] == "mdi:less"


async def test_template_icon_with_trigger(hass):
    """Test trigger based template select."""
    with assert_setup_component(1, "input_select"):
        assert await setup.async_setup_component(
            hass,
            "input_select",
            {
                "input_select": {
                    "option": {
                        "options": ["a", "b"],
                        "initial": "a",
                        "name": "Option",
                    },
                }
            },
        )

    assert await setup.async_setup_component(
        hass,
        "template",
        {
            "template": {
                "trigger": {"platform": "state", "entity_id": _OPTION_INPUT_SELECT},
                "select": {
                    "unique_id": "b",
                    "state": "{{ trigger.to_state.state }}",
                    "options": f"{{{{ state_attr('{_OPTION_INPUT_SELECT}', '{INPUT_SELECT_ATTR_OPTIONS}') }}}}",
                    "select_option": {
                        "service": "input_select.select_option",
                        "data": {
                            "entity_id": _OPTION_INPUT_SELECT,
                            "option": "{{ option }}",
                        },
                    },
                    "optimistic": True,
                    "icon": "{% if (trigger.to_state.state or '') == 'a' %}mdi:greater{% else %}mdi:less{% endif %}",
                },
            },
        },
    )

    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    await hass.services.async_call(
        INPUT_SELECT_DOMAIN,
        INPUT_SELECT_SERVICE_SELECT_OPTION,
        {CONF_ENTITY_ID: _OPTION_INPUT_SELECT, INPUT_SELECT_ATTR_OPTION: "b"},
        blocking=True,
    )
    await hass.async_block_till_done()

    state = hass.states.get(_TEST_SELECT)
    assert state is not None
    assert state.state == "b"
    assert state.attributes[ATTR_ICON] == "mdi:less"

    await hass.services.async_call(
        INPUT_SELECT_DOMAIN,
        INPUT_SELECT_SERVICE_SELECT_OPTION,
        {CONF_ENTITY_ID: _OPTION_INPUT_SELECT, INPUT_SELECT_ATTR_OPTION: "a"},
        blocking=True,
    )
    await hass.async_block_till_done()

    state = hass.states.get(_TEST_SELECT)
    assert state.state == "a"
    assert state.attributes[ATTR_ICON] == "mdi:greater"