"""Test the initialization of fujitsu_fglair entities."""

from unittest.mock import AsyncMock, patch

from ayla_iot_unofficial import AylaAuthError
from ayla_iot_unofficial.fujitsu_consts import FGLAIR_APP_CREDENTIALS
from freezegun.api import FrozenDateTimeFactory
import pytest

from homeassistant.components.climate import HVACMode
from homeassistant.components.fujitsu_fglair.const import (
    API_REFRESH,
    API_TIMEOUT,
    CONF_EUROPE,
    CONF_REGION,
    DOMAIN,
    REGION_DEFAULT,
    REGION_EU,
)
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, STATE_UNAVAILABLE
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client

from . import entity_id, setup_integration
from .conftest import TEST_PASSWORD, TEST_USERNAME

from tests.common import MockConfigEntry, async_fire_time_changed


async def test_auth_failure(
    hass: HomeAssistant,
    freezer: FrozenDateTimeFactory,
    mock_ayla_api: AsyncMock,
    mock_config_entry: MockConfigEntry,
    mock_devices: list[AsyncMock],
) -> None:
    """Test entities become unavailable after auth failure."""
    await setup_integration(hass, mock_config_entry)

    mock_ayla_api.async_get_devices.side_effect = AylaAuthError
    freezer.tick(API_REFRESH)
    async_fire_time_changed(hass)
    await hass.async_block_till_done()

    assert hass.states.get(entity_id(mock_devices[0])).state == STATE_UNAVAILABLE
    assert hass.states.get(entity_id(mock_devices[1])).state == STATE_UNAVAILABLE


@pytest.mark.parametrize(
    "mock_config_entry", FGLAIR_APP_CREDENTIALS.keys(), indirect=True
)
async def test_auth_regions(
    hass: HomeAssistant,
    freezer: FrozenDateTimeFactory,
    mock_ayla_api: AsyncMock,
    mock_config_entry: MockConfigEntry,
    mock_devices: list[AsyncMock],
) -> None:
    """Test that we use the correct credentials if europe is selected."""
    with patch(
        "homeassistant.components.fujitsu_fglair.new_ayla_api", return_value=AsyncMock()
    ) as new_ayla_api_patch:
        await setup_integration(hass, mock_config_entry)
        new_ayla_api_patch.assert_called_once_with(
            TEST_USERNAME,
            TEST_PASSWORD,
            FGLAIR_APP_CREDENTIALS[mock_config_entry.data[CONF_REGION]][0],
            FGLAIR_APP_CREDENTIALS[mock_config_entry.data[CONF_REGION]][1],
            europe=mock_config_entry.data[CONF_REGION] == "EU",
            websession=aiohttp_client.async_get_clientsession(hass),
            timeout=API_TIMEOUT,
        )


@pytest.mark.parametrize("is_europe", [True, False])
async def test_migrate_entry_v11_v12(
    hass: HomeAssistant,
    freezer: FrozenDateTimeFactory,
    mock_ayla_api: AsyncMock,
    is_europe: bool,
    mock_devices: list[AsyncMock],
) -> None:
    """Test migration from schema 1.1 to 1.2."""
    v11_config_entry = MockConfigEntry(
        domain=DOMAIN,
        unique_id=TEST_USERNAME,
        data={
            CONF_USERNAME: TEST_USERNAME,
            CONF_PASSWORD: TEST_PASSWORD,
            CONF_EUROPE: is_europe,
        },
    )

    await setup_integration(hass, v11_config_entry)
    updated_entry = hass.config_entries.async_get_entry(v11_config_entry.entry_id)

    assert updated_entry.state is ConfigEntryState.LOADED
    assert updated_entry.version == 1
    assert updated_entry.minor_version == 2
    if is_europe:
        assert updated_entry.data[CONF_REGION] is REGION_EU
    else:
        assert updated_entry.data[CONF_REGION] is REGION_DEFAULT


async def test_device_auth_failure(
    hass: HomeAssistant,
    freezer: FrozenDateTimeFactory,
    mock_ayla_api: AsyncMock,
    mock_config_entry: MockConfigEntry,
    mock_devices: list[AsyncMock],
) -> None:
    """Test entities become unavailable after auth failure with updating devices."""
    await setup_integration(hass, mock_config_entry)

    for d in mock_ayla_api.async_get_devices.return_value:
        d.async_update.side_effect = AylaAuthError

    freezer.tick(API_REFRESH)
    async_fire_time_changed(hass)
    await hass.async_block_till_done()

    assert hass.states.get(entity_id(mock_devices[0])).state == STATE_UNAVAILABLE
    assert hass.states.get(entity_id(mock_devices[1])).state == STATE_UNAVAILABLE


async def test_device_offline(
    hass: HomeAssistant,
    freezer: FrozenDateTimeFactory,
    mock_ayla_api: AsyncMock,
    mock_config_entry: MockConfigEntry,
    mock_devices: list[AsyncMock],
) -> None:
    """Test entities become unavailable if device if offline."""
    await setup_integration(hass, mock_config_entry)

    mock_ayla_api.async_get_devices.return_value[0].is_online.return_value = False

    freezer.tick(API_REFRESH)
    async_fire_time_changed(hass)
    await hass.async_block_till_done()

    assert hass.states.get(entity_id(mock_devices[0])).state == STATE_UNAVAILABLE
    assert hass.states.get(entity_id(mock_devices[1])).state == HVACMode.COOL


async def test_token_expired(
    hass: HomeAssistant,
    mock_ayla_api: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Make sure sign_in is called if the token expired."""
    mock_ayla_api.token_expired = True
    await setup_integration(hass, mock_config_entry)

    # Called once during setup and once during update
    assert mock_ayla_api.async_sign_in.call_count == 2


async def test_token_expiring_soon(
    hass: HomeAssistant,
    mock_ayla_api: AsyncMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Make sure sign_in is called if the token expired."""
    mock_ayla_api.token_expiring_soon = True
    await setup_integration(hass, mock_config_entry)

    mock_ayla_api.async_refresh_auth.assert_called_once()


@pytest.mark.parametrize("exception", [AylaAuthError, TimeoutError])
async def test_startup_exception(
    hass: HomeAssistant,
    mock_ayla_api: AsyncMock,
    mock_config_entry: MockConfigEntry,
    exception: Exception,
) -> None:
    """Make sure no devices are added if login raises an exception."""
    mock_ayla_api.async_sign_in.side_effect = exception
    await setup_integration(hass, mock_config_entry)

    assert len(hass.states.async_all()) == 0
