"""Test the OpenRouter config flow."""

from unittest.mock import AsyncMock

import pytest
from python_open_router import OpenRouterError

from homeassistant.components.open_router.const import (
    CONF_PROMPT,
    CONF_WEB_SEARCH,
    DOMAIN,
)
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_API_KEY, CONF_LLM_HASS_API, CONF_MODEL
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType

from . import get_subentry_id, setup_integration

from tests.common import MockConfigEntry


@pytest.mark.usefixtures("mock_setup_entry")
async def test_full_flow(
    hass: HomeAssistant, mock_open_router_client: AsyncMock
) -> None:
    """Test the full config flow."""

    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": SOURCE_USER}
    )
    assert result["type"] is FlowResultType.FORM
    assert not result["errors"]
    assert result["step_id"] == "user"

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"], {CONF_API_KEY: "bla"}
    )

    assert result["type"] is FlowResultType.CREATE_ENTRY
    assert result["title"] == "Test account"
    assert result["data"] == {CONF_API_KEY: "bla"}


@pytest.mark.usefixtures("mock_setup_entry")
async def test_second_account(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test that a second account with a different API key can be added."""
    mock_config_entry.add_to_hass(hass)

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

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        {CONF_API_KEY: "different_key"},
    )

    assert result["type"] is FlowResultType.CREATE_ENTRY
    assert result["title"] == "Test account"
    assert result["data"] == {CONF_API_KEY: "different_key"}


@pytest.mark.parametrize(
    ("exception", "error"),
    [
        (OpenRouterError("exception"), "cannot_connect"),
        (Exception, "unknown"),
    ],
)
@pytest.mark.usefixtures("mock_setup_entry")
async def test_form_errors(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    exception: Exception,
    error: str,
) -> None:
    """Test we handle errors from the OpenRouter API."""
    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": SOURCE_USER}
    )

    mock_open_router_client.get_key_data.side_effect = exception

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        {CONF_API_KEY: "bla"},
    )

    assert result["type"] is FlowResultType.FORM
    assert result["errors"] == {"base": error}

    mock_open_router_client.get_key_data.side_effect = None

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        {CONF_API_KEY: "bla"},
    )

    assert result["type"] is FlowResultType.CREATE_ENTRY


@pytest.mark.usefixtures("mock_setup_entry")
async def test_duplicate_entry(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test aborting the flow if an entry already exists."""

    mock_config_entry.add_to_hass(hass)

    result = await hass.config_entries.flow.async_init(
        DOMAIN, context={"source": SOURCE_USER}
    )
    assert result["type"] is FlowResultType.FORM
    assert not result["errors"]
    assert result["step_id"] == "user"

    result = await hass.config_entries.flow.async_configure(
        result["flow_id"],
        {CONF_API_KEY: "bla"},
    )

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


async def test_create_conversation_agent(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_openai_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test creating a conversation agent."""
    await setup_integration(hass, mock_config_entry)

    result = await hass.config_entries.subentries.async_init(
        (mock_config_entry.entry_id, "conversation"),
        context={"source": SOURCE_USER},
    )
    assert result["type"] is FlowResultType.FORM
    assert not result["errors"]
    assert result["step_id"] == "init"

    assert result["data_schema"].schema["model"].config["options"] == [
        {"value": "openai/gpt-3.5-turbo", "label": "OpenAI: GPT-3.5 Turbo"},
        {"value": "openai/gpt-4", "label": "OpenAI: GPT-4"},
    ]

    result = await hass.config_entries.subentries.async_configure(
        result["flow_id"],
        {
            CONF_MODEL: "openai/gpt-3.5-turbo",
            CONF_PROMPT: "you are an assistant",
            CONF_LLM_HASS_API: ["assist"],
            CONF_WEB_SEARCH: "off",
        },
    )

    assert result["type"] is FlowResultType.CREATE_ENTRY
    assert result["data"] == {
        CONF_MODEL: "openai/gpt-3.5-turbo",
        CONF_PROMPT: "you are an assistant",
        CONF_LLM_HASS_API: ["assist"],
        CONF_WEB_SEARCH: "off",
    }


async def test_create_conversation_agent_no_control(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_openai_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test creating a conversation agent without control over the LLM API."""
    await setup_integration(hass, mock_config_entry)

    result = await hass.config_entries.subentries.async_init(
        (mock_config_entry.entry_id, "conversation"),
        context={"source": SOURCE_USER},
    )
    assert result["type"] is FlowResultType.FORM
    assert not result["errors"]
    assert result["step_id"] == "init"

    assert result["data_schema"].schema["model"].config["options"] == [
        {"value": "openai/gpt-3.5-turbo", "label": "OpenAI: GPT-3.5 Turbo"},
        {"value": "openai/gpt-4", "label": "OpenAI: GPT-4"},
    ]

    result = await hass.config_entries.subentries.async_configure(
        result["flow_id"],
        {
            CONF_MODEL: "openai/gpt-3.5-turbo",
            CONF_PROMPT: "you are an assistant",
            CONF_LLM_HASS_API: [],
            CONF_WEB_SEARCH: "off",
        },
    )

    assert result["type"] is FlowResultType.CREATE_ENTRY
    assert result["data"] == {
        CONF_MODEL: "openai/gpt-3.5-turbo",
        CONF_PROMPT: "you are an assistant",
        CONF_WEB_SEARCH: "off",
    }


async def test_create_ai_task(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_openai_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test creating an AI Task."""
    await setup_integration(hass, mock_config_entry)

    result = await hass.config_entries.subentries.async_init(
        (mock_config_entry.entry_id, "ai_task_data"),
        context={"source": SOURCE_USER},
    )
    assert result["type"] is FlowResultType.FORM
    assert not result["errors"]
    assert result["step_id"] == "init"

    assert result["data_schema"].schema["model"].config["options"] == [
        {"value": "openai/gpt-4", "label": "OpenAI: GPT-4"},
    ]

    result = await hass.config_entries.subentries.async_configure(
        result["flow_id"],
        {CONF_MODEL: "openai/gpt-4"},
    )

    assert result["type"] is FlowResultType.CREATE_ENTRY
    assert result["data"] == {CONF_MODEL: "openai/gpt-4"}


@pytest.mark.parametrize(
    "subentry_type",
    ["conversation", "ai_task_data"],
)
@pytest.mark.parametrize(
    ("exception", "reason"),
    [(OpenRouterError("exception"), "cannot_connect"), (Exception, "unknown")],
)
async def test_subentry_exceptions(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_openai_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
    subentry_type: str,
    exception: Exception,
    reason: str,
) -> None:
    """Test subentry flow exceptions."""
    await setup_integration(hass, mock_config_entry)

    mock_open_router_client.get_models.side_effect = exception

    result = await hass.config_entries.subentries.async_init(
        (mock_config_entry.entry_id, subentry_type),
        context={"source": SOURCE_USER},
    )
    assert result["type"] is FlowResultType.ABORT
    assert result["reason"] == reason


async def test_reconfigure_conversation_agent(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_openai_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test reconfiguring a conversation agent."""
    await setup_integration(hass, mock_config_entry)

    subentry_id = get_subentry_id(mock_config_entry, "conversation")

    # Now reconfigure it
    result = await mock_config_entry.start_subentry_reconfigure_flow(hass, subentry_id)
    assert result["type"] is FlowResultType.FORM
    assert result["step_id"] == "init"

    # Update the configuration
    result = await hass.config_entries.subentries.async_configure(
        result["flow_id"],
        {
            CONF_MODEL: "openai/gpt-4",
            CONF_PROMPT: "updated prompt",
            CONF_LLM_HASS_API: ["assist"],
            CONF_WEB_SEARCH: "off",
        },
    )

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

    subentry = mock_config_entry.subentries[subentry_id]
    assert subentry.data[CONF_MODEL] == "openai/gpt-4"
    assert subentry.data[CONF_PROMPT] == "updated prompt"
    assert subentry.data[CONF_LLM_HASS_API] == ["assist"]
    assert subentry.data[CONF_WEB_SEARCH] == "off"


async def test_reconfigure_ai_task(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_openai_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test reconfiguring an AI task."""
    await setup_integration(hass, mock_config_entry)

    subentry_id = get_subentry_id(mock_config_entry, "ai_task_data")

    result = await mock_config_entry.start_subentry_reconfigure_flow(hass, subentry_id)
    assert result["type"] is FlowResultType.FORM
    assert result["step_id"] == "init"

    # Update the configuration
    result = await hass.config_entries.subentries.async_configure(
        result["flow_id"],
        {CONF_MODEL: "openai/gpt-4"},
    )

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


@pytest.mark.parametrize(
    "subentry_type",
    ["conversation", "ai_task_data"],
)
async def test_reconfigure_entry_not_loaded(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_openai_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
    subentry_type: str,
) -> None:
    """Test reconfiguring an AI task."""
    mock_config_entry.add_to_hass(hass)

    result = await hass.config_entries.subentries.async_init(
        (mock_config_entry.entry_id, subentry_type),
        context={"source": SOURCE_USER},
    )

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


@pytest.mark.parametrize(
    ("exception", "reason"),
    [(OpenRouterError("exception"), "cannot_connect"), (Exception, "unknown")],
)
async def test_reconfigure_conversation_agent_abort(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_openai_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
    exception: Exception,
    reason: str,
) -> None:
    """Test reconfiguring a conversation agent with error and recovery."""
    await setup_integration(hass, mock_config_entry)

    subentry_id = get_subentry_id(mock_config_entry, "conversation")

    mock_open_router_client.get_models.side_effect = exception

    result = await mock_config_entry.start_subentry_reconfigure_flow(hass, subentry_id)
    assert result["type"] is FlowResultType.ABORT
    assert result["reason"] == reason


@pytest.mark.parametrize(
    ("exception", "reason"),
    [(OpenRouterError("exception"), "cannot_connect"), (Exception, "unknown")],
)
async def test_reconfigure_ai_task_abort(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_openai_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
    exception: Exception,
    reason: str,
) -> None:
    """Test reconfiguring an AI task with error and recovery."""
    await setup_integration(hass, mock_config_entry)

    subentry_id = get_subentry_id(mock_config_entry, "ai_task_data")

    # Trigger an error during reconfiguration
    mock_open_router_client.get_models.side_effect = exception

    result = await mock_config_entry.start_subentry_reconfigure_flow(hass, subentry_id)
    assert result["type"] is FlowResultType.ABORT
    assert result["reason"] == reason


@pytest.mark.parametrize(
    ("web_search", "expected_web_search"),
    [("plugin", "plugin"), ("off", "off")],
    indirect=["web_search"],
)
@pytest.mark.usefixtures("mock_setup_entry")
async def test_create_conversation_agent_web_search(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
    web_search: str,
    expected_web_search: str,
) -> None:
    """Test creating a conversation agent with web search enabled/disabled."""
    await setup_integration(hass, mock_config_entry)

    result = await hass.config_entries.subentries.async_init(
        (mock_config_entry.entry_id, "conversation"),
        context={"source": SOURCE_USER},
    )
    assert result["type"] is FlowResultType.FORM
    assert result["step_id"] == "init"

    # Verify web_search field is present in schema with correct default
    schema = result["data_schema"].schema
    key = next(k for k in schema if k == CONF_WEB_SEARCH)
    assert key.default() == "off"

    result = await hass.config_entries.subentries.async_configure(
        result["flow_id"],
        {
            CONF_MODEL: "openai/gpt-3.5-turbo",
            CONF_PROMPT: "you are an assistant",
            CONF_LLM_HASS_API: ["assist"],
            CONF_WEB_SEARCH: expected_web_search,
        },
    )

    assert result["type"] is FlowResultType.CREATE_ENTRY
    assert result["data"][CONF_WEB_SEARCH] is expected_web_search


@pytest.mark.parametrize(
    ("current_web_search", "expected_default"),
    [("plugin", "plugin"), ("off", "off")],
)
@pytest.mark.usefixtures("mock_setup_entry")
async def test_reconfigure_conversation_subentry_web_search_default(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
    current_web_search: str,
    expected_default: str,
) -> None:
    """Test web_search field default reflects existing value when reconfiguring."""
    await setup_integration(hass, mock_config_entry)

    subentry = next(iter(mock_config_entry.subentries.values()))
    hass.config_entries.async_update_subentry(
        mock_config_entry,
        subentry,
        data={**subentry.data, CONF_WEB_SEARCH: current_web_search},
    )
    await hass.async_block_till_done()

    result = await mock_config_entry.start_subentry_reconfigure_flow(
        hass, subentry.subentry_id
    )

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

    schema = result["data_schema"].schema
    key = next(k for k in schema if k == CONF_WEB_SEARCH)
    assert key.default() is expected_default


@pytest.mark.parametrize(
    ("current_llm_apis", "suggested_llm_apis", "expected_options"),
    [
        (["assist"], ["assist"], ["assist"]),
        (["non-existent"], [], ["assist"]),
        (["assist", "non-existent"], ["assist"], ["assist"]),
    ],
)
@pytest.mark.usefixtures("mock_setup_entry")
async def test_reconfigure_conversation_subentry_llm_api_schema(
    hass: HomeAssistant,
    mock_open_router_client: AsyncMock,
    mock_config_entry: MockConfigEntry,
    current_llm_apis: list[str],
    suggested_llm_apis: list[str],
    expected_options: list[str],
) -> None:
    """Test llm_hass_api field values when reconfiguring a conversation subentry."""
    await setup_integration(hass, mock_config_entry)

    subentry = next(iter(mock_config_entry.subentries.values()))
    hass.config_entries.async_update_subentry(
        mock_config_entry,
        subentry,
        data={**subentry.data, CONF_LLM_HASS_API: current_llm_apis},
    )
    await hass.async_block_till_done()

    result = await mock_config_entry.start_subentry_reconfigure_flow(
        hass, subentry.subentry_id
    )

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

    # Only valid LLM APIs should be suggested and shown as options
    schema = result["data_schema"].schema
    key = next(k for k in schema if k == CONF_LLM_HASS_API)

    assert key.default() == suggested_llm_apis

    field_schema = schema[key]
    assert field_schema.config
    assert [
        opt["value"] for opt in field_schema.config.get("options")
    ] == expected_options
