"""
Scanner manager for BLE device discovery.
"""

import asyncio
from typing import Optional

from bleak import BleakScanner
from bleak.backends.device import BLEDevice

from airctl.common.logging import get_logger
from airctl.common.utils import get_timestamp
from airctl.models.device import BLEDeviceInfo
from airctl.models.events import EventType

logger = get_logger(__name__)


class ScannerManager:
    """
    Manages BLE device scanning.
    """

    def __init__(self) -> None:
        self._scanner: Optional[BleakScanner] = None
        self._scanning = False

    async def scan(
        self,
        timeout: float = 10.0,
        service_uuids: Optional[list[str]] = None,
    ) -> list[BLEDeviceInfo]:
        """
        Scan for BLE devices.

        Args:
            timeout: Scan duration in seconds
            service_uuids: Optional list of service UUIDs to filter

        Returns:
            List of discovered device info
        """
        logger.info(f"Starting BLE scan (timeout: {timeout}s)")

        devices: list[BLEDeviceInfo] = []

        def detection_callback(device: BLEDevice, advertisement_data) -> None:
            device_info = BLEDeviceInfo(
                address=device.address,
                name=device.name,
                rssi=advertisement_data.rssi,
                service_uuids=[str(u) for u in advertisement_data.service_uuids],
                manufacturer_data={
                    k: v.hex() for k, v in advertisement_data.manufacturer_data.items()
                },
                service_data={
                    k: v.hex() for k, v in advertisement_data.service_data.items()
                },
                local_name=advertisement_data.local_name,
                tx_power=advertisement_data.tx_power,
            )
            devices.append(device_info)

        scanner = BleakScanner(
            detection_callback=detection_callback,
            service_uuids=service_uuids,
        )

        try:
            await scanner.start()
            await asyncio.sleep(timeout)
            await scanner.stop()
        except Exception as e:
            logger.error(f"Scan error: {e}")
            try:
                await scanner.stop()
            except Exception:
                pass
            raise

        unique_devices: dict[str, BLEDeviceInfo] = {}
        for d in devices:
            if d.address not in unique_devices or d.rssi > unique_devices[d.address].rssi:
                unique_devices[d.address] = d

        logger.info(f"Scan complete, found {len(unique_devices)} devices")
        return list(unique_devices.values())

    async def find_device_by_address(
        self,
        address: str,
        timeout: float = 10.0,
    ) -> Optional[BLEDeviceInfo]:
        """
        Find a device by address.

        Args:
            address: Device address
            timeout: Scan timeout

        Returns:
            Device info if found, None otherwise
        """
        device = await BleakScanner.find_device_by_address(address, timeout=timeout)
        if device:
            return BLEDeviceInfo(
                address=device.address,
                name=device.name,
                rssi=0,
            )
        return None

    async def find_device_by_name(
        self,
        name: str,
        timeout: float = 10.0,
    ) -> Optional[BLEDeviceInfo]:
        """
        Find a device by name.

        Args:
            name: Device name
            timeout: Scan timeout

        Returns:
            Device info if found, None otherwise
        """
        device = await BleakScanner.find_device_by_name(name, timeout=timeout)
        if device:
            return BLEDeviceInfo(
                address=device.address,
                name=device.name,
                rssi=0,
            )
        return None
