# Copyright 2026, Jiri Eischmann <jiri@eischmann.cz> and the meshy contributors
# SPDX-License-Identifier: GPL-3.0-or-later

"""Platform-agnostic BLE transport base class."""

from abc import ABC, abstractmethod
from collections.abc import Callable

from meshy.transport_base import ConnectionState

# Nordic UART Service UUIDs (same across all platforms)
NUS_SERVICE_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
NUS_TX_CHAR_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"  # Notify (device -> app)
NUS_RX_CHAR_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"  # Write (app -> device)


class BleDevice:
    """Platform-agnostic BLE device representation."""

    def __init__(self, path: str, address: str, name: str, rssi: int = -100):
        self.path = path
        self.address = address
        self.name = name
        self.rssi = rssi

    def __repr__(self):
        return f"BleDevice({self.name}, {self.address})"


class BleTransportBase(ABC):
    """Abstract base class for platform-specific BLE implementations.

    Implementations must support the Nordic UART Service (NUS) for
    communication with MeshCore devices.
    """

    # Callbacks that consumers can set
    on_state_changed: Callable[[ConnectionState], None] | None = None
    on_data_received: Callable[[bytes], None] | None = None
    on_device_discovered: Callable[[BleDevice], None] | None = None
    on_passkey_requested: Callable[[str, Callable], None] | None = None

    @property
    @abstractmethod
    def state(self) -> ConnectionState:
        """Current connection state."""
        ...

    @property
    @abstractmethod
    def is_connected(self) -> bool:
        """True if connected to a device."""
        ...

    @property
    @abstractmethod
    def connected_address(self) -> str | None:
        """Address of the connected device, or None."""
        ...

    @property
    @abstractmethod
    def discovered_devices(self) -> list[BleDevice]:
        """List of discovered BLE devices."""
        ...

    @abstractmethod
    def start_scan(self):
        """Start scanning for BLE devices."""
        ...

    @abstractmethod
    def stop_scan(self):
        """Stop scanning for BLE devices."""
        ...

    @abstractmethod
    def connect_device(self, device_path: str):
        """Connect to a device by its path/identifier."""
        ...

    @abstractmethod
    def connect_by_address(self, address: str):
        """Connect to a device by its MAC address."""
        ...

    @abstractmethod
    def pair_device(self, device_path: str):
        """Pair with a device."""
        ...

    @abstractmethod
    def disconnect(self):
        """Disconnect from the current device."""
        ...

    @abstractmethod
    def send_data(self, data: bytes):
        """Send data to the connected device."""
        ...

    @abstractmethod
    def is_bluetooth_enabled(self) -> bool:
        """Check if Bluetooth is enabled."""
        ...

    @abstractmethod
    def has_bluetooth_adapter(self) -> bool:
        """Check if a Bluetooth adapter is available."""
        ...

    @abstractmethod
    def get_paired_devices(self) -> list[BleDevice]:
        """Get list of paired devices that support NUS."""
        ...

    @abstractmethod
    def remove_device(self, device_path: str) -> bool:
        """Remove/unpair a device."""
        ...
