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

from http import HTTPStatus
from unittest.mock import patch

from freezegun.api import FrozenDateTimeFactory
import pytest
from requests.exceptions import ReadTimeout
from syrupy.assertion import SnapshotAssertion

from homeassistant.components.fritz.const import DOMAIN, SCAN_INTERVAL
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 import device_registry as dr, entity_registry as er
from homeassistant.helpers.entity_registry import EntityRegistry
from homeassistant.util import slugify

from .const import (
    MOCK_FB_SERVICES,
    MOCK_MESH_MASTER_MAC,
    MOCK_SERIAL_NUMBER,
    MOCK_USER_DATA,
)

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

GUEST_WIFI_CHANGED: dict[str, dict] = {
    "WLANConfiguration1": {},
    "WLANConfiguration2": {
        "GetBeaconAdvertisement": {"NewBeaconAdvertisementEnabled": 1},
        "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] = {
    "WLANConfiguration1": {},
    "WLANConfiguration2": {
        "GetBeaconAdvertisement": {"NewBeaconAdvertisementEnabled": 1},
        "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}),
        ({**MOCK_FB_SERVICES, **GUEST_WIFI_DISABLED}),
    ],
)
async def test_image_entity(
    hass: HomeAssistant,
    hass_client: ClientSessionGenerator,
    entity_registry: er.EntityRegistry,
    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",
    }

    assert (state := entity_registry.async_get("image.mock_title_guestwifi"))
    assert state.unique_id == f"{MOCK_SERIAL_NUMBER}-guest_wifi_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})])
async def test_image_update(
    hass: HomeAssistant,
    hass_client: ClientSessionGenerator,
    snapshot: SnapshotAssertion,
    freezer: FrozenDateTimeFactory,
    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})

    freezer.tick(SCAN_INTERVAL)
    async_fire_time_changed(hass)
    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})])
async def test_image_update_unavailable(
    hass: HomeAssistant,
    freezer: FrozenDateTimeFactory,
    fc_class_mock,
    fh_class_mock,
) -> None:
    """Test image update when fritzbox is unavailable."""

    entity_id = "image.mock_title_guestwifi"

    # 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

    assert hass.states.get(entity_id)

    # fritzbox becomes unavailable
    fc_class_mock().call_action_side_effect(ReadTimeout)

    freezer.tick(SCAN_INTERVAL)
    async_fire_time_changed(hass)
    await hass.async_block_till_done(wait_background_tasks=True)

    assert (state := hass.states.get(entity_id))
    assert state.state == STATE_UNKNOWN

    # fritzbox is available again
    fc_class_mock().call_action_side_effect(None)

    freezer.tick(SCAN_INTERVAL)
    async_fire_time_changed(hass)
    await hass.async_block_till_done(wait_background_tasks=True)

    assert (state := hass.states.get(entity_id))
    assert state.state != STATE_UNKNOWN


async def test_migrate_to_new_unique_id(
    hass: HomeAssistant,
    fc_class_mock,
    fh_class_mock,
    entity_registry: EntityRegistry,
    device_registry: dr.DeviceRegistry,
) -> None:
    """Test migrate from old unique id to new unique id."""

    mock_unique_id = "1234567890"

    entry = MockConfigEntry(
        domain=DOMAIN,
        data=MOCK_USER_DATA,
        unique_id=mock_unique_id,
    )
    entry.add_to_hass(hass)

    old_unique_id = slugify(f"{MOCK_SERIAL_NUMBER}-GuestWifi-qr-code")
    new_unique_id = f"{MOCK_SERIAL_NUMBER}-guest_wifi_qr_code"

    entity_registry.async_get_or_create(
        suggested_object_id="mock_title_mywifi",
        disabled_by=None,
        domain=Platform.IMAGE,
        platform=DOMAIN,
        unique_id=old_unique_id,
        config_entry=entry,
    )

    device_registry.async_get_or_create(
        config_entry_id=entry.entry_id,
        identifiers={(DOMAIN, MOCK_SERIAL_NUMBER)},
        connections={(dr.CONNECTION_NETWORK_MAC, MOCK_MESH_MASTER_MAC)},
    )
    await hass.async_block_till_done()

    entity_entry = entity_registry.async_get("image.mock_title_mywifi")
    assert entity_entry
    assert entity_entry.unique_id == old_unique_id

    with patch("homeassistant.components.fritz.PLATFORMS", [Platform.IMAGE]):
        await hass.config_entries.async_setup(entry.entry_id)
    await hass.async_block_till_done()

    entity_entry = entity_registry.async_get("image.mock_title_mywifi")
    assert entity_entry
    assert entity_entry.unique_id == new_unique_id
