"""Test configuration and mocks for LCN component."""

import json
from typing import Any
from unittest.mock import AsyncMock, Mock, patch

import pypck
from pypck import lcn_defs
from pypck.device import DeviceConnection, Serials
import pytest

from homeassistant.components.lcn import PchkConnectionManager
from homeassistant.components.lcn.config_flow import LcnFlowHandler
from homeassistant.components.lcn.const import DOMAIN
from homeassistant.components.lcn.helpers import AddressType, generate_unique_id
from homeassistant.const import CONF_ADDRESS, CONF_DEVICES, CONF_ENTITIES, CONF_HOST
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr

from tests.common import MockConfigEntry, load_fixture

LATEST_CONFIG_ENTRY_VERSION = (LcnFlowHandler.VERSION, LcnFlowHandler.MINOR_VERSION)


class MockDeviceConnection(DeviceConnection):
    """Fake a LCN module connection."""

    request_name = AsyncMock(return_value="TestModule")
    request_serials = AsyncMock(
        return_value=Serials(
            hardware_serial=0x1A20A1234,
            manu=0x01,
            software_serial=0x190B11,
            hardware_type=lcn_defs.HardwareType.UPP,
        )
    )
    send_command = AsyncMock(return_value=True)
    request_status_output = AsyncMock()
    request_status_relays = AsyncMock()
    request_status_motor_position = AsyncMock()
    request_status_binary_sensors = AsyncMock()
    request_status_variable = AsyncMock()
    request_status_led_and_logic_ops = AsyncMock()
    request_status_locked_keys = AsyncMock()

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        """Construct ModuleConnection instance."""
        super().__init__(*args, **kwargs)
        self._serials_known.set()


class MockPchkConnectionManager(PchkConnectionManager):
    """Fake connection handler."""

    async def async_connect(self, timeout: int = 30) -> None:
        """Mock establishing a connection to PCHK."""
        self.authentication_completed_future.set_result(True)
        self.license_error_future.set_result(True)
        self.segment_scan_completed_event.set()

    async def async_close(self) -> None:
        """Mock closing a connection to PCHK."""

    @patch.object(pypck.connection, "DeviceConnection", MockDeviceConnection)
    def get_device_connection(self, addr):
        """Get LCN device connection."""
        return super().get_device_connection(addr)

    scan_modules = AsyncMock()
    send_command = AsyncMock()


def create_config_entry(
    name: str, version: tuple[int, int] = LATEST_CONFIG_ENTRY_VERSION
) -> MockConfigEntry:
    """Set up config entries with configuration data."""
    fixture_filename = f"lcn/config_entry_{name}.json"
    entry_data = json.loads(load_fixture(fixture_filename))
    for device in entry_data[CONF_DEVICES]:
        device[CONF_ADDRESS] = tuple(device[CONF_ADDRESS])
    for entity in entry_data[CONF_ENTITIES]:
        entity[CONF_ADDRESS] = tuple(entity[CONF_ADDRESS])

    options = {}

    title = entry_data[CONF_HOST]
    return MockConfigEntry(
        entry_id=fixture_filename.replace(".", "_"),
        domain=DOMAIN,
        title=title,
        data=entry_data,
        options=options,
        version=version[0],
        minor_version=version[1],
    )


@pytest.fixture(name="entry")
def create_config_entry_pchk() -> MockConfigEntry:
    """Return one specific config entry."""
    return create_config_entry("pchk")


@pytest.fixture(name="entry2")
def create_config_entry_myhome() -> MockConfigEntry:
    """Return one specific config entry."""
    return create_config_entry("myhome")


async def init_integration(
    hass: HomeAssistant, entry: MockConfigEntry
) -> MockPchkConnectionManager:
    """Set up the LCN integration in Home Assistant."""
    hass.http = Mock()  # needs to be mocked as hass.http.register_static_path is called when registering the frontend
    lcn_connection = None

    def lcn_connection_factory(*args, **kwargs):
        nonlocal lcn_connection
        lcn_connection = MockPchkConnectionManager(*args, **kwargs)
        return lcn_connection

    entry.add_to_hass(hass)
    with patch(
        "homeassistant.components.lcn.PchkConnectionManager",
        side_effect=lcn_connection_factory,
    ):
        await hass.config_entries.async_setup(entry.entry_id)
        await hass.async_block_till_done()

    return lcn_connection


def get_device(
    hass: HomeAssistant, entry: MockConfigEntry, address: AddressType
) -> dr.DeviceEntry:
    """Get LCN device for specified address."""
    device_registry = dr.async_get(hass)
    identifiers = {(DOMAIN, generate_unique_id(entry.entry_id, address))}
    device = device_registry.async_get_device(identifiers=identifiers)
    assert device
    return device
