"""The Fjäråskupan integration."""
from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass
from datetime import timedelta
import logging

from bleak import BleakScanner
from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from fjaraskupan import UUID_SERVICE, Device, State, device_filter

from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import (
    async_dispatcher_connect,
    async_dispatcher_send,
)
from homeassistant.helpers.entity import DeviceInfo, Entity
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

from .const import DISPATCH_DETECTION, DOMAIN

PLATFORMS = [
    Platform.BINARY_SENSOR,
    Platform.FAN,
    Platform.LIGHT,
    Platform.NUMBER,
    Platform.SENSOR,
]

_LOGGER = logging.getLogger(__name__)


@dataclass
class DeviceState:
    """Store state of a device."""

    device: Device
    coordinator: DataUpdateCoordinator[State]
    device_info: DeviceInfo


@dataclass
class EntryState:
    """Store state of config entry."""

    scanner: BleakScanner
    devices: dict[str, DeviceState]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Fjäråskupan from a config entry."""

    scanner = BleakScanner(filters={"UUIDs": [str(UUID_SERVICE)]})

    state = EntryState(scanner, {})
    hass.data.setdefault(DOMAIN, {})
    hass.data[DOMAIN][entry.entry_id] = state

    async def detection_callback(
        ble_device: BLEDevice, advertisement_data: AdvertisementData
    ) -> None:
        if data := state.devices.get(ble_device.address):
            _LOGGER.debug(
                "Update: %s %s - %s", ble_device.name, ble_device, advertisement_data
            )

            data.device.detection_callback(ble_device, advertisement_data)
            data.coordinator.async_set_updated_data(data.device.state)
        else:
            if not device_filter(ble_device, advertisement_data):
                return

            _LOGGER.debug(
                "Detected: %s %s - %s", ble_device.name, ble_device, advertisement_data
            )

            device = Device(ble_device)
            device.detection_callback(ble_device, advertisement_data)

            async def async_update_data():
                """Handle an explicit update request."""
                await device.update()
                return device.state

            coordinator: DataUpdateCoordinator[State] = DataUpdateCoordinator(
                hass,
                logger=_LOGGER,
                name="Fjaraskupan Updater",
                update_interval=timedelta(seconds=120),
                update_method=async_update_data,
            )
            coordinator.async_set_updated_data(device.state)

            device_info = DeviceInfo(
                identifiers={(DOMAIN, ble_device.address)},
                manufacturer="Fjäråskupan",
                name="Fjäråskupan",
            )
            device_state = DeviceState(device, coordinator, device_info)
            state.devices[ble_device.address] = device_state
            async_dispatcher_send(
                hass, f"{DISPATCH_DETECTION}.{entry.entry_id}", device_state
            )

    scanner.register_detection_callback(detection_callback)
    await scanner.start()

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)
    return True


@callback
def async_setup_entry_platform(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
    constructor: Callable[[DeviceState], list[Entity]],
) -> None:
    """Set up a platform with added entities."""

    entry_state: EntryState = hass.data[DOMAIN][entry.entry_id]
    async_add_entities(
        entity
        for device_state in entry_state.devices.values()
        for entity in constructor(device_state)
    )

    @callback
    def _detection(device_state: DeviceState) -> None:
        async_add_entities(constructor(device_state))

    entry.async_on_unload(
        async_dispatcher_connect(
            hass, f"{DISPATCH_DETECTION}.{entry.entry_id}", _detection
        )
    )


async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Unload a config entry."""
    unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
    if unload_ok:
        entry_state: EntryState = hass.data[DOMAIN].pop(entry.entry_id)
        await entry_state.scanner.stop()

    return unload_ok
