"""Test the ROMY config flow."""

from collections.abc import Generator
from ipaddress import ip_address
from unittest.mock import AsyncMock, Mock, PropertyMock, patch

import pytest
from romy import RomyRobot

from homeassistant import config_entries
from homeassistant.components.romy.const import DOMAIN
from homeassistant.const import CONF_HOST, CONF_PASSWORD
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers.service_info.zeroconf import (
    ATTR_PROPERTIES_ID,
    ZeroconfServiceInfo,
)


def _create_mocked_romy(
    is_initialized,
    is_unlocked,
    name="Agon",
    user_name="MyROMY",
    unique_id="aicu-aicgsbksisfapcjqmqjq",
    model="005:000:000:000:005",
    port=8080,
):
    mocked_romy = Mock(spec_set=RomyRobot)
    type(mocked_romy).is_initialized = PropertyMock(return_value=is_initialized)
    type(mocked_romy).is_unlocked = PropertyMock(return_value=is_unlocked)
    type(mocked_romy).name = PropertyMock(return_value=name)
    type(mocked_romy).user_name = PropertyMock(return_value=user_name)
    type(mocked_romy).unique_id = PropertyMock(return_value=unique_id)
    type(mocked_romy).port = PropertyMock(return_value=port)
    type(mocked_romy).model = PropertyMock(return_value=model)

    return mocked_romy


CONFIG = {CONF_HOST: "1.2.3.4", CONF_PASSWORD: "12345678"}

INPUT_CONFIG_HOST = {
    CONF_HOST: CONFIG[CONF_HOST],
}


@pytest.fixture(autouse=True)
def mock_setup_entry() -> Generator[AsyncMock]:
    """Override async_setup_entry."""
    with patch(
        "homeassistant.components.romy.async_setup_entry", return_value=True
    ) as mock_setup_entry:
        yield mock_setup_entry


async def test_show_user_form_robot_is_offline_and_locked(hass: HomeAssistant) -> None:
    """Test that the user set up form with config."""

    # Robot not reachable
    with patch(
        "homeassistant.components.romy.config_flow.romy.create_romy",
        return_value=_create_mocked_romy(False, False),
    ):
        result1 = await hass.config_entries.flow.async_init(
            DOMAIN,
            context={"source": config_entries.SOURCE_USER},
            data=INPUT_CONFIG_HOST,
        )

        assert result1["errors"].get("host") == "cannot_connect"
        assert result1["step_id"] == "user"
        assert result1["type"] is FlowResultType.FORM

    # Robot is locked
    with patch(
        "homeassistant.components.romy.config_flow.romy.create_romy",
        return_value=_create_mocked_romy(True, False),
    ):
        result2 = await hass.config_entries.flow.async_configure(
            result1["flow_id"], {"host": "1.2.3.4"}
        )

        assert result2["step_id"] == "password"
        assert result2["type"] is FlowResultType.FORM

    # Robot is initialized and unlocked
    with patch(
        "homeassistant.components.romy.config_flow.romy.create_romy",
        return_value=_create_mocked_romy(True, True),
    ):
        result3 = await hass.config_entries.flow.async_configure(
            result2["flow_id"], {"password": "12345678"}
        )

        assert "errors" not in result3
        assert result3["type"] is FlowResultType.CREATE_ENTRY


async def test_show_user_form_robot_unlock_with_password(hass: HomeAssistant) -> None:
    """Test that the user set up form with config."""

    with patch(
        "homeassistant.components.romy.config_flow.romy.create_romy",
        return_value=_create_mocked_romy(True, False),
    ):
        result = await hass.config_entries.flow.async_init(
            DOMAIN,
            context={"source": config_entries.SOURCE_USER},
            data=INPUT_CONFIG_HOST,
        )

    with patch(
        "homeassistant.components.romy.config_flow.romy.create_romy",
        return_value=_create_mocked_romy(True, False),
    ):
        result2 = await hass.config_entries.flow.async_configure(
            result["flow_id"], {"password": "12345678"}
        )

        assert result2["errors"] == {"password": "invalid_auth"}
        assert result2["step_id"] == "password"
        assert result2["type"] is FlowResultType.FORM

    with patch(
        "homeassistant.components.romy.config_flow.romy.create_romy",
        return_value=_create_mocked_romy(False, False),
    ):
        result3 = await hass.config_entries.flow.async_configure(
            result2["flow_id"], {"password": "12345678"}
        )

        assert result3["errors"] == {"password": "cannot_connect"}
        assert result3["step_id"] == "password"
        assert result3["type"] is FlowResultType.FORM

    with patch(
        "homeassistant.components.romy.config_flow.romy.create_romy",
        return_value=_create_mocked_romy(True, True),
    ):
        result4 = await hass.config_entries.flow.async_configure(
            result3["flow_id"], {"password": "12345678"}
        )

        assert "errors" not in result4
        assert result4["type"] is FlowResultType.CREATE_ENTRY


async def test_show_user_form_robot_reachable_again(hass: HomeAssistant) -> None:
    """Test that the user set up form with config."""

    # Robot not reachable
    with patch(
        "homeassistant.components.romy.config_flow.romy.create_romy",
        return_value=_create_mocked_romy(False, False),
    ):
        result1 = await hass.config_entries.flow.async_init(
            DOMAIN,
            context={"source": config_entries.SOURCE_USER},
            data=INPUT_CONFIG_HOST,
        )

        assert result1["errors"].get("host") == "cannot_connect"
        assert result1["step_id"] == "user"
        assert result1["type"] is FlowResultType.FORM

    # Robot is locked
    with patch(
        "homeassistant.components.romy.config_flow.romy.create_romy",
        return_value=_create_mocked_romy(True, True),
    ):
        result2 = await hass.config_entries.flow.async_configure(
            result1["flow_id"], {"host": "1.2.3.4"}
        )

        assert "errors" not in result2
        assert result2["type"] is FlowResultType.CREATE_ENTRY


DISCOVERY_INFO = ZeroconfServiceInfo(
    ip_address=ip_address("1.2.3.4"),
    ip_addresses=[ip_address("1.2.3.4")],
    port=8080,
    hostname="aicu-aicgsbksisfapcjqmqjq.local",
    type="mock_type",
    name="myROMY",
    properties={ATTR_PROPERTIES_ID: "aicu-aicgsbksisfapcjqmqjqZERO"},
)


async def test_zero_conf_locked_interface_robot(hass: HomeAssistant) -> None:
    """Test zerconf which discovered locked robot."""

    with patch(
        "homeassistant.components.romy.config_flow.romy.create_romy",
        return_value=_create_mocked_romy(True, False),
    ):
        result1 = await hass.config_entries.flow.async_init(
            DOMAIN,
            data=DISCOVERY_INFO,
            context={"source": config_entries.SOURCE_ZEROCONF},
        )

    assert result1["step_id"] == "password"
    assert result1["type"] is FlowResultType.FORM

    with patch(
        "homeassistant.components.romy.config_flow.romy.create_romy",
        return_value=_create_mocked_romy(True, True),
    ):
        result2 = await hass.config_entries.flow.async_configure(
            result1["flow_id"], {"password": "12345678"}
        )

        assert "errors" not in result2
        assert result2["type"] is FlowResultType.CREATE_ENTRY


async def test_zero_conf_uninitialized_robot(hass: HomeAssistant) -> None:
    """Test zerconf which discovered locked robot."""

    with patch(
        "homeassistant.components.romy.config_flow.romy.create_romy",
        return_value=_create_mocked_romy(False, False),
    ):
        result = await hass.config_entries.flow.async_init(
            DOMAIN,
            data=DISCOVERY_INFO,
            context={"source": config_entries.SOURCE_ZEROCONF},
        )

    assert result["reason"] == "cannot_connect"
    assert result["type"] is FlowResultType.ABORT


async def test_zero_conf_unlocked_interface_robot(hass: HomeAssistant) -> None:
    """Test zerconf which discovered already unlocked robot."""

    with patch(
        "homeassistant.components.romy.config_flow.romy.create_romy",
        return_value=_create_mocked_romy(True, True),
    ):
        result = await hass.config_entries.flow.async_init(
            DOMAIN,
            data=DISCOVERY_INFO,
            context={"source": config_entries.SOURCE_ZEROCONF},
        )

    assert result["step_id"] == "zeroconf_confirm"
    assert result["type"] is FlowResultType.FORM

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

    assert result["data"]
    assert result["data"][CONF_HOST] == "1.2.3.4"

    assert result["result"]
    assert result["result"].unique_id == "aicu-aicgsbksisfapcjqmqjq"

    assert result["type"] is FlowResultType.CREATE_ENTRY
