"""Tests for the WLED update platform."""

from collections.abc import Generator
from unittest.mock import MagicMock, patch

from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy.assertion import SnapshotAssertion
from wled import Releases, WLEDError

from homeassistant.components.update import (
    ATTR_INSTALLED_VERSION,
    ATTR_LATEST_VERSION,
    DOMAIN as UPDATE_DOMAIN,
    SERVICE_INSTALL,
)
from homeassistant.components.wled.const import RELEASES_SCAN_INTERVAL
from homeassistant.const import (
    ATTR_ENTITY_ID,
    STATE_OFF,
    STATE_ON,
    STATE_UNAVAILABLE,
    STATE_UNKNOWN,
    Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er

from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform

pytestmark = pytest.mark.usefixtures("init_integration")


@pytest.fixture(autouse=True)
def override_platforms() -> Generator[None]:
    """Override PLATFORMS."""
    with patch("homeassistant.components.wled.PLATFORMS", [Platform.UPDATE]):
        yield


async def test_update_available(
    hass: HomeAssistant,
    entity_registry: er.EntityRegistry,
    snapshot: SnapshotAssertion,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test states of the update."""
    await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)

    assert (state := hass.states.get("update.wled_rgb_light_firmware"))
    assert state.state == STATE_ON

    assert snapshot == state


async def test_update_information_available(
    hass: HomeAssistant,
    freezer: FrozenDateTimeFactory,
    entity_registry: er.EntityRegistry,
    mock_wled_releases: MagicMock,
    snapshot: SnapshotAssertion,
) -> None:
    """Test having no update information available at all."""
    mock_wled_releases.releases.return_value = Releases(
        beta=None,
        nightly=None,
        repo="https://github.com/wled/WLED",
        stable=None,
    )

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

    assert (state := hass.states.get("update.wled_rgb_light_firmware"))
    assert state.state == STATE_UNKNOWN

    assert snapshot == state


@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize("device_fixture", ["rgb_websocket"])
async def test_no_update_available(
    hass: HomeAssistant,
    entity_registry: er.EntityRegistry,
    snapshot: SnapshotAssertion,
) -> None:
    """Test there is no update available."""
    assert (state := hass.states.get("update.wled_websocket_firmware"))
    assert state.state == STATE_OFF

    assert snapshot == state


async def test_update_error(
    hass: HomeAssistant,
    mock_wled: MagicMock,
    caplog: pytest.LogCaptureFixture,
) -> None:
    """Test error handling of the WLED update."""
    mock_wled.update.side_effect = WLEDError

    await hass.services.async_call(
        UPDATE_DOMAIN,
        SERVICE_INSTALL,
        {ATTR_ENTITY_ID: "update.wled_rgb_light_firmware"},
        blocking=True,
    )

    assert (state := hass.states.get("update.wled_rgb_light_firmware"))
    assert state.state == STATE_UNAVAILABLE
    assert "Invalid response from WLED API" in caplog.text


async def test_update_stay_stable(
    hass: HomeAssistant,
    mock_wled: MagicMock,
) -> None:
    """Test the update entity staying on stable.

    There is both an update for beta and stable available, however, the device
    is currently running a stable version. Therefore, the update entity should
    update to the next stable (even though beta is newer).
    """
    assert (state := hass.states.get("update.wled_rgb_light_firmware"))
    assert state.state == STATE_ON
    assert state.attributes[ATTR_INSTALLED_VERSION] == "0.14.4"
    assert state.attributes[ATTR_LATEST_VERSION] == "0.99.0"

    await hass.services.async_call(
        UPDATE_DOMAIN,
        SERVICE_INSTALL,
        {ATTR_ENTITY_ID: "update.wled_rgb_light_firmware"},
        blocking=True,
    )
    assert mock_wled.upgrade.call_count == 1
    mock_wled.upgrade.assert_called_with(version="0.99.0")


@pytest.mark.parametrize("device_fixture", ["rgbw"])
async def test_update_beta_to_stable(
    hass: HomeAssistant,
    mock_wled: MagicMock,
) -> None:
    """Test the update entity.

    There is both an update for beta and stable available and the device
    is currently a beta, however, a newer stable is available. Therefore, the
    update entity should update to the next stable.
    """
    assert (state := hass.states.get("update.wled_rgbw_light_firmware"))
    assert state.state == STATE_ON
    assert state.attributes[ATTR_INSTALLED_VERSION] == "0.99.0b1"
    assert state.attributes[ATTR_LATEST_VERSION] == "0.99.0"

    await hass.services.async_call(
        UPDATE_DOMAIN,
        SERVICE_INSTALL,
        {ATTR_ENTITY_ID: "update.wled_rgbw_light_firmware"},
        blocking=True,
    )
    assert mock_wled.upgrade.call_count == 1
    mock_wled.upgrade.assert_called_with(version="0.99.0")


@pytest.mark.parametrize("device_fixture", ["rgb_single_segment"])
async def test_update_stay_beta(
    hass: HomeAssistant,
    mock_wled: MagicMock,
) -> None:
    """Test the update entity.

    There is an update for beta and the device is currently a beta. Therefore,
    the update entity should update to the next beta.
    """
    assert (state := hass.states.get("update.wled_rgb_light_firmware"))
    assert state.state == STATE_ON
    assert state.attributes[ATTR_INSTALLED_VERSION] == "1.0.0b4"
    assert state.attributes[ATTR_LATEST_VERSION] == "1.0.0b5"

    await hass.services.async_call(
        UPDATE_DOMAIN,
        SERVICE_INSTALL,
        {ATTR_ENTITY_ID: "update.wled_rgb_light_firmware"},
        blocking=True,
    )
    assert mock_wled.upgrade.call_count == 1
    mock_wled.upgrade.assert_called_with(version="1.0.0b5")
