"""Test Lutron integration setup."""

from typing import Any, cast
from unittest.mock import MagicMock

from pylutron import LutronException
import pytest

from homeassistant.components.lutron.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.setup import async_setup_component

from tests.common import MockConfigEntry


async def test_setup_entry(
    hass: HomeAssistant, mock_lutron: MagicMock, mock_config_entry: MockConfigEntry
) -> None:
    """Test setting up the integration."""
    mock_config_entry.add_to_hass(hass)

    assert await async_setup_component(hass, "lutron", {})
    await hass.async_block_till_done()

    assert mock_config_entry.runtime_data.client is mock_lutron
    assert len(mock_config_entry.runtime_data.lights) == 1

    # Verify that the unique ID is generated correctly.
    # This prevents regression in unique ID generation which would be a breaking change.
    entity_registry = er.async_get(hass)
    # The light from mock_lutron has uuid="light_uuid" and guid="12345678901"
    expected_unique_id = "12345678901_light_uuid"
    entry = entity_registry.async_get("light.test_light")
    assert entry.unique_id == expected_unique_id


async def test_unload_entry(
    hass: HomeAssistant, mock_lutron: MagicMock, mock_config_entry: MockConfigEntry
) -> None:
    """Test unloading the integration."""
    mock_config_entry.add_to_hass(hass)

    assert await async_setup_component(hass, "lutron", {})
    await hass.async_block_till_done()

    assert await hass.config_entries.async_unload(mock_config_entry.entry_id)
    await hass.async_block_till_done()


@pytest.mark.parametrize("method", ["load_xml_db", "connect"])
async def test_setup_entry_not_ready(
    hass: HomeAssistant,
    mock_lutron: MagicMock,
    mock_config_entry: MockConfigEntry,
    method: str,
) -> None:
    """Test setting up the integration when Lutron repeater is not ready."""
    mock_config_entry.add_to_hass(hass)

    getattr(mock_lutron, method).side_effect = LutronException(f"{method} failed")

    await hass.config_entries.async_setup(mock_config_entry.entry_id)
    await hass.async_block_till_done()

    assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY


async def test_unique_id_migration(
    hass: HomeAssistant, mock_lutron: MagicMock, mock_config_entry: MockConfigEntry
) -> None:
    """Test migration of legacy unique IDs to the newer UUID-based format.

    In older versions of the integration, unique IDs were based on a legacy UUID format.
    The integration now prefers a newer UUID format when available. This test ensures
    that existing entities and devices are automatically migrated to the new format
    without losing their registry entries.
    """
    mock_config_entry.add_to_hass(hass)

    # Setup registries with an entry using the "legacy" unique ID format.
    # This simulates a user who had configured the integration in an older version.
    entity_registry = er.async_get(hass)
    device_registry = dr.async_get(hass)

    legacy_unique_id = "12345678901_light_legacy_uuid"
    new_unique_id = "12345678901_light_uuid"

    # Create a device in the registry using the legacy ID
    device = device_registry.async_get_or_create(
        config_entry_id=mock_config_entry.entry_id,
        identifiers={(DOMAIN, legacy_unique_id)},
        manufacturer="Lutron",
        name="Test Light",
    )

    # Create an entity in the registry using the legacy ID
    entity = entity_registry.async_get_or_create(
        domain="light",
        platform="lutron",
        unique_id=legacy_unique_id,
        config_entry=mock_config_entry,
        device_id=device.id,
    )

    # Verify our starting state: registry holds the legacy ID
    assert entity.unique_id == legacy_unique_id
    assert (DOMAIN, legacy_unique_id) in device.identifiers

    # Trigger the integration setup.
    # The async_setup_entry logic will detect the legacy IDs in the registry
    # and update them to the new UUIDs provided by the mock_lutron fixture.
    assert await async_setup_component(hass, "lutron", {})
    await hass.async_block_till_done()

    # Verify that the entity's unique ID has been updated to the new format.
    entity = entity_registry.async_get(entity.entity_id)
    assert entity.unique_id == new_unique_id

    # Verify that the device's identifiers have also been migrated.
    device = device_registry.async_get(device.id)
    assert (DOMAIN, new_unique_id) in device.identifiers
    assert (DOMAIN, legacy_unique_id) not in device.identifiers


async def test_keypad_integer_migration(
    hass: HomeAssistant,
    device_registry: dr.DeviceRegistry,
    mock_lutron: MagicMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test migration from integer keypad ID to GUID-prefixed legacy UUID."""
    mock_config_entry.add_to_hass(hass)

    # Create a device with the old integer-based identifier
    device_registry.async_get_or_create(
        config_entry_id=mock_config_entry.entry_id,
        identifiers={(DOMAIN, cast(Any, 1))},
        manufacturer="Lutron",
        name="Test Keypad",
    )

    # Mock keypad data for migration
    keypad = mock_lutron.areas[0].keypads[0]
    keypad.id = 1
    keypad.uuid = ""  # No proper UUID yet
    keypad.legacy_uuid = "1-0"
    controller_guid = mock_lutron.guid

    await hass.config_entries.async_setup(mock_config_entry.entry_id)
    await hass.async_block_till_done()

    # Verify the device identifier has been updated
    device = device_registry.async_get_device(identifiers={(DOMAIN, cast(Any, 1))})
    assert device is None

    new_unique_id = f"{controller_guid}_{keypad.legacy_uuid}"
    device = device_registry.async_get_device(identifiers={(DOMAIN, new_unique_id)})
    assert device is not None
    assert device.name == "Test Keypad"


async def test_keypad_uuid_migration(
    hass: HomeAssistant,
    device_registry: dr.DeviceRegistry,
    mock_lutron: MagicMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test migration from legacy UUID to proper UUID."""
    mock_config_entry.add_to_hass(hass)

    controller_guid = mock_lutron.guid
    legacy_uuid = "1-0"
    proper_uuid = "proper-keypad-uuid"

    # Create a device with the legacy UUID-based identifier
    device_registry.async_get_or_create(
        config_entry_id=mock_config_entry.entry_id,
        identifiers={(DOMAIN, f"{controller_guid}_{legacy_uuid}")},
        manufacturer="Lutron",
        name="Test Keypad",
    )

    # Mock keypad data with a proper UUID (e.g. after a firmware update)
    keypad = mock_lutron.areas[0].keypads[0]
    keypad.id = 1
    keypad.uuid = proper_uuid
    keypad.legacy_uuid = legacy_uuid

    await hass.config_entries.async_setup(mock_config_entry.entry_id)
    await hass.async_block_till_done()

    # Verify the device identifier has been updated to use the proper UUID
    device = device_registry.async_get_device(
        identifiers={(DOMAIN, f"{controller_guid}_{legacy_uuid}")}
    )
    assert device is None

    device = device_registry.async_get_device(
        identifiers={(DOMAIN, f"{controller_guid}_{proper_uuid}")}
    )
    assert device is not None
    assert device.name == "Test Keypad"


async def test_keypad_integer_to_uuid_migration(
    hass: HomeAssistant,
    device_registry: dr.DeviceRegistry,
    mock_lutron: MagicMock,
    mock_config_entry: MockConfigEntry,
) -> None:
    """Test migration from integer keypad ID directly to proper UUID."""
    mock_config_entry.add_to_hass(hass)

    # Create a device with the old integer-based identifier
    device_registry.async_get_or_create(
        config_entry_id=mock_config_entry.entry_id,
        identifiers={(DOMAIN, cast(Any, 1))},
        manufacturer="Lutron",
        name="Test Keypad",
    )

    # Mock keypad data with a proper UUID
    keypad = mock_lutron.areas[0].keypads[0]
    keypad.id = 1
    keypad.uuid = "proper-keypad-uuid"
    keypad.legacy_uuid = "1-0"
    controller_guid = mock_lutron.guid

    await hass.config_entries.async_setup(mock_config_entry.entry_id)
    await hass.async_block_till_done()

    # Verify the device identifier has been updated to the proper UUID
    device = device_registry.async_get_device(identifiers={(DOMAIN, cast(Any, 1))})
    assert device is None

    new_unique_id = f"{controller_guid}_{keypad.uuid}"
    device = device_registry.async_get_device(identifiers={(DOMAIN, new_unique_id)})
    assert device is not None
    assert device.name == "Test Keypad"
