"""The tests for Shelly device triggers."""
import pytest

from homeassistant.components import automation
from homeassistant.components.device_automation import DeviceAutomationType
from homeassistant.components.device_automation.exceptions import (
    InvalidDeviceAutomationConfig,
)
from homeassistant.components.shelly.const import (
    ATTR_CHANNEL,
    ATTR_CLICK_TYPE,
    CONF_SUBTYPE,
    DOMAIN,
    EVENT_SHELLY_CLICK,
)
from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_PLATFORM, CONF_TYPE
from homeassistant.helpers.device_registry import (
    CONNECTION_NETWORK_MAC,
    async_entries_for_config_entry,
    async_get as async_get_dev_reg,
)
from homeassistant.setup import async_setup_component

from . import init_integration

from tests.common import (
    MockConfigEntry,
    assert_lists_same,
    async_get_device_automations,
)


@pytest.mark.parametrize(
    ("button_type", "is_valid"),
    [
        ("momentary", True),
        ("momentary_on_release", True),
        ("detached", True),
        ("toggle", False),
    ],
)
async def test_get_triggers_block_device(
    hass, mock_block_device, monkeypatch, button_type, is_valid
):
    """Test we get the expected triggers from a shelly block device."""
    monkeypatch.setitem(
        mock_block_device.settings,
        "relays",
        [
            {"btn_type": button_type},
            {"btn_type": "toggle"},
        ],
    )
    entry = await init_integration(hass, 1)
    dev_reg = async_get_dev_reg(hass)
    device = async_entries_for_config_entry(dev_reg, entry.entry_id)[0]

    expected_triggers = []
    if is_valid:
        expected_triggers = [
            {
                CONF_PLATFORM: "device",
                CONF_DEVICE_ID: device.id,
                CONF_DOMAIN: DOMAIN,
                CONF_TYPE: type_,
                CONF_SUBTYPE: "button1",
                "metadata": {},
            }
            for type_ in ["single", "long"]
        ]

    triggers = await async_get_device_automations(
        hass, DeviceAutomationType.TRIGGER, device.id
    )
    triggers = [value for value in triggers if value["domain"] == DOMAIN]
    assert_lists_same(triggers, expected_triggers)


async def test_get_triggers_rpc_device(hass, mock_rpc_device):
    """Test we get the expected triggers from a shelly RPC device."""
    entry = await init_integration(hass, 2)
    dev_reg = async_get_dev_reg(hass)
    device = async_entries_for_config_entry(dev_reg, entry.entry_id)[0]

    expected_triggers = [
        {
            CONF_PLATFORM: "device",
            CONF_DEVICE_ID: device.id,
            CONF_DOMAIN: DOMAIN,
            CONF_TYPE: type,
            CONF_SUBTYPE: "button1",
            "metadata": {},
        }
        for type in [
            "btn_down",
            "btn_up",
            "single_push",
            "double_push",
            "triple_push",
            "long_push",
        ]
    ]

    triggers = await async_get_device_automations(
        hass, DeviceAutomationType.TRIGGER, device.id
    )
    triggers = [value for value in triggers if value["domain"] == DOMAIN]
    assert_lists_same(triggers, expected_triggers)


async def test_get_triggers_button(hass, mock_block_device):
    """Test we get the expected triggers from a shelly button."""
    entry = await init_integration(hass, 1, model="SHBTN-1")
    dev_reg = async_get_dev_reg(hass)
    device = async_entries_for_config_entry(dev_reg, entry.entry_id)[0]

    expected_triggers = [
        {
            CONF_PLATFORM: "device",
            CONF_DEVICE_ID: device.id,
            CONF_DOMAIN: DOMAIN,
            CONF_TYPE: type,
            CONF_SUBTYPE: "button",
            "metadata": {},
        }
        for type in ["single", "double", "triple", "long"]
    ]

    triggers = await async_get_device_automations(
        hass, DeviceAutomationType.TRIGGER, device.id
    )
    triggers = [value for value in triggers if value["domain"] == DOMAIN]
    assert_lists_same(triggers, expected_triggers)


async def test_get_triggers_non_initialized_devices(
    hass, mock_block_device, monkeypatch
):
    """Test we get the empty triggers for non-initialized devices."""
    monkeypatch.setattr(mock_block_device, "initialized", False)
    entry = await init_integration(hass, 1)
    dev_reg = async_get_dev_reg(hass)
    device = async_entries_for_config_entry(dev_reg, entry.entry_id)[0]

    expected_triggers = []

    triggers = await async_get_device_automations(
        hass, DeviceAutomationType.TRIGGER, device.id
    )
    triggers = [value for value in triggers if value["domain"] == DOMAIN]
    assert_lists_same(triggers, expected_triggers)


async def test_get_triggers_for_invalid_device_id(hass, device_reg, mock_block_device):
    """Test error raised for invalid shelly device_id."""
    await init_integration(hass, 1)
    config_entry = MockConfigEntry(domain=DOMAIN, data={})
    config_entry.add_to_hass(hass)
    invalid_device = device_reg.async_get_or_create(
        config_entry_id=config_entry.entry_id,
        connections={(CONNECTION_NETWORK_MAC, "12:34:56:AB:CD:EF")},
    )

    with pytest.raises(InvalidDeviceAutomationConfig):
        await async_get_device_automations(
            hass, DeviceAutomationType.TRIGGER, invalid_device.id
        )


async def test_if_fires_on_click_event_block_device(hass, calls, mock_block_device):
    """Test for click_event trigger firing for block device."""
    entry = await init_integration(hass, 1)
    dev_reg = async_get_dev_reg(hass)
    device = async_entries_for_config_entry(dev_reg, entry.entry_id)[0]

    assert await async_setup_component(
        hass,
        automation.DOMAIN,
        {
            automation.DOMAIN: [
                {
                    "trigger": {
                        CONF_PLATFORM: "device",
                        CONF_DOMAIN: DOMAIN,
                        CONF_DEVICE_ID: device.id,
                        CONF_TYPE: "single",
                        CONF_SUBTYPE: "button1",
                    },
                    "action": {
                        "service": "test.automation",
                        "data_template": {"some": "test_trigger_single_click"},
                    },
                },
            ]
        },
    )

    message = {
        CONF_DEVICE_ID: device.id,
        ATTR_CLICK_TYPE: "single",
        ATTR_CHANNEL: 1,
    }
    hass.bus.async_fire(EVENT_SHELLY_CLICK, message)
    await hass.async_block_till_done()

    assert len(calls) == 1
    assert calls[0].data["some"] == "test_trigger_single_click"


async def test_if_fires_on_click_event_rpc_device(hass, calls, mock_rpc_device):
    """Test for click_event trigger firing for rpc device."""
    entry = await init_integration(hass, 2)
    dev_reg = async_get_dev_reg(hass)
    device = async_entries_for_config_entry(dev_reg, entry.entry_id)[0]

    assert await async_setup_component(
        hass,
        automation.DOMAIN,
        {
            automation.DOMAIN: [
                {
                    "trigger": {
                        CONF_PLATFORM: "device",
                        CONF_DOMAIN: DOMAIN,
                        CONF_DEVICE_ID: device.id,
                        CONF_TYPE: "single_push",
                        CONF_SUBTYPE: "button1",
                    },
                    "action": {
                        "service": "test.automation",
                        "data_template": {"some": "test_trigger_single_push"},
                    },
                },
            ]
        },
    )

    message = {
        CONF_DEVICE_ID: device.id,
        ATTR_CLICK_TYPE: "single_push",
        ATTR_CHANNEL: 1,
    }
    hass.bus.async_fire(EVENT_SHELLY_CLICK, message)
    await hass.async_block_till_done()

    assert len(calls) == 1
    assert calls[0].data["some"] == "test_trigger_single_push"


async def test_validate_trigger_block_device_not_ready(
    hass, calls, mock_block_device, monkeypatch
):
    """Test validate trigger config when block device is not ready."""
    monkeypatch.setattr(mock_block_device, "initialized", False)
    entry = await init_integration(hass, 1)
    dev_reg = async_get_dev_reg(hass)
    device = async_entries_for_config_entry(dev_reg, entry.entry_id)[0]

    assert await async_setup_component(
        hass,
        automation.DOMAIN,
        {
            automation.DOMAIN: [
                {
                    "trigger": {
                        CONF_PLATFORM: "device",
                        CONF_DOMAIN: DOMAIN,
                        CONF_DEVICE_ID: device.id,
                        CONF_TYPE: "single",
                        CONF_SUBTYPE: "button1",
                    },
                    "action": {
                        "service": "test.automation",
                        "data_template": {"some": "test_trigger_single_click"},
                    },
                },
            ]
        },
    )
    message = {
        CONF_DEVICE_ID: device.id,
        ATTR_CLICK_TYPE: "single",
        ATTR_CHANNEL: 1,
    }
    hass.bus.async_fire(EVENT_SHELLY_CLICK, message)
    await hass.async_block_till_done()

    assert len(calls) == 1
    assert calls[0].data["some"] == "test_trigger_single_click"


async def test_validate_trigger_rpc_device_not_ready(
    hass, calls, mock_rpc_device, monkeypatch
):
    """Test validate trigger config when RPC device is not ready."""
    monkeypatch.setattr(mock_rpc_device, "initialized", False)
    entry = await init_integration(hass, 2)
    dev_reg = async_get_dev_reg(hass)
    device = async_entries_for_config_entry(dev_reg, entry.entry_id)[0]

    assert await async_setup_component(
        hass,
        automation.DOMAIN,
        {
            automation.DOMAIN: [
                {
                    "trigger": {
                        CONF_PLATFORM: "device",
                        CONF_DOMAIN: DOMAIN,
                        CONF_DEVICE_ID: device.id,
                        CONF_TYPE: "single_push",
                        CONF_SUBTYPE: "button1",
                    },
                    "action": {
                        "service": "test.automation",
                        "data_template": {"some": "test_trigger_single_push"},
                    },
                },
            ]
        },
    )
    message = {
        CONF_DEVICE_ID: device.id,
        ATTR_CLICK_TYPE: "single_push",
        ATTR_CHANNEL: 1,
    }
    hass.bus.async_fire(EVENT_SHELLY_CLICK, message)
    await hass.async_block_till_done()

    assert len(calls) == 1
    assert calls[0].data["some"] == "test_trigger_single_push"


async def test_validate_trigger_invalid_triggers(hass, mock_block_device, caplog):
    """Test for click_event with invalid triggers."""
    entry = await init_integration(hass, 1)
    dev_reg = async_get_dev_reg(hass)
    device = async_entries_for_config_entry(dev_reg, entry.entry_id)[0]

    assert await async_setup_component(
        hass,
        automation.DOMAIN,
        {
            automation.DOMAIN: [
                {
                    "trigger": {
                        CONF_PLATFORM: "device",
                        CONF_DOMAIN: DOMAIN,
                        CONF_DEVICE_ID: device.id,
                        CONF_TYPE: "single",
                        CONF_SUBTYPE: "button3",
                    },
                    "action": {
                        "service": "test.automation",
                        "data_template": {"some": "test_trigger_single_click"},
                    },
                },
            ]
        },
    )

    assert "Invalid (type,subtype): ('single', 'button3')" in caplog.text