"""The dhcp integration websocket apis."""

from __future__ import annotations

from typing import Any, TypedDict

import voluptuous as vol

from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.json import json_bytes
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo as _DhcpServiceInfo

from .const import HOSTNAME, IP_ADDRESS
from .helpers import (
    async_get_address_data_internal,
    async_register_dhcp_callback_internal,
)


class DHCPDiscovery(TypedDict):
    """Typed dict for DHCP discovery."""

    mac_address: str
    hostname: str
    ip_address: str


@callback
def async_setup(hass: HomeAssistant) -> None:
    """Set up the DHCP websocket API."""
    websocket_api.async_register_command(hass, ws_subscribe_discovery)


def serialize_service_info(service_info: _DhcpServiceInfo) -> DHCPDiscovery:
    """Serialize a _DhcpServiceInfo object."""
    serialized: DHCPDiscovery = {
        "mac_address": service_info.macaddress,
        "hostname": service_info.hostname,
        "ip_address": service_info.ip,
    }
    return serialized


class _DiscoverySubscription:
    """Class to hold and manage the subscription data."""

    def __init__(
        self,
        hass: HomeAssistant,
        connection: websocket_api.ActiveConnection,
        ws_msg_id: int,
    ) -> None:
        """Initialize the subscription data."""
        self.hass = hass
        self.ws_msg_id = ws_msg_id
        self.connection = connection

    @callback
    def async_start(self) -> None:
        """Start the subscription."""
        connection = self.connection
        connection.subscriptions[self.ws_msg_id] = (
            async_register_dhcp_callback_internal(
                self.hass,
                self._async_on_discovery,
            )
        )
        connection.send_message(
            json_bytes(websocket_api.result_message(self.ws_msg_id))
        )
        self._async_send_current_address_data()

    def _async_event_message(self, message: dict[str, Any]) -> None:
        self.connection.send_message(
            json_bytes(websocket_api.event_message(self.ws_msg_id, message))
        )

    def _async_send_current_address_data(self) -> None:
        """Send the current address data."""
        address_data = async_get_address_data_internal(self.hass)
        self._async_event_message(
            {
                "add": [
                    {
                        "address": mac_address,
                        "hostname": data[HOSTNAME],
                        "ip_address": data[IP_ADDRESS],
                    }
                    for mac_address, data in address_data.items()
                ]
            }
        )

    def _async_added(self, service_infos: list[_DhcpServiceInfo]) -> None:
        self._async_event_message(
            {
                "add": [
                    serialize_service_info(service_info)
                    for service_info in service_infos
                ]
            }
        )

    @callback
    def _async_on_discovery(self, service_info: _DhcpServiceInfo) -> None:
        """Handle the callback."""
        self._async_event_message({"add": [serialize_service_info(service_info)]})


@websocket_api.require_admin
@websocket_api.websocket_command(
    {
        vol.Required("type"): "dhcp/subscribe_discovery",
    }
)
@websocket_api.async_response
async def ws_subscribe_discovery(
    hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
) -> None:
    """Handle subscribe discovery websocket command."""
    _DiscoverySubscription(hass, connection, msg["id"]).async_start()