"""GATT Server implementation for Dangerous Pi.

This GATT server provides BLE access to all Dangerous Pi functionality by
reusing the service layer. This ensures identical behavior between REST API
and BLE interface - NO CODE DUPLICATION!

Architecture:
    BLE GATT Characteristic Handler
           ↓
    Service Layer (PM3Service, WiFiService, etc.)
           ↓
    Managers/Workers (PM3Worker, WiFiManager, etc.)

The handlers are thin adapters, just like REST endpoints.
"""
import asyncio
import json
import logging
from typing import Dict, Any, Optional, Callable
from dataclasses import dataclass

from ..services.container import container
from .characteristics import (
    PM3CharacteristicUUIDs,
    WiFiCharacteristicUUIDs,
    SystemCharacteristicUUIDs,
    UpdateCharacteristicUUIDs,
)

logger = logging.getLogger(__name__)


@dataclass
class CharacteristicHandler:
    """Configuration for a GATT characteristic handler."""
    uuid: str
    properties: list  # ["read", "write", "notify"]
    read_handler: Optional[Callable] = None
    write_handler: Optional[Callable] = None
    description: str = ""


class DangerousPiGATTServer:
    """GATT server for Dangerous Pi BLE interface.

    This server exposes Dangerous Pi functionality over BLE by reusing
    the service layer. All business logic comes from services, ensuring
    consistency with the REST API.

    Key Features:
    - Uses ServiceContainer for all operations (same as REST!)
    - Converts BLE data formats to/from service calls
    - Sends notifications for async operations
    - Zero business logic duplication
    """

    def __init__(self):
        """Initialize GATT server."""
        self.is_running = False
        self._notification_callbacks: Dict[str, Callable] = {}
        self._characteristic_handlers: Dict[str, CharacteristicHandler] = {}

        # Register all characteristic handlers
        self._register_pm3_characteristics()
        self._register_wifi_characteristics()
        self._register_system_characteristics()
        self._register_update_characteristics()

        logger.info("GATT server initialized with %d characteristics",
                   len(self._characteristic_handlers))

    # ========================================================================
    # PM3 Characteristic Handlers
    # ========================================================================

    def _register_pm3_characteristics(self):
        """Register PM3 GATT characteristics.

        All handlers delegate to PM3Service - NO business logic here!
        """
        self._characteristic_handlers.update({
            PM3CharacteristicUUIDs.COMMAND_WRITE: CharacteristicHandler(
                uuid=PM3CharacteristicUUIDs.COMMAND_WRITE,
                properties=["write"],
                write_handler=self._handle_pm3_command_write,
                description="Execute PM3 command"
            ),
            PM3CharacteristicUUIDs.STATUS: CharacteristicHandler(
                uuid=PM3CharacteristicUUIDs.STATUS,
                properties=["read"],
                read_handler=self._handle_pm3_status_read,
                description="Get PM3 status"
            ),
            PM3CharacteristicUUIDs.SESSION_CREATE: CharacteristicHandler(
                uuid=PM3CharacteristicUUIDs.SESSION_CREATE,
                properties=["write"],
                write_handler=self._handle_session_create_write,
                description="Create PM3 session"
            ),
            PM3CharacteristicUUIDs.SESSION_RELEASE: CharacteristicHandler(
                uuid=PM3CharacteristicUUIDs.SESSION_RELEASE,
                properties=["write"],
                write_handler=self._handle_session_release_write,
                description="Release PM3 session"
            ),
        })

    async def _handle_pm3_command_write(self, value: bytes) -> Dict[str, Any]:
        """Handle PM3 command execution via BLE.

        This is a THIN ADAPTER - delegates to PM3Service!

        Args:
            value: JSON bytes: {"command": "hw version", "session_id": "..."}

        Returns:
            Response dict to send via notification
        """
        try:
            # Parse BLE request
            data = json.loads(value.decode('utf-8'))
            command = data.get("command")
            session_id = data.get("session_id")

            if not command:
                return {
                    "success": False,
                    "error": {"code": "invalid_request", "message": "Command required"}
                }

            # ✅ REUSE PM3Service - same logic as REST API!
            result = await container.pm3_service.execute_command(
                command=command,
                session_id=session_id
            )

            # Convert service result to BLE response
            if result.success:
                response = {
                    "success": True,
                    "output": result.data["output"],
                    "command": result.data["command"]
                }
            else:
                response = {
                    "success": False,
                    "error": {
                        "code": result.error.code,
                        "message": result.error.message
                    }
                }

            # Send notification with result
            await self._notify_characteristic(
                PM3CharacteristicUUIDs.COMMAND_RESULT,
                json.dumps(response).encode('utf-8')
            )

            return response

        except json.JSONDecodeError:
            return {
                "success": False,
                "error": {"code": "invalid_json", "message": "Invalid JSON"}
            }
        except Exception as e:
            logger.error("Error handling PM3 command: %s", e)
            return {
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }

    async def _handle_pm3_status_read(self) -> bytes:
        """Handle PM3 status read via BLE.

        This is a THIN ADAPTER - delegates to PM3Service!

        Returns:
            JSON bytes with PM3 status
        """
        try:
            # ✅ REUSE PM3Service - same logic as REST API!
            result = await container.pm3_service.get_status()

            if result.success:
                response = {
                    "success": True,
                    "connected": result.data["connected"],
                    "device_path": result.data["device_path"],
                    "version": result.data.get("version"),
                    "session_active": result.data["session_active"]
                }
            else:
                response = {
                    "success": False,
                    "error": {
                        "code": result.error.code,
                        "message": result.error.message
                    }
                }

            return json.dumps(response).encode('utf-8')

        except Exception as e:
            logger.error("Error getting PM3 status: %s", e)
            return json.dumps({
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }).encode('utf-8')

    async def _handle_session_create_write(self, value: bytes) -> Dict[str, Any]:
        """Handle session creation via BLE.

        Args:
            value: JSON bytes: {"force_takeover": false}
        """
        try:
            data = json.loads(value.decode('utf-8'))
            force_takeover = data.get("force_takeover", False)

            # ✅ REUSE PM3Service
            result = container.pm3_service.create_session(
                force_takeover=force_takeover
            )

            if result.success:
                return {
                    "success": True,
                    "session_id": result.data["session_id"]
                }
            else:
                return {
                    "success": False,
                    "error": {
                        "code": result.error.code,
                        "message": result.error.message
                    }
                }

        except Exception as e:
            logger.error("Error creating session: %s", e)
            return {
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }

    async def _handle_session_release_write(self, value: bytes) -> Dict[str, Any]:
        """Handle session release via BLE.

        Args:
            value: JSON bytes: {"session_id": "..."}
        """
        try:
            data = json.loads(value.decode('utf-8'))
            session_id = data.get("session_id")

            if not session_id:
                return {
                    "success": False,
                    "error": {"code": "invalid_request", "message": "Session ID required"}
                }

            # ✅ REUSE PM3Service
            result = container.pm3_service.release_session(session_id)

            if result.success:
                return {"success": True, "message": result.data["message"]}
            else:
                return {
                    "success": False,
                    "error": {
                        "code": result.error.code,
                        "message": result.error.message
                    }
                }

        except Exception as e:
            logger.error("Error releasing session: %s", e)
            return {
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }

    # ========================================================================
    # WiFi Characteristic Handlers
    # ========================================================================

    def _register_wifi_characteristics(self):
        """Register WiFi GATT characteristics."""
        self._characteristic_handlers.update({
            WiFiCharacteristicUUIDs.STATUS: CharacteristicHandler(
                uuid=WiFiCharacteristicUUIDs.STATUS,
                properties=["read"],
                read_handler=self._handle_wifi_status_read,
                description="Get WiFi status"
            ),
            WiFiCharacteristicUUIDs.SCAN: CharacteristicHandler(
                uuid=WiFiCharacteristicUUIDs.SCAN,
                properties=["write", "notify"],
                write_handler=self._handle_wifi_scan_write,
                description="Scan for WiFi networks"
            ),
            WiFiCharacteristicUUIDs.CONNECT: CharacteristicHandler(
                uuid=WiFiCharacteristicUUIDs.CONNECT,
                properties=["write"],
                write_handler=self._handle_wifi_connect_write,
                description="Connect to WiFi network"
            ),
            WiFiCharacteristicUUIDs.MODE: CharacteristicHandler(
                uuid=WiFiCharacteristicUUIDs.MODE,
                properties=["read", "write"],
                read_handler=self._handle_wifi_mode_read,
                write_handler=self._handle_wifi_mode_write,
                description="WiFi mode"
            ),
        })

    async def _handle_wifi_status_read(self) -> bytes:
        """Handle WiFi status read via BLE."""
        try:
            # ✅ REUSE WiFiService
            result = await container.wifi_service.get_status()

            if result.success:
                return json.dumps(result.data).encode('utf-8')
            else:
                return json.dumps({
                    "success": False,
                    "error": {
                        "code": result.error.code,
                        "message": result.error.message
                    }
                }).encode('utf-8')

        except Exception as e:
            logger.error("Error getting WiFi status: %s", e)
            return json.dumps({
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }).encode('utf-8')

    async def _handle_wifi_scan_write(self, value: bytes) -> Dict[str, Any]:
        """Handle WiFi scan request via BLE."""
        try:
            data = json.loads(value.decode('utf-8')) if value else {}
            interface = data.get("interface")

            # ✅ REUSE WiFiService
            result = await container.wifi_service.scan_networks(interface)

            if result.success:
                # Send scan results via notification
                await self._notify_characteristic(
                    WiFiCharacteristicUUIDs.SCAN,
                    json.dumps(result.data).encode('utf-8')
                )
                return {"success": True, "count": result.data["count"]}
            else:
                return {
                    "success": False,
                    "error": {
                        "code": result.error.code,
                        "message": result.error.message
                    }
                }

        except Exception as e:
            logger.error("Error scanning WiFi: %s", e)
            return {
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }

    async def _handle_wifi_connect_write(self, value: bytes) -> Dict[str, Any]:
        """Handle WiFi connect request via BLE."""
        try:
            data = json.loads(value.decode('utf-8'))
            ssid = data.get("ssid")
            password = data.get("password")
            hidden = data.get("hidden", False)

            if not ssid:
                return {
                    "success": False,
                    "error": {"code": "invalid_request", "message": "SSID required"}
                }

            # ✅ REUSE WiFiService
            result = await container.wifi_service.connect(
                ssid=ssid,
                password=password,
                hidden=hidden
            )

            if result.success:
                return {
                    "success": True,
                    "ssid": result.data["ssid"],
                    "ip": result.data.get("ip")
                }
            else:
                return {
                    "success": False,
                    "error": {
                        "code": result.error.code,
                        "message": result.error.message
                    }
                }

        except Exception as e:
            logger.error("Error connecting to WiFi: %s", e)
            return {
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }

    async def _handle_wifi_mode_read(self) -> bytes:
        """Handle WiFi mode read via BLE."""
        try:
            result = await container.wifi_service.get_status()
            if result.success:
                return json.dumps({"mode": result.data["mode"]}).encode('utf-8')
            else:
                return json.dumps({
                    "success": False,
                    "error": {"code": result.error.code, "message": result.error.message}
                }).encode('utf-8')
        except Exception as e:
            return json.dumps({
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }).encode('utf-8')

    async def _handle_wifi_mode_write(self, value: bytes) -> Dict[str, Any]:
        """Handle WiFi mode change via BLE."""
        try:
            data = json.loads(value.decode('utf-8'))
            mode = data.get("mode")

            if not mode:
                return {
                    "success": False,
                    "error": {"code": "invalid_request", "message": "Mode required"}
                }

            # ✅ REUSE WiFiService
            result = await container.wifi_service.set_mode(mode)

            if result.success:
                return {"success": True, "mode": result.data["mode"]}
            else:
                return {
                    "success": False,
                    "error": {
                        "code": result.error.code,
                        "message": result.error.message
                    }
                }

        except Exception as e:
            logger.error("Error setting WiFi mode: %s", e)
            return {
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }

    # ========================================================================
    # System Characteristic Handlers
    # ========================================================================

    def _register_system_characteristics(self):
        """Register System GATT characteristics."""
        self._characteristic_handlers.update({
            SystemCharacteristicUUIDs.INFO: CharacteristicHandler(
                uuid=SystemCharacteristicUUIDs.INFO,
                properties=["read"],
                read_handler=self._handle_system_info_read,
                description="Get system information"
            ),
            SystemCharacteristicUUIDs.SHUTDOWN: CharacteristicHandler(
                uuid=SystemCharacteristicUUIDs.SHUTDOWN,
                properties=["write"],
                write_handler=self._handle_system_shutdown_write,
                description="Initiate system shutdown"
            ),
            SystemCharacteristicUUIDs.RESTART: CharacteristicHandler(
                uuid=SystemCharacteristicUUIDs.RESTART,
                properties=["write"],
                write_handler=self._handle_system_restart_write,
                description="Initiate system restart"
            ),
        })

    async def _handle_system_info_read(self) -> bytes:
        """Handle system info read via BLE."""
        try:
            # ✅ REUSE SystemService
            result = await container.system_service.get_info()

            if result.success:
                return json.dumps(result.data).encode('utf-8')
            else:
                return json.dumps({
                    "success": False,
                    "error": {
                        "code": result.error.code,
                        "message": result.error.message
                    }
                }).encode('utf-8')

        except Exception as e:
            logger.error("Error getting system info: %s", e)
            return json.dumps({
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }).encode('utf-8')

    async def _handle_system_shutdown_write(self, value: bytes) -> Dict[str, Any]:
        """Handle system shutdown request via BLE."""
        try:
            data = json.loads(value.decode('utf-8')) if value else {}
            delay = data.get("delay", 0)

            # ✅ REUSE SystemService
            result = await container.system_service.shutdown(delay=delay)

            if result.success:
                return {"success": True, "message": result.data["message"]}
            else:
                return {
                    "success": False,
                    "error": {
                        "code": result.error.code,
                        "message": result.error.message
                    }
                }

        except Exception as e:
            logger.error("Error initiating shutdown: %s", e)
            return {
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }

    async def _handle_system_restart_write(self, value: bytes) -> Dict[str, Any]:
        """Handle system restart request via BLE."""
        try:
            data = json.loads(value.decode('utf-8')) if value else {}
            delay = data.get("delay", 0)

            # ✅ REUSE SystemService
            result = await container.system_service.restart(delay=delay)

            if result.success:
                return {"success": True, "message": result.data["message"]}
            else:
                return {
                    "success": False,
                    "error": {
                        "code": result.error.code,
                        "message": result.error.message
                    }
                }

        except Exception as e:
            logger.error("Error initiating restart: %s", e)
            return {
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }

    # ========================================================================
    # Update Characteristic Handlers
    # ========================================================================

    def _register_update_characteristics(self):
        """Register Update GATT characteristics."""
        self._characteristic_handlers.update({
            UpdateCharacteristicUUIDs.CHECK: CharacteristicHandler(
                uuid=UpdateCharacteristicUUIDs.CHECK,
                properties=["write", "notify"],
                write_handler=self._handle_update_check_write,
                description="Check for updates"
            ),
            UpdateCharacteristicUUIDs.PROGRESS: CharacteristicHandler(
                uuid=UpdateCharacteristicUUIDs.PROGRESS,
                properties=["read", "notify"],
                read_handler=self._handle_update_progress_read,
                description="Get update progress"
            ),
        })

    async def _handle_update_check_write(self, value: bytes) -> Dict[str, Any]:
        """Handle update check request via BLE."""
        try:
            # ✅ REUSE UpdateService
            result = await container.update_service.check_for_updates()

            if result.success:
                # Send result via notification
                await self._notify_characteristic(
                    UpdateCharacteristicUUIDs.CHECK,
                    json.dumps(result.data).encode('utf-8')
                )
                return result.data
            else:
                return {
                    "success": False,
                    "error": {
                        "code": result.error.code,
                        "message": result.error.message
                    }
                }

        except Exception as e:
            logger.error("Error checking for updates: %s", e)
            return {
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }

    async def _handle_update_progress_read(self) -> bytes:
        """Handle update progress read via BLE."""
        try:
            # ✅ REUSE UpdateService
            result = await container.update_service.get_progress()

            if result.success:
                return json.dumps(result.data).encode('utf-8')
            else:
                return json.dumps({
                    "success": False,
                    "error": {
                        "code": result.error.code,
                        "message": result.error.message
                    }
                }).encode('utf-8')

        except Exception as e:
            logger.error("Error getting update progress: %s", e)
            return json.dumps({
                "success": False,
                "error": {"code": "internal_error", "message": str(e)}
            }).encode('utf-8')

    # ========================================================================
    # GATT Server Management
    # ========================================================================

    async def start(self):
        """Start the GATT server."""
        logger.info("Starting Dangerous Pi GATT server")
        self.is_running = True
        logger.info("GATT server started with %d characteristics",
                   len(self._characteristic_handlers))

    async def stop(self):
        """Stop the GATT server."""
        logger.info("Stopping Dangerous Pi GATT server")
        self.is_running = False
        logger.info("GATT server stopped")

    def get_characteristic_handler(self, uuid: str) -> Optional[CharacteristicHandler]:
        """Get handler for a characteristic UUID.

        Args:
            uuid: Characteristic UUID

        Returns:
            CharacteristicHandler or None if not found
        """
        return self._characteristic_handlers.get(uuid)

    async def _notify_characteristic(self, uuid: str, value: bytes):
        """Send notification for a characteristic.

        Args:
            uuid: Characteristic UUID
            value: Notification value (bytes)
        """
        if uuid in self._notification_callbacks:
            try:
                await self._notification_callbacks[uuid](value)
                logger.debug("Sent notification for %s", uuid)
            except Exception as e:
                logger.error("Error sending notification for %s: %s", uuid, e)
        else:
            logger.debug("No notification callback registered for %s", uuid)

    def register_notification_callback(self, uuid: str, callback: Callable):
        """Register callback for sending notifications.

        Args:
            uuid: Characteristic UUID
            callback: Async callable that sends the notification
        """
        self._notification_callbacks[uuid] = callback
        logger.debug("Registered notification callback for %s", uuid)

    def get_all_characteristics(self) -> Dict[str, CharacteristicHandler]:
        """Get all registered characteristic handlers.

        Returns:
            Dict mapping UUID to CharacteristicHandler
        """
        return self._characteristic_handlers.copy()
