"""Tests for the Seko PoolDose number platform."""

from copy import deepcopy
from datetime import timedelta
from unittest.mock import AsyncMock

from freezegun.api import FrozenDateTimeFactory
from pooldose.request_status import RequestStatus
import pytest
from syrupy.assertion import SnapshotAssertion

from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN, SERVICE_SET_VALUE
from homeassistant.const import ATTR_ENTITY_ID, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import entity_registry as er

from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform


@pytest.fixture
def platforms() -> list[Platform]:
    """Fixture to specify platforms to test."""
    return [Platform.NUMBER]


@pytest.mark.usefixtures("entity_registry_enabled_by_default", "init_integration")
async def test_all_numbers(
    hass: HomeAssistant,
    snapshot: SnapshotAssertion,
    entity_registry: er.EntityRegistry,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test the Pooldose numbers."""
    await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)


@pytest.mark.usefixtures("init_integration")
async def test_number_entity_unavailable_no_coordinator_data(
    hass: HomeAssistant,
    init_integration: MockConfigEntry,
    mock_pooldose_client: AsyncMock,
    freezer: FrozenDateTimeFactory,
) -> None:
    """Test number entity becomes unavailable when coordinator has no data."""
    # Verify entity has a state initially
    ph_target_state = hass.states.get("number.pool_device_ph_target")
    assert ph_target_state.state == "6.5"

    # Update coordinator data to None
    mock_pooldose_client.instant_values_structured.return_value = (None, None)
    freezer.tick(timedelta(minutes=5))
    async_fire_time_changed(hass)
    await hass.async_block_till_done()

    # Check entity becomes unavailable
    ph_target_state = hass.states.get("number.pool_device_ph_target")
    assert ph_target_state.state == "unavailable"


@pytest.mark.usefixtures("init_integration")
async def test_number_state_changes(
    hass: HomeAssistant,
    init_integration: MockConfigEntry,
    mock_pooldose_client: AsyncMock,
    freezer: FrozenDateTimeFactory,
) -> None:
    """Test number state changes when coordinator updates."""
    # Initial state
    ph_target_state = hass.states.get("number.pool_device_ph_target")
    assert ph_target_state.state == "6.5"

    # Update coordinator data with number value changed
    current_data = mock_pooldose_client.instant_values_structured.return_value[1]
    updated_data = deepcopy(current_data)
    updated_data["number"]["ph_target"]["value"] = 7.2

    mock_pooldose_client.instant_values_structured.return_value = (
        RequestStatus.SUCCESS,
        updated_data,
    )

    freezer.tick(timedelta(minutes=5))
    async_fire_time_changed(hass)
    await hass.async_block_till_done()

    # Check state changed
    ph_target_state = hass.states.get("number.pool_device_ph_target")
    assert ph_target_state.state == "7.2"


@pytest.mark.usefixtures("init_integration")
async def test_set_number_value(
    hass: HomeAssistant,
    mock_pooldose_client: AsyncMock,
) -> None:
    """Test setting a number value."""
    # Verify initial state
    ph_target_state = hass.states.get("number.pool_device_ph_target")
    assert ph_target_state.state == "6.5"

    # Set new value
    await hass.services.async_call(
        NUMBER_DOMAIN,
        SERVICE_SET_VALUE,
        {ATTR_ENTITY_ID: "number.pool_device_ph_target", "value": 7.0},
        blocking=True,
    )

    # Verify API was called
    mock_pooldose_client.set_number.assert_called_once_with("ph_target", 7.0)

    # Verify state updated immediately (optimistic update)
    ph_target_state = hass.states.get("number.pool_device_ph_target")
    assert ph_target_state.state == "7.0"


@pytest.mark.usefixtures("init_integration")
async def test_actions_cannot_connect_number(
    hass: HomeAssistant,
    mock_pooldose_client: AsyncMock,
    init_integration: MockConfigEntry,
    snapshot: SnapshotAssertion,
) -> None:
    """Test ServiceValidationError on client write failure."""
    client = mock_pooldose_client
    entity_id = "number.pool_device_ph_target"
    before = hass.states.get(entity_id)
    assert before is not None

    client.is_connected = False
    client.set_number = AsyncMock(return_value=False)

    with pytest.raises(ServiceValidationError) as excinfo:
        await hass.services.async_call(
            "number", "set_value", {"entity_id": entity_id, "value": 7.0}, blocking=True
        )

    assert excinfo.value.translation_key == "cannot_connect"

    after = hass.states.get(entity_id)
    assert after is not None
    assert before.state == after.state


@pytest.mark.usefixtures("init_integration")
async def test_actions_write_rejected_number(
    hass: HomeAssistant,
    mock_pooldose_client: AsyncMock,
    init_integration: MockConfigEntry,
    snapshot: SnapshotAssertion,
) -> None:
    """Test ServiceValidationError when write is rejected."""
    client = mock_pooldose_client
    entity_id = "number.pool_device_ph_target"
    before = hass.states.get(entity_id)
    assert before is not None

    client.set_number = AsyncMock(return_value=False)

    with pytest.raises(ServiceValidationError) as excinfo:
        await hass.services.async_call(
            "number", "set_value", {"entity_id": entity_id, "value": 7.0}, blocking=True
        )

    assert excinfo.value.translation_key == "write_rejected"

    after = hass.states.get(entity_id)
    assert after is not None
    assert before.state == after.state
