"""The tests for the Group Lock platform."""
from unittest.mock import patch

from homeassistant import config as hass_config
from homeassistant.components.demo import lock as demo_lock
from homeassistant.components.group import DOMAIN, SERVICE_RELOAD
from homeassistant.components.lock import (
    DOMAIN as LOCK_DOMAIN,
    SERVICE_LOCK,
    SERVICE_OPEN,
    SERVICE_UNLOCK,
)
from homeassistant.const import (
    ATTR_ENTITY_ID,
    STATE_JAMMED,
    STATE_LOCKED,
    STATE_LOCKING,
    STATE_UNAVAILABLE,
    STATE_UNKNOWN,
    STATE_UNLOCKED,
    STATE_UNLOCKING,
)
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component

from tests.common import get_fixture_path


async def test_default_state(hass):
    """Test lock group default state."""
    hass.states.async_set("lock.front", "locked")
    await async_setup_component(
        hass,
        LOCK_DOMAIN,
        {
            LOCK_DOMAIN: {
                "platform": DOMAIN,
                "entities": ["lock.front", "lock.back"],
                "name": "Door Group",
                "unique_id": "unique_identifier",
            }
        },
    )
    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    state = hass.states.get("lock.door_group")
    assert state is not None
    assert state.state == STATE_LOCKED
    assert state.attributes.get(ATTR_ENTITY_ID) == ["lock.front", "lock.back"]

    entity_registry = er.async_get(hass)
    entry = entity_registry.async_get("lock.door_group")
    assert entry
    assert entry.unique_id == "unique_identifier"


async def test_state_reporting(hass):
    """Test the state reporting."""
    await async_setup_component(
        hass,
        LOCK_DOMAIN,
        {
            LOCK_DOMAIN: {
                "platform": DOMAIN,
                "entities": ["lock.test1", "lock.test2"],
            }
        },
    )
    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    hass.states.async_set("lock.test1", STATE_LOCKED)
    hass.states.async_set("lock.test2", STATE_UNAVAILABLE)
    await hass.async_block_till_done()
    assert hass.states.get("lock.lock_group").state == STATE_UNKNOWN

    hass.states.async_set("lock.test1", STATE_LOCKED)
    hass.states.async_set("lock.test2", STATE_UNLOCKED)
    await hass.async_block_till_done()
    assert hass.states.get("lock.lock_group").state == STATE_UNLOCKED

    hass.states.async_set("lock.test1", STATE_LOCKED)
    hass.states.async_set("lock.test2", STATE_LOCKED)
    await hass.async_block_till_done()
    assert hass.states.get("lock.lock_group").state == STATE_LOCKED

    hass.states.async_set("lock.test1", STATE_UNLOCKED)
    hass.states.async_set("lock.test2", STATE_UNLOCKED)
    await hass.async_block_till_done()
    assert hass.states.get("lock.lock_group").state == STATE_UNLOCKED

    hass.states.async_set("lock.test1", STATE_UNLOCKED)
    hass.states.async_set("lock.test2", STATE_JAMMED)
    await hass.async_block_till_done()
    assert hass.states.get("lock.lock_group").state == STATE_JAMMED

    hass.states.async_set("lock.test1", STATE_LOCKED)
    hass.states.async_set("lock.test2", STATE_UNLOCKING)
    await hass.async_block_till_done()
    assert hass.states.get("lock.lock_group").state == STATE_UNLOCKING

    hass.states.async_set("lock.test1", STATE_UNLOCKED)
    hass.states.async_set("lock.test2", STATE_LOCKING)
    await hass.async_block_till_done()
    assert hass.states.get("lock.lock_group").state == STATE_LOCKING

    hass.states.async_set("lock.test1", STATE_UNAVAILABLE)
    hass.states.async_set("lock.test2", STATE_UNAVAILABLE)
    await hass.async_block_till_done()
    assert hass.states.get("lock.lock_group").state == STATE_UNAVAILABLE


@patch.object(demo_lock, "LOCK_UNLOCK_DELAY", 0)
async def test_service_calls(hass, enable_custom_integrations):
    """Test service calls."""
    await async_setup_component(
        hass,
        LOCK_DOMAIN,
        {
            LOCK_DOMAIN: [
                {"platform": "demo"},
                {
                    "platform": DOMAIN,
                    "entities": [
                        "lock.front_door",
                        "lock.kitchen_door",
                    ],
                },
            ]
        },
    )
    await hass.async_block_till_done()

    group_state = hass.states.get("lock.lock_group")
    assert group_state.state == STATE_UNLOCKED
    assert hass.states.get("lock.front_door").state == STATE_LOCKED
    assert hass.states.get("lock.kitchen_door").state == STATE_UNLOCKED

    await hass.services.async_call(
        LOCK_DOMAIN,
        SERVICE_OPEN,
        {ATTR_ENTITY_ID: "lock.lock_group"},
        blocking=True,
    )
    assert hass.states.get("lock.front_door").state == STATE_UNLOCKED
    assert hass.states.get("lock.kitchen_door").state == STATE_UNLOCKED

    await hass.services.async_call(
        LOCK_DOMAIN,
        SERVICE_LOCK,
        {ATTR_ENTITY_ID: "lock.lock_group"},
        blocking=True,
    )
    assert hass.states.get("lock.front_door").state == STATE_LOCKED
    assert hass.states.get("lock.kitchen_door").state == STATE_LOCKED

    await hass.services.async_call(
        LOCK_DOMAIN,
        SERVICE_UNLOCK,
        {ATTR_ENTITY_ID: "lock.lock_group"},
        blocking=True,
    )
    assert hass.states.get("lock.front_door").state == STATE_UNLOCKED
    assert hass.states.get("lock.kitchen_door").state == STATE_UNLOCKED


async def test_reload(hass):
    """Test the ability to reload locks."""
    await async_setup_component(
        hass,
        LOCK_DOMAIN,
        {
            LOCK_DOMAIN: [
                {"platform": "demo"},
                {
                    "platform": DOMAIN,
                    "entities": [
                        "lock.front_door",
                        "lock.kitchen_door",
                    ],
                },
            ]
        },
    )
    await hass.async_block_till_done()

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

    await hass.async_block_till_done()
    assert hass.states.get("lock.lock_group").state == STATE_UNLOCKED

    yaml_path = get_fixture_path("configuration.yaml", "group")
    with patch.object(hass_config, "YAML_CONFIG_FILE", yaml_path):
        await hass.services.async_call(
            DOMAIN,
            SERVICE_RELOAD,
            {},
            blocking=True,
        )
        await hass.async_block_till_done()

    assert hass.states.get("lock.lock_group") is None
    assert hass.states.get("lock.inside_locks_g") is not None
    assert hass.states.get("lock.outside_locks_g") is not None


async def test_reload_with_platform_not_setup(hass):
    """Test the ability to reload locks."""
    hass.states.async_set("lock.something", STATE_UNLOCKED)
    await async_setup_component(
        hass,
        LOCK_DOMAIN,
        {
            LOCK_DOMAIN: [
                {"platform": "demo"},
            ]
        },
    )
    assert await async_setup_component(
        hass,
        "group",
        {
            "group": {
                "group_zero": {"entities": "lock.something", "icon": "mdi:work"},
            }
        },
    )
    await hass.async_block_till_done()

    yaml_path = get_fixture_path("configuration.yaml", "group")
    with patch.object(hass_config, "YAML_CONFIG_FILE", yaml_path):
        await hass.services.async_call(
            DOMAIN,
            SERVICE_RELOAD,
            {},
            blocking=True,
        )
        await hass.async_block_till_done()

    assert hass.states.get("lock.lock_group") is None
    assert hass.states.get("lock.inside_locks_g") is not None
    assert hass.states.get("lock.outside_locks_g") is not None


async def test_reload_with_base_integration_platform_not_setup(hass):
    """Test the ability to reload locks."""
    assert await async_setup_component(
        hass,
        "group",
        {
            "group": {
                "group_zero": {"entities": "lock.something", "icon": "mdi:work"},
            }
        },
    )
    await hass.async_block_till_done()
    hass.states.async_set("lock.front_lock", STATE_LOCKED)
    hass.states.async_set("lock.back_lock", STATE_UNLOCKED)

    hass.states.async_set("lock.outside_lock", STATE_LOCKED)
    hass.states.async_set("lock.outside_lock_2", STATE_LOCKED)

    yaml_path = get_fixture_path("configuration.yaml", "group")
    with patch.object(hass_config, "YAML_CONFIG_FILE", yaml_path):
        await hass.services.async_call(
            DOMAIN,
            SERVICE_RELOAD,
            {},
            blocking=True,
        )
        await hass.async_block_till_done()

    assert hass.states.get("lock.lock_group") is None
    assert hass.states.get("lock.inside_locks_g") is not None
    assert hass.states.get("lock.outside_locks_g") is not None
    assert hass.states.get("lock.inside_locks_g").state == STATE_UNLOCKED
    assert hass.states.get("lock.outside_locks_g").state == STATE_LOCKED


@patch.object(demo_lock, "LOCK_UNLOCK_DELAY", 0)
async def test_nested_group(hass):
    """Test nested lock group."""
    await async_setup_component(
        hass,
        LOCK_DOMAIN,
        {
            LOCK_DOMAIN: [
                {"platform": "demo"},
                {
                    "platform": DOMAIN,
                    "entities": ["lock.some_group"],
                    "name": "Nested Group",
                },
                {
                    "platform": DOMAIN,
                    "entities": [
                        "lock.front_door",
                        "lock.kitchen_door",
                    ],
                    "name": "Some Group",
                },
            ]
        },
    )
    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    state = hass.states.get("lock.some_group")
    assert state is not None
    assert state.state == STATE_UNLOCKED
    assert state.attributes.get(ATTR_ENTITY_ID) == [
        "lock.front_door",
        "lock.kitchen_door",
    ]

    state = hass.states.get("lock.nested_group")
    assert state is not None
    assert state.state == STATE_UNLOCKED
    assert state.attributes.get(ATTR_ENTITY_ID) == ["lock.some_group"]

    # Test controlling the nested group
    await hass.services.async_call(
        LOCK_DOMAIN,
        SERVICE_LOCK,
        {ATTR_ENTITY_ID: "lock.nested_group"},
        blocking=True,
    )
    assert hass.states.get("lock.front_door").state == STATE_LOCKED
    assert hass.states.get("lock.kitchen_door").state == STATE_LOCKED
    assert hass.states.get("lock.some_group").state == STATE_LOCKED
    assert hass.states.get("lock.nested_group").state == STATE_LOCKED