"""Tests for Fritz!Tools image platform."""

from datetime import timedelta
from http import HTTPStatus
from unittest.mock import patch

import pytest
from requests.exceptions import ReadTimeout
from syrupy.assertion import SnapshotAssertion

from homeassistant.components.fritz.const import DOMAIN
from homeassistant.components.image import DOMAIN as IMAGE_DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_registry import async_get as async_get_entity_registry
from homeassistant.util.dt import utcnow

from .const import MOCK_FB_SERVICES, MOCK_USER_DATA

from tests.common import MockConfigEntry, async_fire_time_changed
from tests.typing import ClientSessionGenerator

GUEST_WIFI_ENABLED: dict[str, dict] = {
    "WLANConfiguration0": {},
    "WLANConfiguration1": {
        "GetInfo": {
            "NewEnable": True,
            "NewStatus": "Up",
            "NewSSID": "GuestWifi",
            "NewBeaconType": "11iandWPA3",
            "NewX_AVM-DE_PossibleBeaconTypes": "None,11i,11iandWPA3",
            "NewStandard": "ax",
            "NewBSSID": "1C:ED:6F:12:34:13",
        },
        "GetSSID": {
            "NewSSID": "GuestWifi",
        },
        "GetSecurityKeys": {"NewKeyPassphrase": "1234567890"},
    },
}

GUEST_WIFI_CHANGED: dict[str, dict] = {
    "WLANConfiguration0": {},
    "WLANConfiguration1": {
        "GetInfo": {
            "NewEnable": True,
            "NewStatus": "Up",
            "NewSSID": "GuestWifi",
            "NewBeaconType": "11iandWPA3",
            "NewX_AVM-DE_PossibleBeaconTypes": "None,11i,11iandWPA3",
            "NewStandard": "ax",
            "NewBSSID": "1C:ED:6F:12:34:13",
        },
        "GetSSID": {
            "NewSSID": "GuestWifi",
        },
        "GetSecurityKeys": {"NewKeyPassphrase": "abcdefghij"},
    },
}

GUEST_WIFI_DISABLED: dict[str, dict] = {
    "WLANConfiguration0": {},
    "WLANConfiguration1": {
        "GetInfo": {
            "NewEnable": False,
            "NewStatus": "Up",
            "NewSSID": "GuestWifi",
            "NewBeaconType": "11iandWPA3",
            "NewX_AVM-DE_PossibleBeaconTypes": "None,11i,11iandWPA3",
            "NewStandard": "ax",
            "NewBSSID": "1C:ED:6F:12:34:13",
        },
        "GetSSID": {
            "NewSSID": "GuestWifi",
        },
        "GetSecurityKeys": {"NewKeyPassphrase": "1234567890"},
    },
}


@pytest.mark.parametrize(
    ("fc_data"),
    [
        ({**MOCK_FB_SERVICES, **GUEST_WIFI_ENABLED}),
        ({**MOCK_FB_SERVICES, **GUEST_WIFI_DISABLED}),
    ],
)
async def test_image_entity(
    hass: HomeAssistant,
    hass_client: ClientSessionGenerator,
    snapshot: SnapshotAssertion,
    fc_class_mock,
    fh_class_mock,
) -> None:
    """Test image entity."""

    # setup component with image platform only
    with patch(
        "homeassistant.components.fritz.PLATFORMS",
        [Platform.IMAGE],
    ):
        entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
        entry.add_to_hass(hass)
        await hass.config_entries.async_setup(entry.entry_id)

    await hass.async_block_till_done()
    assert entry.state is ConfigEntryState.LOADED

    # test image entity is generated as expected
    states = hass.states.async_all(IMAGE_DOMAIN)
    assert len(states) == 1

    state = states[0]
    assert state.name == "Mock Title GuestWifi"
    assert state.entity_id == "image.mock_title_guestwifi"

    access_token = state.attributes["access_token"]
    assert state.attributes == {
        "access_token": access_token,
        "entity_picture": f"/api/image_proxy/image.mock_title_guestwifi?token={access_token}",
        "friendly_name": "Mock Title GuestWifi",
    }

    entity_registry = async_get_entity_registry(hass)
    entity_entry = entity_registry.async_get("image.mock_title_guestwifi")
    assert entity_entry.unique_id == "1c_ed_6f_12_34_11_guestwifi_qr_code"

    # test image download
    client = await hass_client()
    resp = await client.get("/api/image_proxy/image.mock_title_guestwifi")
    assert resp.status == HTTPStatus.OK

    body = await resp.read()
    assert body == snapshot


@pytest.mark.parametrize(("fc_data"), [({**MOCK_FB_SERVICES, **GUEST_WIFI_ENABLED})])
async def test_image_update(
    hass: HomeAssistant,
    hass_client: ClientSessionGenerator,
    snapshot: SnapshotAssertion,
    fc_class_mock,
    fh_class_mock,
) -> None:
    """Test image update."""

    # setup component with image platform only
    with patch(
        "homeassistant.components.fritz.PLATFORMS",
        [Platform.IMAGE],
    ):
        entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
        entry.add_to_hass(hass)
        await hass.config_entries.async_setup(entry.entry_id)

    await hass.async_block_till_done()
    assert entry.state is ConfigEntryState.LOADED

    client = await hass_client()
    resp = await client.get("/api/image_proxy/image.mock_title_guestwifi")
    resp_body = await resp.read()
    assert resp.status == HTTPStatus.OK

    fc_class_mock().override_services({**MOCK_FB_SERVICES, **GUEST_WIFI_CHANGED})
    async_fire_time_changed(hass, utcnow() + timedelta(seconds=60))
    await hass.async_block_till_done(wait_background_tasks=True)

    resp = await client.get("/api/image_proxy/image.mock_title_guestwifi")
    resp_body_new = await resp.read()

    assert resp_body != resp_body_new
    assert resp_body_new == snapshot


@pytest.mark.parametrize(("fc_data"), [({**MOCK_FB_SERVICES, **GUEST_WIFI_ENABLED})])
async def test_image_update_unavailable(
    hass: HomeAssistant,
    fc_class_mock,
    fh_class_mock,
) -> None:
    """Test image update when fritzbox is unavailable."""

    # setup component with image platform only
    with patch(
        "homeassistant.components.fritz.PLATFORMS",
        [Platform.IMAGE],
    ):
        entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
        entry.add_to_hass(hass)
        await hass.config_entries.async_setup(entry.entry_id)

    await hass.async_block_till_done()
    assert entry.state is ConfigEntryState.LOADED

    state = hass.states.get("image.mock_title_guestwifi")
    assert state

    # fritzbox becomes unavailable
    fc_class_mock().call_action_side_effect(ReadTimeout)
    async_fire_time_changed(hass, utcnow() + timedelta(seconds=60))
    await hass.async_block_till_done(wait_background_tasks=True)

    state = hass.states.get("image.mock_title_guestwifi")
    assert state.state == STATE_UNKNOWN

    # fritzbox is available again
    fc_class_mock().call_action_side_effect(None)
    async_fire_time_changed(hass, utcnow() + timedelta(seconds=60))
    await hass.async_block_till_done(wait_background_tasks=True)

    state = hass.states.get("image.mock_title_guestwifi")
    assert state.state != STATE_UNKNOWN
