"""Test KNX interface device."""

from unittest.mock import patch

from freezegun.api import FrozenDateTimeFactory
from xknx.core import XknxConnectionState, XknxConnectionType
from xknx.telegram import IndividualAddress

from homeassistant.components.knx.sensor import SCAN_INTERVAL
from homeassistant.const import EntityCategory
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 .conftest import KNXTestKit

from tests.common import async_capture_events, async_fire_time_changed
from tests.typing import WebSocketGenerator


async def test_diagnostic_entities(
    hass: HomeAssistant,
    knx: KNXTestKit,
    entity_registry: er.EntityRegistry,
    freezer: FrozenDateTimeFactory,
) -> None:
    """Test diagnostic entities."""
    await knx.setup_integration()

    for entity_id in (
        "sensor.knx_interface_individual_address",
        "sensor.knx_interface_connection_established",
        "sensor.knx_interface_connection_type",
        "sensor.knx_interface_incoming_telegrams",
        "sensor.knx_interface_incoming_telegram_errors",
        "sensor.knx_interface_outgoing_telegrams",
        "sensor.knx_interface_outgoing_telegram_errors",
        "sensor.knx_interface_telegrams",
        "sensor.knx_interface_undecodable_data_secure_telegrams",
    ):
        entity = entity_registry.async_get(entity_id)
        assert entity.entity_category is EntityCategory.DIAGNOSTIC

    for entity_id in (
        "sensor.knx_interface_incoming_telegrams",
        "sensor.knx_interface_outgoing_telegrams",
        "sensor.knx_interface_undecodable_data_secure_telegrams",
    ):
        entity = entity_registry.async_get(entity_id)
        assert entity.disabled is True

    knx.xknx.connection_manager.cemi_count_incoming = 20
    knx.xknx.connection_manager.cemi_count_incoming_error = 1
    knx.xknx.connection_manager.cemi_count_outgoing = 10
    knx.xknx.connection_manager.cemi_count_outgoing_error = 2

    events = async_capture_events(hass, "state_changed")
    freezer.tick(SCAN_INTERVAL)
    async_fire_time_changed(hass)
    await hass.async_block_till_done()

    assert len(events) == 3  # 6 polled sensors - 3 disabled
    events.clear()

    for entity_id, test_state in (
        ("sensor.knx_interface_individual_address", "0.0.0"),
        ("sensor.knx_interface_connection_type", "Tunnel TCP"),
        # skipping connected_since timestamp
        ("sensor.knx_interface_incoming_telegram_errors", "1"),
        ("sensor.knx_interface_outgoing_telegram_errors", "2"),
        ("sensor.knx_interface_telegrams", "31"),
    ):
        assert hass.states.get(entity_id).state == test_state

    knx.xknx.connection_manager.connection_state_changed(
        state=XknxConnectionState.DISCONNECTED
    )
    await hass.async_block_till_done()
    assert len(events) == 4
    events.clear()

    knx.xknx.current_address = IndividualAddress("1.1.1")
    knx.xknx.connection_manager.connection_state_changed(
        state=XknxConnectionState.CONNECTED,
        connection_type=XknxConnectionType.TUNNEL_UDP,
    )
    await hass.async_block_till_done()
    assert len(events) == 6  # all diagnostic sensors - counters are reset on connect

    for entity_id, test_state in (
        ("sensor.knx_interface_individual_address", "1.1.1"),
        ("sensor.knx_interface_connection_type", "Tunnel UDP"),
        # skipping connected_since timestamp
        ("sensor.knx_interface_incoming_telegram_errors", "0"),
        ("sensor.knx_interface_outgoing_telegram_errors", "0"),
        ("sensor.knx_interface_telegrams", "0"),
    ):
        assert hass.states.get(entity_id).state == test_state


async def test_removed_entity(
    hass: HomeAssistant, knx: KNXTestKit, entity_registry: er.EntityRegistry
) -> None:
    """Test unregister callback when entity is removed."""
    with patch(
        "xknx.core.connection_manager.ConnectionManager.unregister_connection_state_changed_cb"
    ) as unregister_mock:
        await knx.setup_integration()

        entity_registry.async_update_entity(
            "sensor.knx_interface_connection_established",
            disabled_by=er.RegistryEntryDisabler.USER,
        )
        unregister_mock.assert_called_once()


async def test_remove_interface_device(
    hass: HomeAssistant,
    knx: KNXTestKit,
    device_registry: dr.DeviceRegistry,
    hass_ws_client: WebSocketGenerator,
) -> None:
    """Test device removal."""
    assert await async_setup_component(hass, "config", {})
    await knx.setup_integration()
    client = await hass_ws_client(hass)
    knx_devices = device_registry.devices.get_devices_for_config_entry_id(
        knx.mock_config_entry.entry_id
    )
    assert len(knx_devices) == 1
    assert knx_devices[0].name == "KNX Interface"
    device_id = knx_devices[0].id
    # interface device can't be removed
    res = await client.remove_device(device_id, knx.mock_config_entry.entry_id)
    assert not res["success"]
    assert (
        res["error"]["message"]
        == "Failed to remove device entry, rejected by integration"
    )
