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

from fiblary3.common.exceptions import HTTPException
import pytest

from homeassistant import config_entries
from homeassistant.components.fibaro import DOMAIN
from homeassistant.components.fibaro.config_flow import _normalize_url
from homeassistant.components.fibaro.const import CONF_IMPORT_PLUGINS
from homeassistant.const import CONF_PASSWORD, CONF_URL, CONF_USERNAME

from tests.common import MockConfigEntry

TEST_SERIALNUMBER = "HC2-111111"
TEST_NAME = "my_fibaro_home_center"
TEST_URL = "http://192.168.1.1/api/"
TEST_USERNAME = "user"
TEST_PASSWORD = "password"
TEST_VERSION = "4.360"


@pytest.fixture(name="fibaro_client", autouse=True)
def fibaro_client_fixture():
    """Mock common methods and attributes of fibaro client."""
    info_mock = Mock()
    info_mock.get.return_value = Mock(
        serialNumber=TEST_SERIALNUMBER, hcName=TEST_NAME, softVersion=TEST_VERSION
    )

    array_mock = Mock()
    array_mock.list.return_value = []

    client_mock = Mock()
    client_mock.base_url.return_value = TEST_URL

    with patch(
        "homeassistant.components.fibaro.FibaroClientV4.__init__",
        return_value=None,
    ), patch(
        "homeassistant.components.fibaro.FibaroClientV4.info",
        info_mock,
        create=True,
    ), patch(
        "homeassistant.components.fibaro.FibaroClientV4.rooms",
        array_mock,
        create=True,
    ), patch(
        "homeassistant.components.fibaro.FibaroClientV4.devices",
        array_mock,
        create=True,
    ), patch(
        "homeassistant.components.fibaro.FibaroClientV4.scenes",
        array_mock,
        create=True,
    ), patch(
        "homeassistant.components.fibaro.FibaroClientV4.client",
        client_mock,
        create=True,
    ):
        yield


async def test_config_flow_user_initiated_success(hass):
    """Successful flow manually initialized by the user."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": config_entries.SOURCE_USER}
    )

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

    login_mock = Mock()
    login_mock.get.return_value = Mock(status=True)
    with patch(
        "homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True
    ), patch(
        "homeassistant.components.fibaro.async_setup_entry",
        return_value=True,
    ):
        result = await hass.config_entries.flow.async_configure(
            result["flow_id"],
            {
                CONF_URL: TEST_URL,
                CONF_USERNAME: TEST_USERNAME,
                CONF_PASSWORD: TEST_PASSWORD,
            },
        )

        assert result["type"] == "create_entry"
        assert result["title"] == TEST_NAME
        assert result["data"] == {
            CONF_URL: TEST_URL,
            CONF_USERNAME: TEST_USERNAME,
            CONF_PASSWORD: TEST_PASSWORD,
            CONF_IMPORT_PLUGINS: False,
        }


async def test_config_flow_user_initiated_connect_failure(hass):
    """Connect failure in flow manually initialized by the user."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": config_entries.SOURCE_USER}
    )

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

    login_mock = Mock()
    login_mock.get.return_value = Mock(status=False)
    with patch(
        "homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True
    ):
        result = await hass.config_entries.flow.async_configure(
            result["flow_id"],
            {
                CONF_URL: TEST_URL,
                CONF_USERNAME: TEST_USERNAME,
                CONF_PASSWORD: TEST_PASSWORD,
            },
        )

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


async def test_config_flow_user_initiated_auth_failure(hass):
    """Authentication failure in flow manually initialized by the user."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": config_entries.SOURCE_USER}
    )

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

    login_mock = Mock()
    login_mock.get.side_effect = HTTPException(details="Forbidden")
    with patch(
        "homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True
    ):
        result = await hass.config_entries.flow.async_configure(
            result["flow_id"],
            {
                CONF_URL: TEST_URL,
                CONF_USERNAME: TEST_USERNAME,
                CONF_PASSWORD: TEST_PASSWORD,
            },
        )

        assert result["type"] == "form"
        assert result["step_id"] == "user"
        assert result["errors"] == {"base": "invalid_auth"}


async def test_config_flow_user_initiated_unknown_failure_1(hass):
    """Unknown failure in flow manually initialized by the user."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": config_entries.SOURCE_USER}
    )

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

    login_mock = Mock()
    login_mock.get.side_effect = HTTPException(details="Any")
    with patch(
        "homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True
    ):
        result = await hass.config_entries.flow.async_configure(
            result["flow_id"],
            {
                CONF_URL: TEST_URL,
                CONF_USERNAME: TEST_USERNAME,
                CONF_PASSWORD: TEST_PASSWORD,
            },
        )

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


async def test_config_flow_user_initiated_unknown_failure_2(hass):
    """Unknown failure in flow manually initialized by the user."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": config_entries.SOURCE_USER}
    )

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

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        {
            CONF_URL: TEST_URL,
            CONF_USERNAME: TEST_USERNAME,
            CONF_PASSWORD: TEST_PASSWORD,
        },
    )

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


async def test_config_flow_import(hass):
    """Test for importing config from configuration.yaml."""
    login_mock = Mock()
    login_mock.get.return_value = Mock(status=True)
    with patch(
        "homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True
    ), patch(
        "homeassistant.components.fibaro.async_setup_entry",
        return_value=True,
    ):
        result = await hass.config_entries.flow.async_init(
            DOMAIN,
            context={"source": config_entries.SOURCE_IMPORT},
            data={
                CONF_URL: TEST_URL,
                CONF_USERNAME: TEST_USERNAME,
                CONF_PASSWORD: TEST_PASSWORD,
                CONF_IMPORT_PLUGINS: False,
            },
        )

        assert result["type"] == "create_entry"
        assert result["title"] == TEST_NAME
        assert result["data"] == {
            CONF_URL: TEST_URL,
            CONF_USERNAME: TEST_USERNAME,
            CONF_PASSWORD: TEST_PASSWORD,
            CONF_IMPORT_PLUGINS: False,
        }


async def test_reauth_success(hass):
    """Successful reauth flow initialized by the user."""
    mock_config = MockConfigEntry(
        domain=DOMAIN,
        entry_id=TEST_SERIALNUMBER,
        data={
            CONF_URL: TEST_URL,
            CONF_USERNAME: TEST_USERNAME,
            CONF_PASSWORD: TEST_PASSWORD,
            CONF_IMPORT_PLUGINS: False,
        },
    )
    mock_config.add_to_hass(hass)

    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={
            "source": config_entries.SOURCE_REAUTH,
            "entry_id": mock_config.entry_id,
        },
    )

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

    login_mock = Mock()
    login_mock.get.return_value = Mock(status=True)
    with patch(
        "homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True
    ), patch(
        "homeassistant.components.fibaro.async_setup_entry",
        return_value=True,
    ):
        result = await hass.config_entries.flow.async_configure(
            result["flow_id"],
            user_input={CONF_PASSWORD: "other_fake_password"},
        )

        assert result["type"] == "abort"
        assert result["reason"] == "reauth_successful"


async def test_reauth_connect_failure(hass):
    """Successful reauth flow initialized by the user."""
    mock_config = MockConfigEntry(
        domain=DOMAIN,
        entry_id=TEST_SERIALNUMBER,
        data={
            CONF_URL: TEST_URL,
            CONF_USERNAME: TEST_USERNAME,
            CONF_PASSWORD: TEST_PASSWORD,
            CONF_IMPORT_PLUGINS: False,
        },
    )
    mock_config.add_to_hass(hass)

    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={
            "source": config_entries.SOURCE_REAUTH,
            "entry_id": mock_config.entry_id,
        },
    )

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

    login_mock = Mock()
    login_mock.get.return_value = Mock(status=False)
    with patch(
        "homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True
    ):
        result = await hass.config_entries.flow.async_configure(
            result["flow_id"],
            user_input={CONF_PASSWORD: "other_fake_password"},
        )

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


async def test_reauth_auth_failure(hass):
    """Successful reauth flow initialized by the user."""
    mock_config = MockConfigEntry(
        domain=DOMAIN,
        entry_id=TEST_SERIALNUMBER,
        data={
            CONF_URL: TEST_URL,
            CONF_USERNAME: TEST_USERNAME,
            CONF_PASSWORD: TEST_PASSWORD,
            CONF_IMPORT_PLUGINS: False,
        },
    )
    mock_config.add_to_hass(hass)

    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={
            "source": config_entries.SOURCE_REAUTH,
            "entry_id": mock_config.entry_id,
        },
    )

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

    login_mock = Mock()
    login_mock.get.side_effect = HTTPException(details="Forbidden")
    with patch(
        "homeassistant.components.fibaro.FibaroClientV4.login", login_mock, create=True
    ):
        result = await hass.config_entries.flow.async_configure(
            result["flow_id"],
            user_input={CONF_PASSWORD: "other_fake_password"},
        )

        assert result["type"] == "form"
        assert result["step_id"] == "reauth_confirm"
        assert result["errors"] == {"base": "invalid_auth"}


@pytest.mark.parametrize("url_path", ["/api/", "/api", "/", ""])
async def test_normalize_url(url_path: str) -> None:
    """Test that the url is normalized for different entered values."""
    assert _normalize_url(f"http://192.168.1.1{url_path}") == "http://192.168.1.1/api/"