"""Test entity_registry API."""
from collections import OrderedDict

import pytest

from homeassistant.helpers.entity_registry import RegistryEntry
from homeassistant.components.config import entity_registry
from tests.common import mock_registry, MockEntity, MockEntityPlatform


@pytest.fixture
def client(hass, hass_ws_client):
    """Fixture that can interact with the config manager API."""
    hass.loop.run_until_complete(entity_registry.async_setup(hass))
    yield hass.loop.run_until_complete(hass_ws_client(hass))


async def test_list_entities(hass, client):
    """Test list entries."""
    entities = OrderedDict()
    entities["test_domain.name"] = RegistryEntry(
        entity_id="test_domain.name",
        unique_id="1234",
        platform="test_platform",
        name="Hello World",
    )
    entities["test_domain.no_name"] = RegistryEntry(
        entity_id="test_domain.no_name", unique_id="6789", platform="test_platform"
    )

    mock_registry(hass, entities)

    await client.send_json({"id": 5, "type": "config/entity_registry/list"})
    msg = await client.receive_json()

    assert msg["result"] == [
        {
            "config_entry_id": None,
            "device_id": None,
            "disabled_by": None,
            "entity_id": "test_domain.name",
            "name": "Hello World",
            "platform": "test_platform",
        },
        {
            "config_entry_id": None,
            "device_id": None,
            "disabled_by": None,
            "entity_id": "test_domain.no_name",
            "name": None,
            "platform": "test_platform",
        },
    ]


async def test_get_entity(hass, client):
    """Test get entry."""
    mock_registry(
        hass,
        {
            "test_domain.name": RegistryEntry(
                entity_id="test_domain.name",
                unique_id="1234",
                platform="test_platform",
                name="Hello World",
            ),
            "test_domain.no_name": RegistryEntry(
                entity_id="test_domain.no_name",
                unique_id="6789",
                platform="test_platform",
            ),
        },
    )

    await client.send_json(
        {"id": 5, "type": "config/entity_registry/get", "entity_id": "test_domain.name"}
    )
    msg = await client.receive_json()

    assert msg["result"] == {
        "config_entry_id": None,
        "device_id": None,
        "disabled_by": None,
        "platform": "test_platform",
        "entity_id": "test_domain.name",
        "name": "Hello World",
    }

    await client.send_json(
        {
            "id": 6,
            "type": "config/entity_registry/get",
            "entity_id": "test_domain.no_name",
        }
    )
    msg = await client.receive_json()

    assert msg["result"] == {
        "config_entry_id": None,
        "device_id": None,
        "disabled_by": None,
        "platform": "test_platform",
        "entity_id": "test_domain.no_name",
        "name": None,
    }


async def test_update_entity(hass, client):
    """Test updating entity."""
    registry = mock_registry(
        hass,
        {
            "test_domain.world": RegistryEntry(
                entity_id="test_domain.world",
                unique_id="1234",
                # Using component.async_add_entities is equal to platform "domain"
                platform="test_platform",
                name="before update",
            )
        },
    )
    platform = MockEntityPlatform(hass)
    entity = MockEntity(unique_id="1234")
    await platform.async_add_entities([entity])

    state = hass.states.get("test_domain.world")
    assert state is not None
    assert state.name == "before update"

    # UPDATE NAME
    await client.send_json(
        {
            "id": 6,
            "type": "config/entity_registry/update",
            "entity_id": "test_domain.world",
            "name": "after update",
        }
    )

    msg = await client.receive_json()

    assert msg["result"] == {
        "config_entry_id": None,
        "device_id": None,
        "disabled_by": None,
        "platform": "test_platform",
        "entity_id": "test_domain.world",
        "name": "after update",
    }

    state = hass.states.get("test_domain.world")
    assert state.name == "after update"

    # UPDATE DISABLED_BY TO USER
    await client.send_json(
        {
            "id": 7,
            "type": "config/entity_registry/update",
            "entity_id": "test_domain.world",
            "disabled_by": "user",
        }
    )

    msg = await client.receive_json()

    assert hass.states.get("test_domain.world") is None
    assert registry.entities["test_domain.world"].disabled_by == "user"

    # UPDATE DISABLED_BY TO NONE
    await client.send_json(
        {
            "id": 8,
            "type": "config/entity_registry/update",
            "entity_id": "test_domain.world",
            "disabled_by": None,
        }
    )

    msg = await client.receive_json()

    assert msg["result"] == {
        "config_entry_id": None,
        "device_id": None,
        "disabled_by": None,
        "platform": "test_platform",
        "entity_id": "test_domain.world",
        "name": "after update",
    }


async def test_update_entity_no_changes(hass, client):
    """Test update entity with no changes."""
    mock_registry(
        hass,
        {
            "test_domain.world": RegistryEntry(
                entity_id="test_domain.world",
                unique_id="1234",
                # Using component.async_add_entities is equal to platform "domain"
                platform="test_platform",
                name="name of entity",
            )
        },
    )
    platform = MockEntityPlatform(hass)
    entity = MockEntity(unique_id="1234")
    await platform.async_add_entities([entity])

    state = hass.states.get("test_domain.world")
    assert state is not None
    assert state.name == "name of entity"

    await client.send_json(
        {
            "id": 6,
            "type": "config/entity_registry/update",
            "entity_id": "test_domain.world",
            "name": "name of entity",
        }
    )

    msg = await client.receive_json()

    assert msg["result"] == {
        "config_entry_id": None,
        "device_id": None,
        "disabled_by": None,
        "platform": "test_platform",
        "entity_id": "test_domain.world",
        "name": "name of entity",
    }

    state = hass.states.get("test_domain.world")
    assert state.name == "name of entity"


async def test_get_nonexisting_entity(client):
    """Test get entry with nonexisting entity."""
    await client.send_json(
        {
            "id": 6,
            "type": "config/entity_registry/get",
            "entity_id": "test_domain.no_name",
        }
    )
    msg = await client.receive_json()

    assert not msg["success"]


async def test_update_nonexisting_entity(client):
    """Test update a nonexisting entity."""
    await client.send_json(
        {
            "id": 6,
            "type": "config/entity_registry/update",
            "entity_id": "test_domain.no_name",
            "name": "new-name",
        }
    )
    msg = await client.receive_json()

    assert not msg["success"]


async def test_update_entity_id(hass, client):
    """Test update entity id."""
    mock_registry(
        hass,
        {
            "test_domain.world": RegistryEntry(
                entity_id="test_domain.world",
                unique_id="1234",
                # Using component.async_add_entities is equal to platform "domain"
                platform="test_platform",
            )
        },
    )
    platform = MockEntityPlatform(hass)
    entity = MockEntity(unique_id="1234")
    await platform.async_add_entities([entity])

    assert hass.states.get("test_domain.world") is not None

    await client.send_json(
        {
            "id": 6,
            "type": "config/entity_registry/update",
            "entity_id": "test_domain.world",
            "new_entity_id": "test_domain.planet",
        }
    )

    msg = await client.receive_json()

    assert msg["result"] == {
        "config_entry_id": None,
        "device_id": None,
        "disabled_by": None,
        "platform": "test_platform",
        "entity_id": "test_domain.planet",
        "name": None,
    }

    assert hass.states.get("test_domain.world") is None
    assert hass.states.get("test_domain.planet") is not None


async def test_remove_entity(hass, client):
    """Test removing entity."""
    registry = mock_registry(
        hass,
        {
            "test_domain.world": RegistryEntry(
                entity_id="test_domain.world",
                unique_id="1234",
                # Using component.async_add_entities is equal to platform "domain"
                platform="test_platform",
                name="before update",
            )
        },
    )

    await client.send_json(
        {
            "id": 6,
            "type": "config/entity_registry/remove",
            "entity_id": "test_domain.world",
        }
    )

    msg = await client.receive_json()

    assert msg["success"]
    assert len(registry.entities) == 0
