"""
🐧 Linux BLE Server using BlueZ D-Bus API.

Uses dbus-next for asynchronous D-Bus communication with BlueZ.
"""

import asyncio
import logging
from typing import Callable, Awaitable, Optional

logger = logging.getLogger(__name__)

try:
    from dbus_next.aio import MessageBus
    from dbus_next.service import ServiceInterface, method, dbus_property
    from dbus_next import Variant, BusType
    DBUS_AVAILABLE = True
except ImportError:
    DBUS_AVAILABLE = False
    logger.debug("dbus-next not available")


# BlueZ D-Bus paths and interfaces
BLUEZ_SERVICE = "org.bluez"
ADAPTER_PATH = "/org/bluez/hci0"
GATT_MANAGER_IFACE = "org.bluez.GattManager1"
LE_ADVERTISING_MANAGER_IFACE = "org.bluez.LEAdvertisingManager1"
GATT_SERVICE_IFACE = "org.bluez.GattService1"
GATT_CHARACTERISTIC_IFACE = "org.bluez.GattCharacteristic1"


class LinuxBLEServer:
    """
    BLE GATT Server implementation for Linux using BlueZ D-Bus API.
    """
    
    def __init__(
        self,
        config,  # BLEConfig
        on_message: Optional[Callable[[str], Awaitable[None]]] = None,
    ):
        self.config = config
        self.on_message = on_message
        self._bus = None
        self._running = False
        self._app_path = "/org/bluelobster"
    
    async def start(self) -> bool:
        """Start the Linux BLE peripheral."""
        if not DBUS_AVAILABLE:
            logger.error("dbus-next not available")
            return False
        
        try:
            # Connect to system bus
            self._bus = await MessageBus(bus_type=BusType.SYSTEM).connect()
            logger.info("🐧 Connected to D-Bus system bus")
            
            # Get BlueZ adapter
            introspection = await self._bus.introspect(BLUEZ_SERVICE, ADAPTER_PATH)
            adapter = self._bus.get_proxy_object(BLUEZ_SERVICE, ADAPTER_PATH, introspection)
            
            # Register GATT application
            await self._register_gatt_application(adapter)
            
            # Register advertisement
            await self._register_advertisement(adapter)
            
            self._running = True
            logger.info("🦞 BlueLobster is now discoverable!")
            return True
            
        except Exception as e:
            logger.error(f"Failed to start BLE server: {e}")
            logger.info("💡 Make sure BlueZ is running: sudo systemctl start bluetooth")
            logger.info("💡 Make sure adapter is powered: sudo hciconfig hci0 up")
            return False
    
    async def stop(self) -> None:
        """Stop the Linux BLE peripheral."""
        self._running = False
        if self._bus:
            self._bus.disconnect()
        logger.info("🐧 Linux BLE peripheral stopped")
    
    async def _register_gatt_application(self, adapter) -> None:
        """Register the GATT application with BlueZ."""
        # Create and export the GATT service
        app = BlueLobsterGattApplication(
            self.config,
            self._handle_write,
            self._app_path,
        )
        
        # Export the application to D-Bus
        self._bus.export(self._app_path, app)
        
        # Get GATT Manager interface
        gatt_manager = adapter.get_interface(GATT_MANAGER_IFACE)
        
        # Register application
        await gatt_manager.call_register_application(self._app_path, {})
        logger.info("✅ GATT application registered")
    
    async def _register_advertisement(self, adapter) -> None:
        """Register advertisement with BlueZ."""
        ad_path = f"{self._app_path}/advertisement"
        
        # Create advertisement
        ad = BlueLobsterAdvertisement(
            self.config.device_name,
            self.config.service_uuid,
            ad_path,
        )
        
        # Export to D-Bus
        self._bus.export(ad_path, ad)
        
        # Get LE Advertising Manager
        ad_manager = adapter.get_interface(LE_ADVERTISING_MANAGER_IFACE)
        
        # Register advertisement
        await ad_manager.call_register_advertisement(ad_path, {})
        logger.info("📡 Advertisement registered")
    
    async def _handle_write(self, data: bytes) -> None:
        """Handle incoming write from mobile device."""
        try:
            text = data.decode('utf-8')
            logger.info(f"📥 Received via BLE: {text}")
            
            if self.on_message:
                await self.on_message(text)
        except Exception as e:
            logger.error(f"Error handling write: {e}")


if DBUS_AVAILABLE:
    class BlueLobsterGattApplication(ServiceInterface):
        """GATT Application containing the BlueLobster service."""
        
        def __init__(self, config, on_write, path: str):
            super().__init__("org.freedesktop.DBus.ObjectManager")
            self.config = config
            self.on_write = on_write
            self.path = path
        
        @method()
        def GetManagedObjects(self) -> 'a{oa{sa{sv}}}':
            """Return all managed GATT objects."""
            service_path = f"{self.path}/service0"
            char_path = f"{service_path}/char0"
            
            return {
                service_path: {
                    GATT_SERVICE_IFACE: {
                        "UUID": Variant("s", self.config.service_uuid),
                        "Primary": Variant("b", True),
                    }
                },
                char_path: {
                    GATT_CHARACTERISTIC_IFACE: {
                        "UUID": Variant("s", self.config.write_char_uuid),
                        "Service": Variant("o", service_path),
                        "Flags": Variant("as", ["write", "write-without-response"]),
                    }
                },
            }
    
    
    class BlueLobsterAdvertisement(ServiceInterface):
        """LE Advertisement for BlueLobster."""
        
        def __init__(self, name: str, service_uuid: str, path: str):
            super().__init__("org.bluez.LEAdvertisement1")
            self._name = name
            self._service_uuid = service_uuid
            self.path = path
        
        @dbus_property()
        def Type(self) -> 's':
            return "peripheral"
        
        @dbus_property()
        def LocalName(self) -> 's':
            return self._name
        
        @dbus_property()
        def ServiceUUIDs(self) -> 'as':
            return [self._service_uuid]
        
        @method()
        def Release(self):
            """Called when advertisement is released."""
            logger.info("Advertisement released")
