"""Test the sia config flow."""
from unittest.mock import patch

import pytest

from homeassistant import config_entries, data_entry_flow
from homeassistant.components.sia.config_flow import ACCOUNT_SCHEMA, HUB_SCHEMA
from homeassistant.components.sia.const import (
    CONF_ACCOUNT,
    CONF_ACCOUNTS,
    CONF_ADDITIONAL_ACCOUNTS,
    CONF_ENCRYPTION_KEY,
    CONF_IGNORE_TIMESTAMPS,
    CONF_PING_INTERVAL,
    CONF_ZONES,
    DOMAIN,
)
from homeassistant.const import CONF_PORT, CONF_PROTOCOL
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component

from tests.common import MockConfigEntry

BASIS_CONFIG_ENTRY_ID = 1
BASIC_CONFIG = {
    CONF_PORT: 7777,
    CONF_PROTOCOL: "TCP",
    CONF_ACCOUNT: "ABCDEF",
    CONF_ENCRYPTION_KEY: "AAAAAAAAAAAAAAAA",
    CONF_PING_INTERVAL: 10,
    CONF_ZONES: 1,
    CONF_ADDITIONAL_ACCOUNTS: False,
}

BASIC_OPTIONS = {CONF_IGNORE_TIMESTAMPS: False, CONF_ZONES: 2}

BASE_OUT = {
    "data": {
        CONF_PORT: 7777,
        CONF_PROTOCOL: "TCP",
        CONF_ACCOUNTS: [
            {
                CONF_ACCOUNT: "ABCDEF",
                CONF_ENCRYPTION_KEY: "AAAAAAAAAAAAAAAA",
                CONF_PING_INTERVAL: 10,
            },
        ],
    },
    "options": {
        CONF_ACCOUNTS: {"ABCDEF": {CONF_IGNORE_TIMESTAMPS: False, CONF_ZONES: 1}}
    },
}

ADDITIONAL_CONFIG_ENTRY_ID = 2
BASIC_CONFIG_ADDITIONAL = {
    CONF_PORT: 7777,
    CONF_PROTOCOL: "TCP",
    CONF_ACCOUNT: "ABCDEF",
    CONF_ENCRYPTION_KEY: "AAAAAAAAAAAAAAAA",
    CONF_PING_INTERVAL: 10,
    CONF_ZONES: 1,
    CONF_ADDITIONAL_ACCOUNTS: True,
}

ADDITIONAL_ACCOUNT = {
    CONF_ACCOUNT: "ACC2",
    CONF_ENCRYPTION_KEY: "AAAAAAAAAAAAAAAA",
    CONF_PING_INTERVAL: 2,
    CONF_ZONES: 2,
    CONF_ADDITIONAL_ACCOUNTS: False,
}
ADDITIONAL_OUT = {
    "data": {
        CONF_PORT: 7777,
        CONF_PROTOCOL: "TCP",
        CONF_ACCOUNTS: [
            {
                CONF_ACCOUNT: "ABCDEF",
                CONF_ENCRYPTION_KEY: "AAAAAAAAAAAAAAAA",
                CONF_PING_INTERVAL: 10,
            },
            {
                CONF_ACCOUNT: "ACC2",
                CONF_ENCRYPTION_KEY: "AAAAAAAAAAAAAAAA",
                CONF_PING_INTERVAL: 2,
            },
        ],
    },
    "options": {
        CONF_ACCOUNTS: {
            "ABCDEF": {CONF_IGNORE_TIMESTAMPS: False, CONF_ZONES: 1},
            "ACC2": {CONF_IGNORE_TIMESTAMPS: False, CONF_ZONES: 2},
        }
    },
}

ADDITIONAL_OPTIONS = {
    CONF_ACCOUNTS: {
        "ABCDEF": {CONF_IGNORE_TIMESTAMPS: False, CONF_ZONES: 2},
        "ACC2": {CONF_IGNORE_TIMESTAMPS: False, CONF_ZONES: 2},
    }
}


@pytest.fixture
async def flow_at_user_step(hass):
    """Return a initialized flow."""
    return await hass.config_entries.flow.async_init(
        DOMAIN,
        context={"source": config_entries.SOURCE_USER},
    )


@pytest.fixture
async def entry_with_basic_config(hass, flow_at_user_step):
    """Return a entry with a basic config."""
    with patch("homeassistant.components.sia.async_setup_entry", return_value=True):
        return await hass.config_entries.flow.async_configure(
            flow_at_user_step["flow_id"], BASIC_CONFIG
        )


@pytest.fixture
async def flow_at_add_account_step(hass, flow_at_user_step):
    """Return a initialized flow at the additional account step."""
    return await hass.config_entries.flow.async_configure(
        flow_at_user_step["flow_id"], BASIC_CONFIG_ADDITIONAL
    )


@pytest.fixture
async def entry_with_additional_account_config(hass, flow_at_add_account_step):
    """Return a entry with a two account config."""
    with patch("homeassistant.components.sia.async_setup_entry", return_value=True):
        return await hass.config_entries.flow.async_configure(
            flow_at_add_account_step["flow_id"], ADDITIONAL_ACCOUNT
        )


async def setup_sia(hass, config_entry: MockConfigEntry):
    """Add mock config to HASS."""
    assert await async_setup_component(hass, DOMAIN, {})
    config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(config_entry.entry_id)
    await hass.async_block_till_done()


async def test_form_start_user(hass: HomeAssistant, flow_at_user_step) -> None:
    """Start the form and check if you get the right id and schema for the user step."""
    assert flow_at_user_step["step_id"] == "user"
    assert flow_at_user_step["errors"] is None
    assert flow_at_user_step["data_schema"] == HUB_SCHEMA


async def test_form_start_account(
    hass: HomeAssistant, flow_at_add_account_step
) -> None:
    """Start the form and check if you get the right id and schema for the additional account step."""
    assert flow_at_add_account_step["step_id"] == "add_account"
    assert flow_at_add_account_step["errors"] is None
    assert flow_at_add_account_step["data_schema"] == ACCOUNT_SCHEMA


async def test_create(hass: HomeAssistant, entry_with_basic_config) -> None:
    """Test we create a entry through the form."""
    assert (
        entry_with_basic_config["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
    )
    assert (
        entry_with_basic_config["title"]
        == f"SIA Alarm on port {BASIC_CONFIG[CONF_PORT]}"
    )
    assert entry_with_basic_config["data"] == BASE_OUT["data"]
    assert entry_with_basic_config["options"] == BASE_OUT["options"]


async def test_create_additional_account(
    hass: HomeAssistant, entry_with_additional_account_config
) -> None:
    """Test we create a config with two accounts."""
    assert (
        entry_with_additional_account_config["type"]
        == data_entry_flow.FlowResultType.CREATE_ENTRY
    )
    assert (
        entry_with_additional_account_config["title"]
        == f"SIA Alarm on port {BASIC_CONFIG[CONF_PORT]}"
    )

    assert entry_with_additional_account_config["data"] == ADDITIONAL_OUT["data"]
    assert entry_with_additional_account_config["options"] == ADDITIONAL_OUT["options"]


async def test_abort_form(hass: HomeAssistant) -> None:
    """Test aborting a config that already exists."""
    config_entry = MockConfigEntry(
        domain=DOMAIN,
        data=BASE_OUT["data"],
        options=BASE_OUT["options"],
        title="SIA Alarm on port 7777",
        entry_id=BASIS_CONFIG_ENTRY_ID,
        version=1,
    )
    await setup_sia(hass, config_entry)
    start_another_flow = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": config_entries.SOURCE_USER}
    )
    get_abort = await hass.config_entries.flow.async_configure(
        start_another_flow["flow_id"], BASIC_CONFIG
    )
    assert get_abort["type"] == "abort"
    assert get_abort["reason"] == "already_configured"


@pytest.fixture(autouse=True)
def mock_sia():
    """Mock SIAClient."""
    with patch("homeassistant.components.sia.hub.SIAClient", autospec=True):
        yield


@pytest.mark.parametrize(
    ("field", "value", "error"),
    [
        ("encryption_key", "AAAAAAAAAAAAAZZZ", "invalid_key_format"),
        ("encryption_key", "AAAAAAAAAAAAA", "invalid_key_length"),
        ("account", "ZZZ", "invalid_account_format"),
        ("account", "A", "invalid_account_length"),
        ("ping_interval", 1500, "invalid_ping"),
        ("zones", 0, "invalid_zones"),
    ],
)
async def test_validation_errors_user(
    hass: HomeAssistant,
    flow_at_user_step,
    field,
    value,
    error,
) -> None:
    """Test we handle the different invalid inputs, in the user flow."""
    config = BASIC_CONFIG.copy()
    flow_id = flow_at_user_step["flow_id"]
    config[field] = value
    result_err = await hass.config_entries.flow.async_configure(flow_id, config)
    assert result_err["type"] == "form"
    assert result_err["errors"] == {"base": error}


@pytest.mark.parametrize(
    ("field", "value", "error"),
    [
        ("encryption_key", "AAAAAAAAAAAAAZZZ", "invalid_key_format"),
        ("encryption_key", "AAAAAAAAAAAAA", "invalid_key_length"),
        ("account", "ZZZ", "invalid_account_format"),
        ("account", "A", "invalid_account_length"),
        ("ping_interval", 1500, "invalid_ping"),
        ("zones", 0, "invalid_zones"),
    ],
)
async def test_validation_errors_account(
    hass: HomeAssistant,
    flow_at_user_step,
    field,
    value,
    error,
) -> None:
    """Test we handle the different invalid inputs, in the add_account flow."""
    flow_at_add_account_step = await hass.config_entries.flow.async_configure(
        flow_at_user_step["flow_id"], BASIC_CONFIG_ADDITIONAL
    )
    config = ADDITIONAL_ACCOUNT.copy()
    flow_id = flow_at_add_account_step["flow_id"]
    config[field] = value
    result_err = await hass.config_entries.flow.async_configure(flow_id, config)
    assert result_err["type"] == "form"
    assert result_err["errors"] == {"base": error}


async def test_unknown_user(hass: HomeAssistant, flow_at_user_step) -> None:
    """Test unknown exceptions."""
    flow_id = flow_at_user_step["flow_id"]
    with patch(
        "pysiaalarm.SIAAccount.validate_account",
        side_effect=Exception,
    ):
        config = BASIC_CONFIG
        result_err = await hass.config_entries.flow.async_configure(flow_id, config)
        assert result_err
        assert result_err["step_id"] == "user"
        assert result_err["errors"] == {"base": "unknown"}
        assert result_err["data_schema"] == HUB_SCHEMA


async def test_unknown_account(hass: HomeAssistant, flow_at_user_step) -> None:
    """Test unknown exceptions."""
    flow_at_add_account_step = await hass.config_entries.flow.async_configure(
        flow_at_user_step["flow_id"], BASIC_CONFIG_ADDITIONAL
    )
    flow_id = flow_at_add_account_step["flow_id"]
    with patch(
        "pysiaalarm.SIAAccount.validate_account",
        side_effect=Exception,
    ):
        config = ADDITIONAL_ACCOUNT
        result_err = await hass.config_entries.flow.async_configure(flow_id, config)
        assert result_err
        assert result_err["step_id"] == "add_account"
        assert result_err["errors"] == {"base": "unknown"}
        assert result_err["data_schema"] == ACCOUNT_SCHEMA


async def test_options_basic(hass: HomeAssistant) -> None:
    """Test options flow for single account."""
    config_entry = MockConfigEntry(
        domain=DOMAIN,
        data=BASE_OUT["data"],
        options=BASE_OUT["options"],
        title="SIA Alarm on port 7777",
        entry_id=BASIS_CONFIG_ENTRY_ID,
        version=1,
    )
    await setup_sia(hass, config_entry)
    result = await hass.config_entries.options.async_init(config_entry.entry_id)
    assert result["type"] == data_entry_flow.FlowResultType.FORM
    assert result["step_id"] == "options"
    assert result["last_step"]

    updated = await hass.config_entries.options.async_configure(
        result["flow_id"], BASIC_OPTIONS
    )
    await hass.async_block_till_done()
    assert updated["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY
    assert updated["data"] == {
        CONF_ACCOUNTS: {BASIC_CONFIG[CONF_ACCOUNT]: BASIC_OPTIONS}
    }


async def test_options_additional(hass: HomeAssistant) -> None:
    """Test options flow for single account."""
    config_entry = MockConfigEntry(
        domain=DOMAIN,
        data=ADDITIONAL_OUT["data"],
        options=ADDITIONAL_OUT["options"],
        title="SIA Alarm on port 7777",
        entry_id=ADDITIONAL_CONFIG_ENTRY_ID,
        version=1,
    )
    await setup_sia(hass, config_entry)
    result = await hass.config_entries.options.async_init(config_entry.entry_id)
    assert result["type"] == data_entry_flow.FlowResultType.FORM
    assert result["step_id"] == "options"
    assert not result["last_step"]

    updated = await hass.config_entries.options.async_configure(
        result["flow_id"], BASIC_OPTIONS
    )
    assert updated["type"] == data_entry_flow.FlowResultType.FORM
    assert updated["step_id"] == "options"
    assert updated["last_step"]