"""Tests for the Netgear config flow."""
from unittest.mock import Mock, patch

from pynetgear import DEFAULT_USER
import pytest

from homeassistant import data_entry_flow
from homeassistant.components import ssdp
from homeassistant.components.netgear.const import (
    CONF_CONSIDER_HOME,
    DOMAIN,
    MODELS_PORT_5555,
    PORT_80,
    PORT_5555,
)
from homeassistant.config_entries import SOURCE_SSDP, SOURCE_USER
from homeassistant.const import (
    CONF_HOST,
    CONF_PASSWORD,
    CONF_PORT,
    CONF_SSL,
    CONF_USERNAME,
)

from tests.common import MockConfigEntry

URL = "http://routerlogin.net"
URL_SSL = "https://routerlogin.net"
SERIAL = "5ER1AL0000001"

ROUTER_INFOS = {
    "Description": "Netgear Smart Wizard 3.0, specification 1.6 version",
    "SignalStrength": "-4",
    "SmartAgentversion": "3.0",
    "FirewallVersion": "net-wall 2.0",
    "VPNVersion": None,
    "OthersoftwareVersion": "N/A",
    "Hardwareversion": "N/A",
    "Otherhardwareversion": "N/A",
    "FirstUseDate": "Sunday, 30 Sep 2007 01:10:03",
    "DeviceMode": "0",
    "ModelName": "RBR20",
    "SerialNumber": SERIAL,
    "Firmwareversion": "V2.3.5.26",
    "DeviceName": "Desk",
    "DeviceNameUserSet": "true",
    "FirmwareDLmethod": "HTTPS",
    "FirmwareLastUpdate": "2019_10.5_18:42:58",
    "FirmwareLastChecked": "2020_5.3_1:33:0",
    "DeviceModeCapability": "0;1",
}
TITLE = f"{ROUTER_INFOS['ModelName']} - {ROUTER_INFOS['DeviceName']}"
TITLE_INCOMPLETE = ROUTER_INFOS["ModelName"]

HOST = "10.0.0.1"
SERIAL_2 = "5ER1AL0000002"
PORT = 80
SSL = False
USERNAME = "Home_Assistant"
PASSWORD = "password"
SSDP_URL = f"http://{HOST}:{PORT}/rootDesc.xml"
SSDP_URL_SLL = f"https://{HOST}:{PORT}/rootDesc.xml"


@pytest.fixture(name="service")
def mock_controller_service():
    """Mock a successful service."""
    with patch(
        "homeassistant.components.netgear.async_setup_entry", return_value=True
    ), patch("homeassistant.components.netgear.router.Netgear") as service_mock:
        service_mock.return_value.get_info = Mock(return_value=ROUTER_INFOS)
        service_mock.return_value.port = 80
        service_mock.return_value.ssl = False
        yield service_mock


@pytest.fixture(name="service_5555")
def mock_controller_service_5555():
    """Mock a successful service."""
    with patch(
        "homeassistant.components.netgear.async_setup_entry", return_value=True
    ), patch("homeassistant.components.netgear.router.Netgear") as service_mock:
        service_mock.return_value.get_info = Mock(return_value=ROUTER_INFOS)
        service_mock.return_value.port = 5555
        service_mock.return_value.ssl = True
        yield service_mock


@pytest.fixture(name="service_incomplete")
def mock_controller_service_incomplete():
    """Mock a successful service."""
    router_infos = ROUTER_INFOS.copy()
    router_infos.pop("DeviceName")
    with patch(
        "homeassistant.components.netgear.async_setup_entry", return_value=True
    ), patch("homeassistant.components.netgear.router.Netgear") as service_mock:
        service_mock.return_value.get_info = Mock(return_value=router_infos)
        service_mock.return_value.port = 80
        service_mock.return_value.ssl = False
        yield service_mock


@pytest.fixture(name="service_failed")
def mock_controller_service_failed():
    """Mock a failed service."""
    with patch("homeassistant.components.netgear.router.Netgear") as service_mock:
        service_mock.return_value.login_try_port = Mock(return_value=None)
        service_mock.return_value.get_info = Mock(return_value=None)
        yield service_mock


async def test_user(hass, service):
    """Test user step."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": SOURCE_USER}
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "user"

    # Have to provide all config
    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        {
            CONF_HOST: HOST,
            CONF_USERNAME: USERNAME,
            CONF_PASSWORD: PASSWORD,
        },
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
    assert result["result"].unique_id == SERIAL
    assert result["title"] == TITLE
    assert result["data"].get(CONF_HOST) == HOST
    assert result["data"].get(CONF_PORT) == PORT
    assert result["data"].get(CONF_SSL) == SSL
    assert result["data"].get(CONF_USERNAME) == USERNAME
    assert result["data"][CONF_PASSWORD] == PASSWORD


async def test_user_connect_error(hass, service_failed):
    """Test user step with connection failure."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": SOURCE_USER}
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "user"

    # Have to provide all config
    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        {
            CONF_HOST: HOST,
            CONF_USERNAME: USERNAME,
            CONF_PASSWORD: PASSWORD,
        },
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "user"
    assert result["errors"] == {"base": "config"}


async def test_user_incomplete_info(hass, service_incomplete):
    """Test user step with incomplete device info."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": SOURCE_USER}
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "user"

    # Have to provide all config
    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        {
            CONF_HOST: HOST,
            CONF_USERNAME: USERNAME,
            CONF_PASSWORD: PASSWORD,
        },
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
    assert result["result"].unique_id == SERIAL
    assert result["title"] == TITLE_INCOMPLETE
    assert result["data"].get(CONF_HOST) == HOST
    assert result["data"].get(CONF_PORT) == PORT
    assert result["data"].get(CONF_SSL) == SSL
    assert result["data"].get(CONF_USERNAME) == USERNAME
    assert result["data"][CONF_PASSWORD] == PASSWORD


async def test_abort_if_already_setup(hass, service):
    """Test we abort if the router is already setup."""
    MockConfigEntry(
        domain=DOMAIN,
        data={CONF_PASSWORD: PASSWORD},
        unique_id=SERIAL,
    ).add_to_hass(hass)

    # Should fail, same SERIAL (flow)
    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": SOURCE_USER}
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "user"

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        {CONF_PASSWORD: PASSWORD},
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == "already_configured"


async def test_ssdp_already_configured(hass):
    """Test ssdp abort when the router is already configured."""
    MockConfigEntry(
        domain=DOMAIN,
        data={CONF_PASSWORD: PASSWORD},
        unique_id=SERIAL,
    ).add_to_hass(hass)

    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={"source": SOURCE_SSDP},
        data=ssdp.SsdpServiceInfo(
            ssdp_usn="mock_usn",
            ssdp_st="mock_st",
            ssdp_location=SSDP_URL_SLL,
            upnp={
                ssdp.ATTR_UPNP_MODEL_NUMBER: "RBR20",
                ssdp.ATTR_UPNP_PRESENTATION_URL: URL,
                ssdp.ATTR_UPNP_SERIAL: SERIAL,
            },
        ),
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
    assert result["reason"] == "already_configured"


async def test_ssdp(hass, service):
    """Test ssdp step."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={"source": SOURCE_SSDP},
        data=ssdp.SsdpServiceInfo(
            ssdp_usn="mock_usn",
            ssdp_st="mock_st",
            ssdp_location=SSDP_URL,
            upnp={
                ssdp.ATTR_UPNP_MODEL_NUMBER: "RBR20",
                ssdp.ATTR_UPNP_PRESENTATION_URL: URL,
                ssdp.ATTR_UPNP_SERIAL: SERIAL,
            },
        ),
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "user"

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"], {CONF_PASSWORD: PASSWORD}
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
    assert result["result"].unique_id == SERIAL
    assert result["title"] == TITLE
    assert result["data"].get(CONF_HOST) == HOST
    assert result["data"].get(CONF_PORT) == PORT_80
    assert result["data"].get(CONF_SSL) == SSL
    assert result["data"].get(CONF_USERNAME) == DEFAULT_USER
    assert result["data"][CONF_PASSWORD] == PASSWORD


async def test_ssdp_port_5555(hass, service_5555):
    """Test ssdp step with port 5555."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={"source": SOURCE_SSDP},
        data=ssdp.SsdpServiceInfo(
            ssdp_usn="mock_usn",
            ssdp_st="mock_st",
            ssdp_location=SSDP_URL_SLL,
            upnp={
                ssdp.ATTR_UPNP_MODEL_NUMBER: MODELS_PORT_5555[0],
                ssdp.ATTR_UPNP_PRESENTATION_URL: URL_SSL,
                ssdp.ATTR_UPNP_SERIAL: SERIAL,
            },
        ),
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["step_id"] == "user"

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"], {CONF_PASSWORD: PASSWORD}
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
    assert result["result"].unique_id == SERIAL
    assert result["title"] == TITLE
    assert result["data"].get(CONF_HOST) == HOST
    assert result["data"].get(CONF_PORT) == PORT_5555
    assert result["data"].get(CONF_SSL) is True
    assert result["data"].get(CONF_USERNAME) == DEFAULT_USER
    assert result["data"][CONF_PASSWORD] == PASSWORD


async def test_options_flow(hass, service):
    """Test specifying non default settings using options flow."""
    config_entry = MockConfigEntry(
        domain=DOMAIN,
        data={CONF_PASSWORD: PASSWORD},
        unique_id=SERIAL,
        title=TITLE,
    )
    config_entry.add_to_hass(hass)

    assert await hass.config_entries.async_setup(config_entry.entry_id)
    await hass.async_block_till_done()

    result = await hass.config_entries.options.async_init(config_entry.entry_id)

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

    result = await hass.config_entries.options.async_configure(
        result["flow_id"],
        user_input={
            CONF_CONSIDER_HOME: 1800,
        },
    )

    assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
    assert config_entry.options == {
        CONF_CONSIDER_HOME: 1800,
    }