"""
UDP broadcast component for LAN discovery
Broadcasts MBetterClient server information every 30 seconds
"""

import socket
import json
import time
import logging
import threading
from typing import Optional, Dict, Any

from .thread_manager import ThreadedComponent
from .message_bus import MessageBus, Message, MessageType

logger = logging.getLogger(__name__)


class UDPBroadcastComponent(ThreadedComponent):
    """Component for broadcasting server information via UDP"""
    
    def __init__(self, message_bus: MessageBus, broadcast_port: int = 45123):
        super().__init__("udp_broadcast", message_bus)
        self.broadcast_port = broadcast_port
        self.broadcast_interval = 30.0  # 30 seconds
        self.socket: Optional[socket.socket] = None

        # Server information to broadcast
        self.server_info: Dict[str, Any] = {
            "service": "MBetterClient",
            "host": "127.0.0.1",
            "port": 5001,
            "ssl": False,
            "url": "http://127.0.0.1:5001",
            "timestamp": time.time()
        }

        # Shutdown event for responsive shutdown
        self.shutdown_event = threading.Event()

        # Register message queue
        self.message_queue = self.message_bus.register_component(self.name)

        logger.info(f"UDP Broadcast component initialized on port {broadcast_port}")
    
    def initialize(self) -> bool:
        """Initialize UDP socket for broadcasting"""
        try:
            # Create UDP socket
            self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # Set timeout to prevent blocking operations
            self.socket.settimeout(1.0)

            # Clear shutdown event
            self.shutdown_event.clear()

            # Subscribe to system status messages to get web server info
            self.message_bus.subscribe(self.name, MessageType.SYSTEM_STATUS, self._handle_system_status)

            logger.info("UDP broadcast socket initialized successfully")
            return True

        except Exception as e:
            logger.error(f"UDP broadcast initialization failed: {e}")
            return False
    
    def run(self):
        """Main broadcast loop"""
        try:
            logger.info("UDP broadcast thread started")
            
            # Request initial web server status
            self._request_web_server_status()
            
            last_broadcast_time = 0
            
            while self.running and not self.shutdown_event.is_set():
                try:
                    current_time = time.time()

                    # Process messages with shorter timeout for responsive shutdown
                    message = self.message_bus.get_message(self.name, timeout=0.5)
                    if message:
                        self._process_message(message)

                    # Broadcast every 30 seconds
                    if current_time - last_broadcast_time >= self.broadcast_interval:
                        self._broadcast_server_info()
                        last_broadcast_time = current_time

                    # Update heartbeat
                    self.heartbeat()

                    # Check shutdown event more frequently
                    if self.shutdown_event.wait(0.5):
                        break

                except Exception as e:
                    logger.error(f"UDP broadcast loop error: {e}")
                    # Shorter sleep on error to be more responsive to shutdown
                    if not self.shutdown_event.wait(1.0):
                        continue
                    break
                    
        except Exception as e:
            logger.error(f"UDP broadcast run failed: {e}")
        finally:
            logger.info("UDP broadcast thread ended")
    
    def shutdown(self):
        """Shutdown UDP broadcast component"""
        try:
            logger.info("Shutting down UDP broadcast...")

            # Signal shutdown event to wake up the main loop
            self.shutdown_event.set()

            # Close socket to prevent further operations
            if self.socket:
                try:
                    self.socket.close()
                except Exception as e:
                    logger.debug(f"Error closing UDP socket: {e}")
                finally:
                    self.socket = None

        except Exception as e:
            logger.error(f"UDP broadcast shutdown error: {e}")
    
    def _process_message(self, message: Message):
        """Process received message"""
        try:
            if message.type == MessageType.SYSTEM_STATUS:
                self._handle_system_status(message)
                
        except Exception as e:
            logger.error(f"Failed to process message: {e}")
    
    def _handle_system_status(self, message: Message):
        """Handle system status messages from web dashboard"""
        try:
            if message.sender == "web_dashboard":
                details = message.data.get("details", {})
                
                if "host" in details and "port" in details:
                    self.server_info.update({
                        "host": details.get("host", "127.0.0.1"),
                        "port": details.get("port", 5001),
                        "ssl": details.get("ssl_enabled", False),
                        "url": details.get("url", "http://127.0.0.1:5001"),
                        "timestamp": time.time()
                    })
                    
                    logger.debug(f"Updated server info from web dashboard: {self.server_info}")
                
        except Exception as e:
            logger.error(f"Failed to handle system status: {e}")
    
    def _request_web_server_status(self):
        """Request current web server status"""
        try:
            # Send a status request to get current web server info
            status_request = Message(
                type=MessageType.CONFIG_REQUEST,
                sender=self.name,
                recipient="web_dashboard",
                data={"section": "status"}
            )
            self.message_bus.publish(status_request)
            
        except Exception as e:
            logger.error(f"Failed to request web server status: {e}")
    
    def _broadcast_server_info(self):
        """Broadcast server information via UDP"""
        try:
            if not self.socket:
                logger.warning("UDP socket not available for broadcasting")
                return
            
            # Update timestamp
            self.server_info["timestamp"] = time.time()
            
            # Create broadcast message
            broadcast_data = json.dumps(self.server_info, separators=(',', ':')).encode('utf-8')
            
            # Broadcast to all interfaces
            broadcast_addresses = self._get_broadcast_addresses()
            
            for broadcast_addr in broadcast_addresses:
                try:
                    if self.socket and not self.shutdown_event.is_set():
                        self.socket.sendto(broadcast_data, (broadcast_addr, self.broadcast_port))
                        logger.debug(f"Broadcasted to {broadcast_addr}:{self.broadcast_port}")
                    else:
                        break  # Exit if shutting down or socket closed
                except socket.timeout:
                    logger.debug(f"Timeout broadcasting to {broadcast_addr}")
                except OSError as e:
                    if self.shutdown_event.is_set():
                        break  # Exit if shutting down
                    logger.debug(f"Failed to broadcast to {broadcast_addr}: {e}")
                except Exception as e:
                    logger.debug(f"Failed to broadcast to {broadcast_addr}: {e}")
            
            logger.info(f"UDP broadcast sent: {self.server_info['service']} at {self.server_info['url']}")
            
        except Exception as e:
            logger.error(f"UDP broadcast failed: {e}")
    
    def _get_broadcast_addresses(self) -> list:
        """Get list of broadcast addresses for all network interfaces"""
        broadcast_addresses = ['255.255.255.255']  # Global broadcast
        
        try:
            import netifaces
            
            # Get all network interfaces
            for interface in netifaces.interfaces():
                try:
                    # Get IPv4 addresses for this interface
                    addrs = netifaces.ifaddresses(interface)
                    if netifaces.AF_INET in addrs:
                        for addr_info in addrs[netifaces.AF_INET]:
                            if 'broadcast' in addr_info:
                                broadcast_addr = addr_info['broadcast']
                                if broadcast_addr not in broadcast_addresses:
                                    broadcast_addresses.append(broadcast_addr)
                except Exception:
                    continue
                    
        except ImportError:
            # netifaces not available, use basic approach
            logger.debug("netifaces not available, using global broadcast only")
        except Exception as e:
            logger.debug(f"Error getting network interfaces: {e}")
        
        return broadcast_addresses
    
    def get_current_server_info(self) -> Dict[str, Any]:
        """Get current server information"""
        return self.server_info.copy()