"""Test the Xiaomi Miio config flow."""
from unittest.mock import Mock, patch

from construct.core import ChecksumError
from micloud.micloudexception import MiCloudAccessDenied
from miio import DeviceException
import pytest

from homeassistant import config_entries, data_entry_flow
from homeassistant.components import zeroconf
from homeassistant.components.xiaomi_miio import const
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_TOKEN

from . import TEST_MAC

from tests.common import MockConfigEntry

ZEROCONF_NAME = "name"
ZEROCONF_PROP = "properties"
ZEROCONF_MAC = "mac"

TEST_HOST = "1.2.3.4"
TEST_HOST2 = "5.6.7.8"
TEST_CLOUD_USER = "username"
TEST_CLOUD_PASS = "password"
TEST_CLOUD_COUNTRY = "cn"
TEST_TOKEN = "12345678901234567890123456789012"
TEST_NAME = "Test_Gateway"
TEST_NAME2 = "Test_Gateway_2"
TEST_MODEL = const.MODELS_GATEWAY[0]
TEST_MAC2 = "mn:op:qr:st:uv:wx"
TEST_MAC_DEVICE = "abcdefghijkl"
TEST_MAC_DEVICE2 = "mnopqrstuvwx"
TEST_GATEWAY_ID = TEST_MAC
TEST_HARDWARE_VERSION = "AB123"
TEST_FIRMWARE_VERSION = "1.2.3_456"
TEST_ZEROCONF_NAME = "lumi-gateway-v3_miio12345678._miio._udp.local."
TEST_CLOUD_DEVICES_1 = [
    {
        "parent_id": None,
        "name": TEST_NAME,
        "model": TEST_MODEL,
        "localip": TEST_HOST,
        "mac": TEST_MAC_DEVICE,
        "token": TEST_TOKEN,
    }
]
TEST_CLOUD_DEVICES_2 = [
    {
        "parent_id": None,
        "name": TEST_NAME,
        "model": TEST_MODEL,
        "localip": TEST_HOST,
        "mac": TEST_MAC_DEVICE,
        "token": TEST_TOKEN,
    },
    {
        "parent_id": None,
        "name": TEST_NAME2,
        "model": TEST_MODEL,
        "localip": TEST_HOST2,
        "mac": TEST_MAC_DEVICE2,
        "token": TEST_TOKEN,
    },
]


@pytest.fixture(name="xiaomi_miio_connect", autouse=True)
def xiaomi_miio_connect_fixture():
    """Mock denonavr connection and entry setup."""
    mock_info = get_mock_info()

    with patch(
        "homeassistant.components.xiaomi_miio.device.Device.info",
        return_value=mock_info,
    ), patch(
        "homeassistant.components.xiaomi_miio.config_flow.MiCloud.login",
        return_value=True,
    ), patch(
        "homeassistant.components.xiaomi_miio.config_flow.MiCloud.get_devices",
        return_value=TEST_CLOUD_DEVICES_1,
    ), patch(
        "homeassistant.components.xiaomi_miio.async_setup_entry", return_value=True
    ), patch(
        "homeassistant.components.xiaomi_miio.async_unload_entry", return_value=True
    ):
        yield


def get_mock_info(
    model=TEST_MODEL,
    mac_address=TEST_MAC,
    hardware_version=TEST_HARDWARE_VERSION,
    firmware_version=TEST_FIRMWARE_VERSION,
):
    """Return a mock gateway info instance."""
    gateway_info = Mock()
    gateway_info.model = model
    gateway_info.mac_address = mac_address
    gateway_info.hardware_version = hardware_version
    gateway_info.firmware_version = firmware_version

    return gateway_info


async def test_config_flow_step_gateway_connect_error(hass):
    """Test config flow, gateway connection error."""
    result = await hass.config_entries.flow.async_init(
        const.DOMAIN, context={"source": config_entries.SOURCE_USER}
    )

    assert result["type"] == "form"
    assert result["step_id"] == "cloud"
    assert result["errors"] == {}

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

    assert result["type"] == "form"
    assert result["step_id"] == "manual"
    assert result["errors"] == {}

    with patch(
        "homeassistant.components.xiaomi_miio.device.Device.info",
        side_effect=DeviceException({}),
    ):
        result = await hass.config_entries.flow.async_configure(
            result["flow_id"],
            {CONF_HOST: TEST_HOST, CONF_TOKEN: TEST_TOKEN},
        )

    assert result["type"] == "form"
    assert result["step_id"] == "connect"
    assert result["errors"] == {"base": "cannot_connect"}


async def test_config_flow_gateway_success(hass):
    """Test a successful config flow."""
    result = await hass.config_entries.flow.async_init(
        const.DOMAIN, context={"source": config_entries.SOURCE_USER}
    )

    assert result["type"] == "form"
    assert result["step_id"] == "cloud"
    assert result["errors"] == {}

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

    assert result["type"] == "form"
    assert result["step_id"] == "manual"
    assert result["errors"] == {}

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        {CONF_HOST: TEST_HOST, CONF_TOKEN: TEST_TOKEN},
    )

    assert result["type"] == "create_entry"
    assert result["title"] == TEST_MODEL
    assert result["data"] == {
        const.CONF_FLOW_TYPE: const.CONF_GATEWAY,
        const.CONF_CLOUD_USERNAME: None,
        const.CONF_CLOUD_PASSWORD: None,
        const.CONF_CLOUD_COUNTRY: None,
        CONF_HOST: TEST_HOST,
        CONF_TOKEN: TEST_TOKEN,
        const.CONF_MODEL: TEST_MODEL,
        const.CONF_MAC: TEST_MAC,
    }


async def test_config_flow_gateway_cloud_success(hass):
    """Test a successful config flow using cloud."""
    result = await hass.config_entries.flow.async_init(
        const.DOMAIN, context={"source": config_entries.SOURCE_USER}
    )

    assert result["type"] == "form"
    assert result["step_id"] == "cloud"
    assert result["errors"] == {}

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        {
            const.CONF_CLOUD_USERNAME: TEST_CLOUD_USER,
            const.CONF_CLOUD_PASSWORD: TEST_CLOUD_PASS,
            const.CONF_CLOUD_COUNTRY: TEST_CLOUD_COUNTRY,
        },
    )

    assert result["type"] == "create_entry"
    assert result["title"] == TEST_NAME
    assert result["data"] == {
        const.CONF_FLOW_TYPE: const.CONF_GATEWAY,
        const.CONF_CLOUD_USERNAME: TEST_CLOUD_USER,
        const.CONF_CLOUD_PASSWORD: TEST_CLOUD_PASS,
        const.CONF_CLOUD_COUNTRY: TEST_CLOUD_COUNTRY,
        CONF_HOST: TEST_HOST,
        CONF_TOKEN: TEST_TOKEN,
        const.CONF_MODEL: TEST_MODEL,
        const.CONF_MAC: TEST_MAC,
    }


async def test_config_flow_gateway_cloud_multiple_success(hass):
    """Test a successful config flow using cloud with multiple devices."""
    result = await hass.config_entries.flow.async_init(
        const.DOMAIN, context={"source": config_entries.SOURCE_USER}
    )

    assert result["type"] == "form"
    assert result["step_id"] == "cloud"
    assert result["errors"] == {}

    with patch(
        "homeassistant.components.xiaomi_miio.config_flow.MiCloud.get_devices",
        return_value=TEST_CLOUD_DEVICES_2,
    ):
        result = await hass.config_entries.flow.async_configure(
            result["flow_id"],
            {
                const.CONF_CLOUD_USERNAME: TEST_CLOUD_USER,
                const.CONF_CLOUD_PASSWORD: TEST_CLOUD_PASS,
                const.CONF_CLOUD_COUNTRY: TEST_CLOUD_COUNTRY,
            },
        )

    assert result["type"] == "form"
    assert result["step_id"] == "select"
    assert result["errors"] == {}

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        {"select_device": f"{TEST_NAME2} - {TEST_MODEL}"},
    )

    assert result["type"] == "create_entry"
    assert result["title"] == TEST_NAME2
    assert result["data"] == {
        const.CONF_FLOW_TYPE: const.CONF_GATEWAY,
        const.CONF_CLOUD_USERNAME: TEST_CLOUD_USER,
        const.CONF_CLOUD_PASSWORD: TEST_CLOUD_PASS,
        const.CONF_CLOUD_COUNTRY: TEST_CLOUD_COUNTRY,
        CONF_HOST: TEST_HOST2,
        CONF_TOKEN: TEST_TOKEN,
        const.CONF_MODEL: TEST_MODEL,
        const.CONF_MAC: TEST_MAC2,
    }


async def test_config_flow_gateway_cloud_incomplete(hass):
    """Test a failed config flow using incomplete cloud credentials."""
    result = await hass.config_entries.flow.async_init(
        const.DOMAIN, context={"source": config_entries.SOURCE_USER}
    )

    assert result["type"] == "form"
    assert result["step_id"] == "cloud"
    assert result["errors"] == {}

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        {
            const.CONF_CLOUD_USERNAME: TEST_CLOUD_USER,
            const.CONF_CLOUD_COUNTRY: TEST_CLOUD_COUNTRY,
        },
    )

    assert result["type"] == "form"
    assert result["step_id"] == "cloud"
    assert result["errors"] == {"base": "cloud_credentials_incomplete"}


async def test_config_flow_gateway_cloud_login_error(hass):
    """Test a failed config flow using cloud login error."""