"""Init tests for the Telegram Bot integration."""

import pytest

from homeassistant.components.telegram_bot.const import (
    ATTR_PARSER,
    CONF_ALLOWED_CHAT_IDS,
    CONF_API_ENDPOINT,
    CONF_CHAT_ID,
    DEFAULT_API_ENDPOINT,
    DOMAIN,
    PARSER_MD,
    PLATFORM_BROADCAST,
)
from homeassistant.config_entries import ConfigEntryState, ConfigSubentryData
from homeassistant.const import CONF_API_KEY, CONF_PLATFORM
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er

from tests.common import MockConfigEntry


async def test_migration_error(
    hass: HomeAssistant,
    mock_external_calls: None,
) -> None:
    """Test migrate config entry from unsupported version."""

    mock_config_entry = MockConfigEntry(
        unique_id="mock api key",
        domain=DOMAIN,
        data={
            CONF_PLATFORM: PLATFORM_BROADCAST,
            CONF_API_KEY: "mock api key",
        },
        options={ATTR_PARSER: PARSER_MD},
        version=99,
    )

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

    assert mock_config_entry.state is ConfigEntryState.MIGRATION_ERROR


async def test_migrate_entry_from_1_1(
    hass: HomeAssistant,
    mock_external_calls: None,
) -> None:
    """Test migrate config entry from 1.1, chaining through to the latest version."""

    mock_config_entry = MockConfigEntry(
        unique_id="mock api key",
        domain=DOMAIN,
        data={
            CONF_PLATFORM: PLATFORM_BROADCAST,
            CONF_API_KEY: "mock api key",
        },
        options={ATTR_PARSER: PARSER_MD},
    )

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

    assert mock_config_entry.state is ConfigEntryState.LOADED
    assert mock_config_entry.version == 1
    assert mock_config_entry.minor_version == 3
    assert mock_config_entry.data == {
        CONF_PLATFORM: PLATFORM_BROADCAST,
        CONF_API_KEY: "mock api key",
        CONF_API_ENDPOINT: DEFAULT_API_ENDPOINT,
    }


@pytest.mark.parametrize("collapsed_chat_index", [0, 1])
@pytest.mark.parametrize(
    "chats_without_notify_entity",
    [
        pytest.param((), id="notify entities intact"),
        pytest.param((654321,), id="notify entity deleted"),
    ],
)
async def test_migrate_entry_to_per_chat_devices(
    hass: HomeAssistant,
    mock_external_calls: None,
    device_registry: dr.DeviceRegistry,
    entity_registry: er.EntityRegistry,
    collapsed_chat_index: int,
    chats_without_notify_entity: tuple[int, ...],
) -> None:
    """Test migrating chats sharing one bot device to per-chat devices."""
    bot_id = 123456  # test_user id from mock_external_calls
    chat_ids = (123456, 654321)
    config_entry = MockConfigEntry(
        unique_id="mock api key",
        domain=DOMAIN,
        minor_version=2,
        data={
            CONF_PLATFORM: PLATFORM_BROADCAST,
            CONF_API_KEY: "mock api key",
            CONF_API_ENDPOINT: DEFAULT_API_ENDPOINT,
        },
        options={ATTR_PARSER: PARSER_MD},
        subentries_data=[
            ConfigSubentryData(
                unique_id="123456",
                data={CONF_CHAT_ID: 123456},
                subentry_type=CONF_ALLOWED_CHAT_IDS,
                title="chat 1",
            ),
            ConfigSubentryData(
                unique_id="654321",
                data={CONF_CHAT_ID: 654321},
                subentry_type=CONF_ALLOWED_CHAT_IDS,
                title="chat 2",
            ),
        ],
    )
    config_entry.add_to_hass(hass)
    subentry_ids = list(config_entry.subentries)

    # Post-store-migration state: one shared bot device collapsed onto an arbitrary chat
    # subentry, holding the event entity and every surviving chat's notify entity.
    bot_device = device_registry.async_get_or_create(
        config_entry_id=config_entry.entry_id,
        config_subentry_id=subentry_ids[collapsed_chat_index],
        identifiers={(DOMAIN, str(bot_id))},
    )
    event_entity = entity_registry.async_get_or_create(
        "event",
        DOMAIN,
        f"{bot_id}_update_event",
        config_entry=config_entry,
        device_id=bot_device.id,
    )
    notify_entities = {
        chat_id: entity_registry.async_get_or_create(
            "notify",
            DOMAIN,
            f"{bot_id}_{chat_id}",
            config_entry=config_entry,
            config_subentry_id=subentry_id,
            device_id=bot_device.id,
        )
        for subentry_id, chat_id in zip(subentry_ids, chat_ids, strict=True)
        if chat_id not in chats_without_notify_entity
    }

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

    assert config_entry.state is ConfigEntryState.LOADED
    assert config_entry.minor_version == 3

    # Every chat has its own device - owned by that subentry and linked to the bot device -
    # even a chat whose notify entity was deleted before the migration ran. A surviving
    # notify entity is moved onto its chat's device.
    for subentry_id, chat_id in zip(subentry_ids, chat_ids, strict=True):
        chat_device = device_registry.async_get_device_by_identifier(
            (DOMAIN, f"{bot_id}_{chat_id}"), config_entry.entry_id
        )
        assert chat_device is not None
        assert chat_device.config_subentry_id == subentry_id
        assert chat_device.via_device_id == bot_device.id
        if chat_id in notify_entities:
            assert (
                entity_registry.async_get(notify_entities[chat_id].entity_id).device_id
                == chat_device.id
            )

    # The bot device was handed back to the config entry, keeping the event entity
    bot_device = device_registry.async_get(bot_device.id)
    assert bot_device is not None
    assert bot_device.config_subentry_id is None
    assert entity_registry.async_get(event_entity.entity_id).device_id == bot_device.id


async def test_per_chat_devices(
    hass: HomeAssistant,
    mock_broadcast_config_entry: MockConfigEntry,
    mock_external_calls: None,
    device_registry: dr.DeviceRegistry,
    entity_registry: er.EntityRegistry,
) -> None:
    """Each chat gets its own device linked to the config-entry-level bot device."""
    mock_broadcast_config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(mock_broadcast_config_entry.entry_id)
    await hass.async_block_till_done()

    # The bot device belongs to the config entry (no subentry) and holds the event entity
    bot_device = device_registry.async_get_device_by_identifier(
        (DOMAIN, "123456"), mock_broadcast_config_entry.entry_id
    )
    assert bot_device is not None
    assert bot_device.config_subentry_id is None

    for chat_id in (123456, 654321):
        chat_device = device_registry.async_get_device_by_identifier(
            (DOMAIN, f"123456_{chat_id}"), mock_broadcast_config_entry.entry_id
        )
        assert chat_device is not None
        assert chat_device.config_subentry_id is not None
        assert chat_device.via_device_id == bot_device.id
        notify_entity_id = entity_registry.async_get_entity_id(
            "notify", DOMAIN, f"123456_{chat_id}"
        )
        assert notify_entity_id is not None
        assert entity_registry.async_get(notify_entity_id).device_id == chat_device.id


async def test_remove_chat_subentry_removes_per_chat_device(
    hass: HomeAssistant,
    mock_broadcast_config_entry: MockConfigEntry,
    mock_external_calls: None,
    device_registry: dr.DeviceRegistry,
    entity_registry: er.EntityRegistry,
) -> None:
    """Removing a chat subentry removes just its per-chat device and notify entity."""
    mock_broadcast_config_entry.add_to_hass(hass)
    await hass.config_entries.async_setup(mock_broadcast_config_entry.entry_id)
    await hass.async_block_till_done()

    subentry_id = next(
        sid
        for sid, subentry in mock_broadcast_config_entry.subentries.items()
        if subentry.data[CONF_CHAT_ID] == 123456
    )
    assert device_registry.async_get_device_by_identifier(
        (DOMAIN, "123456_123456"), mock_broadcast_config_entry.entry_id
    )
    assert entity_registry.async_get_entity_id("notify", DOMAIN, "123456_123456")

    hass.config_entries.async_remove_subentry(mock_broadcast_config_entry, subentry_id)
    await hass.async_block_till_done()

    # The removed chat's device and notify entity are gone; the other chat and the bot
    # device remain
    assert not device_registry.async_get_device_by_identifier(
        (DOMAIN, "123456_123456"), mock_broadcast_config_entry.entry_id
    )
    assert not entity_registry.async_get_entity_id("notify", DOMAIN, "123456_123456")
    assert device_registry.async_get_device_by_identifier(
        (DOMAIN, "123456_654321"), mock_broadcast_config_entry.entry_id
    )
    assert device_registry.async_get_device_by_identifier(
        (DOMAIN, "123456"), mock_broadcast_config_entry.entry_id
    )
