"""Define tests for the wsdot config flow."""

from typing import Any
from unittest.mock import AsyncMock

import pytest
from wsdot import WsdotTravelError

from homeassistant.components.wsdot.const import (
    CONF_TRAVEL_TIMES,
    DOMAIN,
    SUBENTRY_TRAVEL_TIMES,
)
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER
from homeassistant.const import CONF_API_KEY, CONF_ID, CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType

from tests.common import MockConfigEntry

VALID_USER_CONFIG = {
    CONF_API_KEY: "abcd-1234",
}

VALID_USER_TRAVEL_TIME_CONFIG = {
    CONF_NAME: "Seattle-Bellevue via I-90 (EB AM)",
}


async def test_create_user_entry(
    hass: HomeAssistant, mock_travel_time: AsyncMock
) -> None:
    """Test that the user step works."""
    # No user data; form is being show for the first time
    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": SOURCE_USER}
    )

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

    # User data; the user entered data and hit submit
    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        user_input=VALID_USER_CONFIG,
    )

    assert result["type"] is FlowResultType.CREATE_ENTRY
    assert result["title"] == DOMAIN
    assert result["data"][CONF_API_KEY] == "abcd-1234"


@pytest.mark.parametrize(
    ("failed_travel_time_status", "errors"),
    [
        (400, {CONF_API_KEY: "invalid_api_key"}),
        (404, {"base": "cannot_connect"}),
    ],
)
@pytest.mark.usefixtures("mock_setup_entry")
async def test_errors(
    hass: HomeAssistant,
    mock_travel_time: AsyncMock,
    failed_travel_time_status: int,
    errors: dict[str, str],
) -> None:
    """Test that the user step works."""
    mock_travel_time.get_all_travel_times.side_effect = WsdotTravelError(
        status=failed_travel_time_status
    )
    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": SOURCE_USER}
    )

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

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        user_input=VALID_USER_CONFIG,
    )

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

    mock_travel_time.get_all_travel_times.side_effect = None

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        user_input=VALID_USER_CONFIG,
    )

    assert result["type"] is FlowResultType.CREATE_ENTRY


@pytest.mark.parametrize(
    "mock_subentries",
    [
        [],
    ],
)
async def test_create_travel_time_subentry(
    hass: HomeAssistant,
    mock_travel_time: AsyncMock,
    init_integration: MockConfigEntry,
) -> None:
    """Test that the user step for Travel Time works."""
    # No user data; form is being show for the first time
    result = await hass.config_entries.subentries.async_init(
        (init_integration.entry_id, SUBENTRY_TRAVEL_TIMES),
        context={"source": SOURCE_USER},
    )

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

    # User data; the user made a choice and hit submit
    result = await hass.config_entries.subentries.async_init(
        (init_integration.entry_id, SUBENTRY_TRAVEL_TIMES),
        context={"source": SOURCE_USER},
        data=VALID_USER_TRAVEL_TIME_CONFIG,
    )

    assert result["type"] is FlowResultType.CREATE_ENTRY
    assert result["data"][CONF_NAME] == "Seattle-Bellevue via I-90 (EB AM)"
    assert result["data"][CONF_ID] == 96


@pytest.mark.parametrize(
    "import_config",
    [
        {
            CONF_API_KEY: "abcd-5678",
            CONF_TRAVEL_TIMES: [{CONF_ID: 96, CONF_NAME: "I-90 EB"}],
        },
        {
            CONF_API_KEY: "abcd-5678",
            CONF_TRAVEL_TIMES: [{CONF_ID: "96", CONF_NAME: "I-90 EB"}],
        },
    ],
    ids=["with-int-id", "with-str-id"],
)
async def test_create_import_entry(
    hass: HomeAssistant,
    mock_travel_time: AsyncMock,
    import_config: dict[str, str | int],
) -> None:
    """Test that the yaml import works."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={"source": SOURCE_IMPORT},
        data=import_config,
    )

    assert result["type"] is FlowResultType.CREATE_ENTRY
    assert result["title"] == "wsdot"
    assert result["data"][CONF_API_KEY] == "abcd-5678"

    entry = result["result"]
    assert entry is not None
    assert len(entry.subentries) == 1
    subentry = next(iter(entry.subentries.values()))
    assert subentry.subentry_type == SUBENTRY_TRAVEL_TIMES
    assert subentry.title == "Seattle-Bellevue via I-90 (EB AM)"
    assert subentry.data[CONF_NAME] == "Seattle-Bellevue via I-90 (EB AM)"
    assert subentry.data[CONF_ID] == 96


@pytest.mark.parametrize(
    ("failed_travel_time_status", "abort_reason"),
    [
        (400, "invalid_api_key"),
        (404, "cannot_connect"),
    ],
)
async def test_failed_import_entry(
    hass: HomeAssistant,
    mock_failed_travel_time: AsyncMock,
    mock_config_data: dict[str, Any],
    failed_travel_time_status: int,
    abort_reason: str,
) -> None:
    """Test the failure modes of a yaml import."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={"source": SOURCE_IMPORT},
        data=mock_config_data,
    )

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


async def test_incorrect_import_entry(
    hass: HomeAssistant,
    mock_travel_time: AsyncMock,
    mock_config_data: dict[str, Any],
) -> None:
    """Test a yaml import of a non-existent route."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={"source": SOURCE_IMPORT},
        data={
            CONF_API_KEY: "abcd-5678",
            CONF_TRAVEL_TIMES: [{CONF_ID: "100001", CONF_NAME: "nowhere"}],
        },
    )

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


@pytest.mark.usefixtures("mock_setup_entry")
async def test_import_integration_already_exists(
    hass: HomeAssistant,
    mock_travel_time: AsyncMock,
    mock_config_entry: MockConfigEntry,
    init_integration: MockConfigEntry,
) -> None:
    """Test we only allow one entry per API key."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={"source": SOURCE_IMPORT},
        data={
            CONF_API_KEY: "abcd-1234",
            CONF_TRAVEL_TIMES: [{CONF_ID: "100001", CONF_NAME: "nowhere"}],
        },
    )

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


async def test_integration_already_exists(
    hass: HomeAssistant,
    mock_travel_time: AsyncMock,
    mock_config_entry: MockConfigEntry,
    init_integration: MockConfigEntry,
) -> None:
    """Test we only allow one entry per API key."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN,
        context={"source": SOURCE_USER},
    )

    assert result["type"] is FlowResultType.FORM
    assert result["step_id"] == "user"
    assert not result["errors"]

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        user_input=VALID_USER_CONFIG,
    )

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


async def test_travel_route_already_exists(
    hass: HomeAssistant,
    mock_travel_time: AsyncMock,
    mock_config_entry: MockConfigEntry,
    init_integration: MockConfigEntry,
) -> None:
    """Test we only allow choosing a travel time route once."""
    result = await hass.config_entries.subentries.async_init(
        (init_integration.entry_id, SUBENTRY_TRAVEL_TIMES),
        context={"source": SOURCE_USER},
    )

    assert result["type"] is FlowResultType.FORM
    assert result["step_id"] == "user"
    assert not result["errors"]

    result = await hass.config_entries.subentries.async_configure(
        result["flow_id"],
        user_input=VALID_USER_TRAVEL_TIME_CONFIG,
    )

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