"""Define tests for the SimpliSafe config flow."""
from unittest.mock import AsyncMock, Mock, patch

import pytest
from simplipy.errors import InvalidCredentialsError, SimplipyError

from homeassistant import data_entry_flow
from homeassistant.components.simplisafe import DOMAIN
from homeassistant.components.simplisafe.config_flow import CONF_AUTH_CODE
from homeassistant.components.simplisafe.const import CONF_USER_ID
from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER
from homeassistant.const import CONF_CODE, CONF_PASSWORD, CONF_TOKEN, CONF_USERNAME

from tests.common import MockConfigEntry


@pytest.fixture(name="api")
def api_fixture():
    """Define a fixture for simplisafe-python API object."""
    api = Mock()
    api.refresh_token = "token123"
    api.user_id = "12345"
    return api


@pytest.fixture(name="mock_async_from_auth")
def mock_async_from_auth_fixture(api):
    """Define a fixture for simplipy.API.async_from_auth."""
    with patch(
        "homeassistant.components.simplisafe.config_flow.API.async_from_auth",
    ) as mock_async_from_auth:
        mock_async_from_auth.side_effect = AsyncMock(return_value=api)
        yield mock_async_from_auth


async def test_duplicate_error(hass, mock_async_from_auth):
    """Test that errors are shown when duplicates are added."""
    MockConfigEntry(
        domain=DOMAIN,
        unique_id="12345",
        data={
            CONF_USER_ID: "12345",
            CONF_TOKEN: "token123",
        },
    ).add_to_hass(hass)

    with patch(
        "homeassistant.components.simplisafe.async_setup_entry", return_value=True
    ):
        result = await hass.config_entries.flow.async_init(
            DOMAIN, context={"source": SOURCE_USER}
        )
        assert result["step_id"] == "user"
        assert result["type"] == data_entry_flow.RESULT_TYPE_FORM

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


async def test_invalid_credentials(hass, mock_async_from_auth):
    """Test that invalid credentials show the correct error."""
    mock_async_from_auth.side_effect = AsyncMock(side_effect=InvalidCredentialsError)

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

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"], user_input={CONF_AUTH_CODE: "code123"}
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["errors"] == {"base": "invalid_auth"}


async def test_options_flow(hass):
    """Test config flow options."""
    entry = MockConfigEntry(
        domain=DOMAIN,
        unique_id="abcde12345",
        data={CONF_USER_ID: "12345", CONF_TOKEN: "token456"},
        options={CONF_CODE: "1234"},
    )
    entry.add_to_hass(hass)

    with patch(
        "homeassistant.components.simplisafe.async_setup_entry", return_value=True
    ):
        await hass.config_entries.async_setup(entry.entry_id)
        result = await hass.config_entries.options.async_init(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_CODE: "4321"}
        )

        assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
        assert entry.options == {CONF_CODE: "4321"}


async def test_step_reauth_old_format(hass, mock_async_from_auth):
    """Test the re-auth step with "old" config entries (those with user IDs)."""
    MockConfigEntry(
        domain=DOMAIN,
        unique_id="user@email.com",
        data={
            CONF_USERNAME: "user@email.com",
            CONF_PASSWORD: "password",
        },
    ).add_to_hass(hass)

    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={"source": SOURCE_REAUTH},
        data={CONF_USERNAME: "user@email.com", CONF_PASSWORD: "password"},
    )
    assert result["step_id"] == "user"

    with patch(
        "homeassistant.components.simplisafe.async_setup_entry", return_value=True
    ), patch("homeassistant.config_entries.ConfigEntries.async_reload"):
        result = await hass.config_entries.flow.async_configure(
            result["flow_id"], user_input={CONF_AUTH_CODE: "code123"}
        )
        assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
        assert result["reason"] == "reauth_successful"

    assert len(hass.config_entries.async_entries()) == 1
    [config_entry] = hass.config_entries.async_entries(DOMAIN)
    assert config_entry.data == {CONF_USER_ID: "12345", CONF_TOKEN: "token123"}


async def test_step_reauth_new_format(hass, mock_async_from_auth):
    """Test the re-auth step with "new" config entries (those with user IDs)."""
    MockConfigEntry(
        domain=DOMAIN,
        unique_id="12345",
        data={
            CONF_USER_ID: "12345",
            CONF_TOKEN: "token123",
        },
    ).add_to_hass(hass)

    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={"source": SOURCE_REAUTH},
        data={CONF_USER_ID: "12345", CONF_TOKEN: "token123"},
    )
    assert result["step_id"] == "user"

    with patch(
        "homeassistant.components.simplisafe.async_setup_entry", return_value=True
    ), patch("homeassistant.config_entries.ConfigEntries.async_reload"):
        result = await hass.config_entries.flow.async_configure(
            result["flow_id"], user_input={CONF_AUTH_CODE: "code123"}
        )
        assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
        assert result["reason"] == "reauth_successful"

    assert len(hass.config_entries.async_entries()) == 1
    [config_entry] = hass.config_entries.async_entries(DOMAIN)
    assert config_entry.data == {CONF_USER_ID: "12345", CONF_TOKEN: "token123"}


async def test_step_reauth_wrong_account(hass, api, mock_async_from_auth):
    """Test the re-auth step returning a different account from this one."""
    MockConfigEntry(
        domain=DOMAIN,
        unique_id="12345",
        data={
            CONF_USER_ID: "12345",
            CONF_TOKEN: "token123",
        },
    ).add_to_hass(hass)

    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={"source": SOURCE_REAUTH},
        data={CONF_USER_ID: "12345", CONF_TOKEN: "token123"},
    )
    assert result["step_id"] == "user"

    # Simulate the next auth call returning a different user ID than the one we've
    # identified as this entry's unique ID:
    api.user_id = "67890"

    with patch(
        "homeassistant.components.simplisafe.async_setup_entry", return_value=True
    ), patch("homeassistant.config_entries.ConfigEntries.async_reload"):
        result = await hass.config_entries.flow.async_configure(
            result["flow_id"], user_input={CONF_AUTH_CODE: "code123"}
        )
        assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
        assert result["reason"] == "wrong_account"

    assert len(hass.config_entries.async_entries()) == 1
    [config_entry] = hass.config_entries.async_entries(DOMAIN)
    assert config_entry.unique_id == "12345"


async def test_step_user(hass, mock_async_from_auth):
    """Test the user step."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": SOURCE_USER}
    )
    assert result["step_id"] == "user"

    with patch(
        "homeassistant.components.simplisafe.async_setup_entry", return_value=True
    ), patch("homeassistant.config_entries.ConfigEntries.async_reload"):
        result = await hass.config_entries.flow.async_configure(
            result["flow_id"], user_input={CONF_AUTH_CODE: "code123"}
        )
        assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY

    assert len(hass.config_entries.async_entries()) == 1
    [config_entry] = hass.config_entries.async_entries(DOMAIN)
    assert config_entry.data == {CONF_USER_ID: "12345", CONF_TOKEN: "token123"}


async def test_unknown_error(hass, mock_async_from_auth):
    """Test that an unknown error shows ohe correct error."""
    mock_async_from_auth.side_effect = AsyncMock(side_effect=SimplipyError)

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

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"], user_input={CONF_AUTH_CODE: "code123"}
    )
    assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
    assert result["errors"] == {"base": "unknown"}