"""Tests for the Config Entry Flow helper."""
from unittest.mock import patch, Mock

import pytest

from homeassistant import config_entries, data_entry_flow, setup
from homeassistant.helpers import config_entry_flow
from tests.common import (
    MockConfigEntry,
    MockModule,
    mock_coro,
    mock_integration,
    mock_entity_platform,
)


@pytest.fixture
def discovery_flow_conf(hass):
    """Register a handler."""
    handler_conf = {"discovered": False}

    async def has_discovered_devices(hass):
        """Mock if we have discovered devices."""
        return handler_conf["discovered"]

    with patch.dict(config_entries.HANDLERS):
        config_entry_flow.register_discovery_flow(
            "test", "Test", has_discovered_devices, config_entries.CONN_CLASS_LOCAL_POLL
        )
        yield handler_conf


@pytest.fixture
def webhook_flow_conf(hass):
    """Register a handler."""
    with patch.dict(config_entries.HANDLERS):
        config_entry_flow.register_webhook_flow("test_single", "Test Single", {}, False)
        config_entry_flow.register_webhook_flow(
            "test_multiple", "Test Multiple", {}, True
        )
        yield {}


async def test_single_entry_allowed(hass, discovery_flow_conf):
    """Test only a single entry is allowed."""
    flow = config_entries.HANDLERS["test"]()
    flow.hass = hass

    MockConfigEntry(domain="test").add_to_hass(hass)
    result = await flow.async_step_user()

    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == "single_instance_allowed"


async def test_user_no_devices_found(hass, discovery_flow_conf):
    """Test if no devices found."""
    flow = config_entries.HANDLERS["test"]()
    flow.hass = hass
    flow.context = {"source": config_entries.SOURCE_USER}
    result = await flow.async_step_confirm(user_input={})

    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == "no_devices_found"


async def test_user_has_confirmation(hass, discovery_flow_conf):
    """Test user requires no confirmation to setup."""
    flow = config_entries.HANDLERS["test"]()
    flow.hass = hass
    discovery_flow_conf["discovered"] = True

    result = await flow.async_step_user()

    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM


@pytest.mark.parametrize("source", ["discovery", "ssdp", "zeroconf"])
async def test_discovery_single_instance(hass, discovery_flow_conf, source):
    """Test we not allow duplicates."""
    flow = config_entries.HANDLERS["test"]()
    flow.hass = hass

    MockConfigEntry(domain="test").add_to_hass(hass)
    result = await getattr(flow, "async_step_{}".format(source))({})

    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == "single_instance_allowed"


@pytest.mark.parametrize("source", ["discovery", "ssdp", "zeroconf"])
async def test_discovery_confirmation(hass, discovery_flow_conf, source):
    """Test we ask for confirmation via discovery."""
    flow = config_entries.HANDLERS["test"]()
    flow.hass = hass

    result = await getattr(flow, "async_step_{}".format(source))({})

    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "confirm"

    result = await flow.async_step_confirm({})
    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY


async def test_multiple_discoveries(hass, discovery_flow_conf):
    """Test we only create one instance for multiple discoveries."""
    mock_entity_platform(hass, "config_flow.test", None)

    result = await hass.config_entries.flow.async_init(
        "test", context={"source": config_entries.SOURCE_DISCOVERY}, data={}
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM

    # Second discovery
    result = await hass.config_entries.flow.async_init(
        "test", context={"source": config_entries.SOURCE_DISCOVERY}, data={}
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT


async def test_only_one_in_progress(hass, discovery_flow_conf):
    """Test a user initialized one will finish and cancel discovered one."""
    mock_entity_platform(hass, "config_flow.test", None)

    # Discovery starts flow
    result = await hass.config_entries.flow.async_init(
        "test", context={"source": config_entries.SOURCE_DISCOVERY}, data={}
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM

    # User starts flow
    result = await hass.config_entries.flow.async_init(
        "test", context={"source": config_entries.SOURCE_USER}, data={}
    )

    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM

    # Discovery flow has not been aborted
    assert len(hass.config_entries.flow.async_progress()) == 2

    # Discovery should be aborted once user confirms
    result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
    assert len(hass.config_entries.flow.async_progress()) == 0


async def test_import_no_confirmation(hass, discovery_flow_conf):
    """Test import requires no confirmation to set up."""
    flow = config_entries.HANDLERS["test"]()
    flow.hass = hass
    discovery_flow_conf["discovered"] = True

    result = await flow.async_step_import(None)
    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY


async def test_import_single_instance(hass, discovery_flow_conf):
    """Test import doesn't create second instance."""
    flow = config_entries.HANDLERS["test"]()
    flow.hass = hass
    discovery_flow_conf["discovered"] = True
    MockConfigEntry(domain="test").add_to_hass(hass)

    result = await flow.async_step_import(None)
    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT


async def test_webhook_single_entry_allowed(hass, webhook_flow_conf):
    """Test only a single entry is allowed."""
    flow = config_entries.HANDLERS["test_single"]()
    flow.hass = hass

    MockConfigEntry(domain="test_single").add_to_hass(hass)
    result = await flow.async_step_user()

    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == "one_instance_allowed"


async def test_webhook_multiple_entries_allowed(hass, webhook_flow_conf):
    """Test multiple entries are allowed when specified."""
    flow = config_entries.HANDLERS["test_multiple"]()
    flow.hass = hass

    MockConfigEntry(domain="test_multiple").add_to_hass(hass)
    hass.config.api = Mock(base_url="http://example.com")

    result = await flow.async_step_user()
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM


async def test_webhook_config_flow_registers_webhook(hass, webhook_flow_conf):
    """Test setting up an entry creates a webhook."""
    flow = config_entries.HANDLERS["test_single"]()
    flow.hass = hass

    hass.config.api = Mock(base_url="http://example.com")
    result = await flow.async_step_user(user_input={})

    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
    assert result["data"]["webhook_id"] is not None


async def test_webhook_create_cloudhook(hass, webhook_flow_conf):
    """Test only a single entry is allowed."""
    assert await setup.async_setup_component(hass, "cloud", {})

    async_setup_entry = Mock(return_value=mock_coro(True))
    async_unload_entry = Mock(return_value=mock_coro(True))

    mock_integration(
        hass,
        MockModule(
            "test_single",
            async_setup_entry=async_setup_entry,
            async_unload_entry=async_unload_entry,
            async_remove_entry=config_entry_flow.webhook_async_remove_entry,
        ),
    )
    mock_entity_platform(hass, "config_flow.test_single", None)

    result = await hass.config_entries.flow.async_init(
        "test_single", context={"source": config_entries.SOURCE_USER}
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM

    coro = mock_coro({"cloudhook_url": "https://example.com"})

    with patch(
        "hass_nabucasa.cloudhooks.Cloudhooks.async_create", return_value=coro
    ) as mock_create, patch(
        "homeassistant.components.cloud.async_active_subscription", return_value=True
    ), patch(
        "homeassistant.components.cloud.async_is_logged_in", return_value=True
    ):

        result = await hass.config_entries.flow.async_configure(result["flow_id"], {})

    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
    assert result["description_placeholders"]["webhook_url"] == "https://example.com"
    assert len(mock_create.mock_calls) == 1
    assert len(async_setup_entry.mock_calls) == 1

    with patch(
        "hass_nabucasa.cloudhooks.Cloudhooks.async_delete", return_value=coro
    ) as mock_delete:

        result = await hass.config_entries.async_remove(result["result"].entry_id)

    assert len(mock_delete.mock_calls) == 1
    assert result["require_restart"] is False
