"""Test Velux light entities."""

from unittest.mock import AsyncMock, MagicMock

import pytest

from homeassistant.components.light import (
    ATTR_BRIGHTNESS,
    DOMAIN as LIGHT_DOMAIN,
    SERVICE_TURN_OFF,
    SERVICE_TURN_ON,
)
from homeassistant.const import STATE_OFF, STATE_ON, STATE_UNAVAILABLE, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er

from . import update_callback_entity

from tests.common import MockConfigEntry, SnapshotAssertion, snapshot_platform

# Apply setup_integration fixture to all tests in this module
pytestmark = pytest.mark.usefixtures("setup_integration")


@pytest.fixture
def platform() -> Platform:
    """Fixture to specify platform to test."""
    return Platform.LIGHT


@pytest.mark.parametrize(
    "mock_pyvlx", ["mock_light", "mock_onoff_light"], indirect=True
)
async def test_light_setup(
    hass: HomeAssistant,
    mock_config_entry: MockConfigEntry,
    entity_registry: er.EntityRegistry,
    snapshot: SnapshotAssertion,
) -> None:
    """Snapshot the entity and validate registry metadata for light entities."""
    await snapshot_platform(
        hass,
        entity_registry,
        snapshot,
        mock_config_entry.entry_id,
    )


async def test_light_device_association(
    hass: HomeAssistant,
    entity_registry: er.EntityRegistry,
    device_registry: dr.DeviceRegistry,
    mock_light: AsyncMock,
) -> None:
    """Test light device association."""

    test_entity_id = f"light.{mock_light.name.lower().replace(' ', '_')}"

    # Get entity + device entry
    entity_entry = entity_registry.async_get(test_entity_id)
    assert entity_entry is not None
    assert entity_entry.device_id is not None
    device_entry = device_registry.async_get(entity_entry.device_id)
    assert device_entry is not None

    # Verify device has correct identifiers + name
    assert ("velux", mock_light.serial_number) in device_entry.identifiers
    assert device_entry.name == mock_light.name


# This test is not light specific, it just uses the light platform
# to test the base entity class.
async def test_entity_callbacks(
    hass: HomeAssistant,
    mock_config_entry: MockConfigEntry,
    mock_light: AsyncMock,
) -> None:
    """Ensure the entity unregisters its device-updated callback when unloaded."""
    # Entity is created by setup_integration; callback should be registered
    test_entity_id = f"light.{mock_light.name.lower().replace(' ', '_')}"
    state = hass.states.get(test_entity_id)
    assert state is not None

    # Callback is registered exactly once with a callable
    assert mock_light.register_device_updated_cb.call_count == 1
    cb = mock_light.register_device_updated_cb.call_args[0][0]
    assert callable(cb)

    # Unload the config entry to trigger async_will_remove_from_hass
    assert await hass.config_entries.async_unload(mock_config_entry.entry_id)
    await hass.async_block_till_done()

    # Callback must be unregistered with the same callable
    assert mock_light.unregister_device_updated_cb.call_count == 1
    assert mock_light.unregister_device_updated_cb.call_args[0][0] is cb


# Test availability functionality by using the light platform
async def test_entity_availability(
    hass: HomeAssistant, mock_light: AsyncMock, caplog: pytest.LogCaptureFixture
) -> None:
    """Test that entity availability updates based on device connection status."""

    entity_id = f"light.{mock_light.name.lower().replace(' ', '_')}"

    # Initially connected
    mock_light.pyvlx.get_connected.return_value = True
    await update_callback_entity(hass, mock_light)
    state = hass.states.get(entity_id)
    assert state is not None
    assert state.state != STATE_UNAVAILABLE

    # Simulate disconnection
    mock_light.pyvlx.get_connected.return_value = False
    await update_callback_entity(hass, mock_light)
    state = hass.states.get(entity_id)
    assert state is not None
    assert state.state == STATE_UNAVAILABLE
    assert caplog.text.count(f"Entity {entity_id} is unavailable") == 1

    # Simulate disconnection, check we don't log again
    mock_light.pyvlx.get_connected.return_value = False
    await update_callback_entity(hass, mock_light)
    state = hass.states.get(entity_id)
    assert state is not None
    assert state.state == STATE_UNAVAILABLE
    assert caplog.text.count(f"Entity {entity_id} is unavailable") == 1

    # Simulate reconnection
    mock_light.pyvlx.get_connected.return_value = True
    await update_callback_entity(hass, mock_light)
    state = hass.states.get(entity_id)
    assert state is not None
    assert state.state != STATE_UNAVAILABLE
    assert caplog.text.count(f"Entity {entity_id} is back online") == 1

    # Simulate reconnection, check we don't log again
    mock_light.pyvlx.get_connected.return_value = True
    await update_callback_entity(hass, mock_light)
    state = hass.states.get(entity_id)
    assert state is not None
    assert state.state != STATE_UNAVAILABLE
    assert caplog.text.count(f"Entity {entity_id} is back online") == 1


async def test_light_brightness_and_is_on(
    hass: HomeAssistant, mock_light: AsyncMock
) -> None:
    """Validate brightness mapping and on/off state from intensity."""

    entity_id = f"light.{mock_light.name.lower().replace(' ', '_')}"

    # Set initial intensity values
    mock_light.intensity.intensity_percent = 20  # 20% "intensity" -> 20% brightness
    mock_light.intensity.off = False
    mock_light.intensity.known = True

    # Trigger state write
    await update_callback_entity(hass, mock_light)

    state = hass.states.get(entity_id)
    assert state is not None
    # brightness = int(20 * 255 / 100) = int(51)
    assert state.attributes.get(ATTR_BRIGHTNESS) == 51
    assert state.state == STATE_ON

    # Mark as off
    mock_light.intensity.off = True
    await update_callback_entity(hass, mock_light)
    state = hass.states.get(entity_id)
    assert state is not None
    assert state.state == STATE_OFF


async def test_light_turn_on_with_brightness_uses_set_intensity(
    hass: HomeAssistant, mock_light: AsyncMock
) -> None:
    """Turning on with brightness calls set_intensity with percent."""

    entity_id = f"light.{mock_light.name.lower().replace(' ', '_')}"

    # Call turn_on with brightness=51 (20% when normalized)
    await hass.services.async_call(
        LIGHT_DOMAIN,
        SERVICE_TURN_ON,
        {"entity_id": entity_id, ATTR_BRIGHTNESS: 51},
        blocking=True,
    )

    # set_intensity called once; turn_on should not be used in this path
    assert mock_light.set_intensity.await_count == 1
    assert mock_light.turn_on.await_count == 0

    # Inspect the intensity argument (first positional)
    args, kwargs = mock_light.set_intensity.await_args
    intensity_obj = args[0]
    # brightness 51 -> 20% normalized -> intensity_percent = 20
    assert intensity_obj.intensity_percent == 20
    assert kwargs.get("wait_for_completion") is True


@pytest.mark.parametrize(
    "mock_pyvlx", ["mock_light", "mock_onoff_light"], indirect=True
)
async def test_light_turn_on_without_brightness_calls_turn_on(
    hass: HomeAssistant, mock_pyvlx: MagicMock
) -> None:
    """Turning on without brightness uses node.turn_on."""

    node = mock_pyvlx.nodes[0]
    entity_id = f"light.{node.name.lower().replace(' ', '_')}"

    await hass.services.async_call(
        LIGHT_DOMAIN,
        SERVICE_TURN_ON,
        {"entity_id": entity_id},
        blocking=True,
    )

    node.turn_on.assert_awaited_once_with(wait_for_completion=True)
    assert node.set_intensity.await_count == 0


@pytest.mark.parametrize(
    "mock_pyvlx", ["mock_light", "mock_onoff_light"], indirect=True
)
async def test_light_turn_off_calls_turn_off(
    hass: HomeAssistant, mock_pyvlx: MagicMock
) -> None:
    """Turning off calls device.turn_off with wait_for_completion."""

    node = mock_pyvlx.nodes[0]
    entity_id = f"light.{node.name.lower().replace(' ', '_')}"

    await hass.services.async_call(
        LIGHT_DOMAIN,
        SERVICE_TURN_OFF,
        {"entity_id": entity_id},
        blocking=True,
    )

    node.turn_off.assert_awaited_once_with(wait_for_completion=True)
    assert node.set_intensity.await_count == 0
