"""Test integration initialization."""
from unittest.mock import patch

from homeassistant import config_entries
from homeassistant.components.hassio.handler import HassioAPIError
from homeassistant.components.ozw import DOMAIN, PLATFORMS, const
from homeassistant.const import ATTR_RESTORED, STATE_UNAVAILABLE

from .common import setup_ozw

from tests.common import MockConfigEntry


async def test_init_entry(hass, generic_data):
    """Test setting up config entry."""
    await setup_ozw(hass, fixture=generic_data)

    # Verify integration + platform loaded.
    assert "ozw" in hass.config.components
    for platform in PLATFORMS:
        assert platform in hass.config.components, platform
        assert f"{platform}.{DOMAIN}" in hass.config.components, f"{platform}.{DOMAIN}"

    # Verify services registered
    assert hass.services.has_service(DOMAIN, const.SERVICE_ADD_NODE)
    assert hass.services.has_service(DOMAIN, const.SERVICE_REMOVE_NODE)


async def test_setup_entry_without_mqtt(hass):
    """Test setting up config entry without mqtt integration setup."""
    entry = MockConfigEntry(
        domain=DOMAIN,
        title="OpenZWave",
    )
    entry.add_to_hass(hass)

    assert not await hass.config_entries.async_setup(entry.entry_id)


async def test_publish_without_mqtt(hass, caplog):
    """Test publish without mqtt integration setup."""
    with patch("homeassistant.components.ozw.OZWOptions") as ozw_options:
        await setup_ozw(hass)

        send_message = ozw_options.call_args[1]["send_message"]

        mqtt_entries = hass.config_entries.async_entries("mqtt")
        mqtt_entry = mqtt_entries[0]
        await hass.config_entries.async_remove(mqtt_entry.entry_id)
        await hass.async_block_till_done()

        assert not hass.config_entries.async_entries("mqtt")

        # Sending a message should not error with the MQTT integration not set up.
        send_message("test_topic", "test_payload")
        await hass.async_block_till_done()

    assert "MQTT integration is not set up" in caplog.text


async def test_unload_entry(hass, generic_data, switch_msg, caplog):
    """Test unload the config entry."""
    entry = MockConfigEntry(
        domain=DOMAIN,
        title="Z-Wave",
    )
    entry.add_to_hass(hass)
    assert entry.state is config_entries.ConfigEntryState.NOT_LOADED

    receive_message = await setup_ozw(hass, entry=entry, fixture=generic_data)

    assert entry.state is config_entries.ConfigEntryState.LOADED
    assert len(hass.states.async_entity_ids("switch")) == 1

    await hass.config_entries.async_unload(entry.entry_id)

    assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
    entities = hass.states.async_entity_ids("switch")
    assert len(entities) == 1
    for entity in entities:
        assert hass.states.get(entity).state == STATE_UNAVAILABLE
        assert hass.states.get(entity).attributes.get(ATTR_RESTORED)

    # Send a message for a switch from the broker to check that
    # all entity topic subscribers are unsubscribed.
    receive_message(switch_msg)
    await hass.async_block_till_done()

    assert len(hass.states.async_entity_ids("switch")) == 1
    for entity in entities:
        assert hass.states.get(entity).state == STATE_UNAVAILABLE
        assert hass.states.get(entity).attributes.get(ATTR_RESTORED)

    # Load the integration again and check that there are no errors when
    # adding the entities.
    # This asserts that we have unsubscribed the entity addition signals
    # when unloading the integration previously.
    await setup_ozw(hass, entry=entry, fixture=generic_data)
    await hass.async_block_till_done()

    assert entry.state is config_entries.ConfigEntryState.LOADED
    assert len(hass.states.async_entity_ids("switch")) == 1
    for record in caplog.records:
        assert record.levelname != "ERROR"


async def test_remove_entry(hass, stop_addon, uninstall_addon, caplog):
    """Test remove the config entry."""
    # test successful remove without created add-on
    entry = MockConfigEntry(
        domain=DOMAIN,
        title="Z-Wave",
        data={"integration_created_addon": False},
    )
    entry.add_to_hass(hass)
    assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
    assert len(hass.config_entries.async_entries(DOMAIN)) == 1

    await hass.config_entries.async_remove(entry.entry_id)

    assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
    assert len(hass.config_entries.async_entries(DOMAIN)) == 0

    # test successful remove with created add-on
    entry = MockConfigEntry(
        domain=DOMAIN,
        title="Z-Wave",
        data={"integration_created_addon": True},
    )
    entry.add_to_hass(hass)
    assert len(hass.config_entries.async_entries(DOMAIN)) == 1

    await hass.config_entries.async_remove(entry.entry_id)

    assert stop_addon.call_count == 1
    assert uninstall_addon.call_count == 1
    assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
    assert len(hass.config_entries.async_entries(DOMAIN)) == 0
    stop_addon.reset_mock()
    uninstall_addon.reset_mock()

    # test add-on stop failure
    entry.add_to_hass(hass)
    assert len(hass.config_entries.async_entries(DOMAIN)) == 1
    stop_addon.side_effect = HassioAPIError()

    await hass.config_entries.async_remove(entry.entry_id)

    assert stop_addon.call_count == 1
    assert uninstall_addon.call_count == 0
    assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
    assert len(hass.config_entries.async_entries(DOMAIN)) == 0
    assert "Failed to stop the OpenZWave add-on" in caplog.text
    stop_addon.side_effect = None
    stop_addon.reset_mock()
    uninstall_addon.reset_mock()

    # test add-on uninstall failure
    entry.add_to_hass(hass)
    assert len(hass.config_entries.async_entries(DOMAIN)) == 1
    uninstall_addon.side_effect = HassioAPIError()

    await hass.config_entries.async_remove(entry.entry_id)

    assert stop_addon.call_count == 1
    assert uninstall_addon.call_count == 1
    assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
    assert len(hass.config_entries.async_entries(DOMAIN)) == 0
    assert "Failed to uninstall the OpenZWave add-on" in caplog.text


async def test_setup_entry_with_addon(hass, get_addon_discovery_info):
    """Test set up entry using OpenZWave add-on."""
    entry = MockConfigEntry(
        domain=DOMAIN,
        title="OpenZWave",
        data={"use_addon": True},
    )
    entry.add_to_hass(hass)

    with patch("homeassistant.components.ozw.MQTTClient", autospec=True) as mock_client:
        assert await hass.config_entries.async_setup(entry.entry_id)
        await hass.async_block_till_done()

    assert mock_client.return_value.start_client.call_count == 1

    # Verify integration + platform loaded.
    assert "ozw" in hass.config.components
    for platform in PLATFORMS:
        assert platform in hass.config.components, platform
        assert f"{platform}.{DOMAIN}" in hass.config.components, f"{platform}.{DOMAIN}"

    # Verify services registered
    assert hass.services.has_service(DOMAIN, const.SERVICE_ADD_NODE)
    assert hass.services.has_service(DOMAIN, const.SERVICE_REMOVE_NODE)


async def test_setup_entry_without_addon_info(hass, get_addon_discovery_info):
    """Test set up entry using OpenZWave add-on but missing discovery info."""
    entry = MockConfigEntry(
        domain=DOMAIN,
        title="OpenZWave",
        data={"use_addon": True},
    )
    entry.add_to_hass(hass)

    get_addon_discovery_info.return_value = None

    with patch("homeassistant.components.ozw.MQTTClient", autospec=True) as mock_client:
        assert not await hass.config_entries.async_setup(entry.entry_id)

    assert mock_client.return_value.start_client.call_count == 0
    assert entry.state is config_entries.ConfigEntryState.SETUP_RETRY


async def test_unload_entry_with_addon(
    hass, get_addon_discovery_info, generic_data, switch_msg, caplog
):
    """Test unload the config entry using the OpenZWave add-on."""
    entry = MockConfigEntry(
        domain=DOMAIN,
        title="OpenZWave",
        data={"use_addon": True},
    )
    entry.add_to_hass(hass)

    assert entry.state is config_entries.ConfigEntryState.NOT_LOADED

    with patch("homeassistant.components.ozw.MQTTClient", autospec=True) as mock_client:
        assert await hass.config_entries.async_setup(entry.entry_id)
        await hass.async_block_till_done()

    assert mock_client.return_value.start_client.call_count == 1
    assert entry.state is config_entries.ConfigEntryState.LOADED

    await hass.config_entries.async_unload(entry.entry_id)

    assert entry.state is config_entries.ConfigEntryState.NOT_LOADED
