#!/usr/bin/env python3
"""
WLED Module
Enthält NUR WLED-spezifische Geräte-Discovery Logik
KEINE Datenbank-Operationen!
"""

import requests
import socket
import concurrent.futures
import logging
from typing import List, Dict, Optional, Tuple
from modules.base_module import BaseModule

logger = logging.getLogger(__name__)


class WLEDDiscovery:
    """Original WLEDDiscovery Klasse - unverändert"""
    
    @staticmethod
    def discover_devices(timeout: int = 5) -> List[str]:
        """Sucht nach WLED-Geräten via mDNS"""
        try:
            from zeroconf import ServiceBrowser, ServiceListener, Zeroconf
            import time
            
            class WLEDListener(ServiceListener):
                def __init__(self):
                    self.devices = []
                
                def add_service(self, zc, type_, name):
                    info = zc.get_service_info(type_, name)
                    if info:
                        addresses = [socket.inet_ntoa(addr) for addr in info.addresses]
                        for addr in addresses:
                            if addr not in self.devices:
                                self.devices.append(addr)
                                logger.info(f"WLED-Gerät gefunden: {name} ({addr})")
                
                def remove_service(self, zc, type_, name):
                    pass
                
                def update_service(self, zc, type_, name):
                    pass
            
            zeroconf = Zeroconf()
            listener = WLEDListener()
            browser = ServiceBrowser(zeroconf, "_http._tcp.local.", listener)
            
            logger.info(f"Suche nach WLED-Geräten (Timeout: {timeout}s)...")
            time.sleep(timeout)
            
            zeroconf.close()
            
            # Nur WLED-Geräte filtern
            wled_devices = []
            for ip in listener.devices:
                if WLEDDiscovery.is_wled_device(ip):
                    wled_devices.append(ip)
            
            logger.info(f"{len(wled_devices)} WLED-Geräte gefunden")
            return wled_devices
            
        except ImportError:
            logger.warning("zeroconf nicht installiert. Verwende Netzwerk-Scan...")
            return WLEDDiscovery.scan_network()
        except Exception as e:
            logger.error(f"Fehler bei WLED-Discovery: {e}")
            return []
    
    @staticmethod
    def scan_network(network: str = None, max_threads: int = 50) -> List[str]:
        """Scannt das Netzwerk nach WLED-Geräten"""
        if network is None:
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                s.connect(("8.8.8.8", 80))
                local_ip = s.getsockname()[0]
                s.close()
                network_prefix = '.'.join(local_ip.split('.')[:-1])
            except:
                logger.warning("Konnte lokale IP nicht ermitteln, verwende 192.168.1.x")
                network_prefix = "192.168.1"
        else:
            network_prefix = '.'.join(network.split('.')[:3])
        
        logger.info(f"Scanne Netzwerk {network_prefix}.0/24 nach WLED-Geräten...")
        
        def check_ip(ip):
            if WLEDDiscovery.is_wled_device(ip):
                return ip
            return None
        
        wled_devices = []
        with concurrent.futures.ThreadPoolExecutor(max_workers=max_threads) as executor:
            futures = [executor.submit(check_ip, f"{network_prefix}.{i}") 
                      for i in range(1, 255)]
            
            for future in concurrent.futures.as_completed(futures):
                result = future.result()
                if result:
                    wled_devices.append(result)
                    logger.info(f"WLED-Gerät gefunden: {result}")
        
        return wled_devices
    
    @staticmethod
    def is_wled_device(ip: str, timeout: float = 1.0) -> bool:
        """Prüft ob IP ein WLED-Gerät ist"""
        try:
            response = requests.get(
                f"http://{ip}/json/info",
                timeout=timeout,
                headers={'User-Agent': 'DeviceDiscovery/1.0'}
            )
            if response.status_code == 200:
                data = response.json()
                return 'ver' in data or 'name' in data
        except:
            pass
        return False


class WLEDAPI:
    """Original WLEDAPI Klasse - unverändert"""
    
    def __init__(self, ip: str):
        self.ip = ip
        self.base_url = f"http://{ip}"
    
    def get_info(self) -> Optional[Dict]:
        try:
            response = requests.get(f"{self.base_url}/json/info", timeout=2)
            response.raise_for_status()
            return response.json()
        except Exception as e:
            logger.error(f"Fehler beim Abrufen der WLED-Info von {self.ip}: {e}")
            return None
    
    def get_presets(self) -> Optional[List]:
        try:
            response = requests.get(f"{self.base_url}/presets.json", timeout=2)
            response.raise_for_status()
            presets_data = response.json()
            preset_list = []
            if isinstance(presets_data, dict):
                for preset_id, preset_data in presets_data.items():
                    preset_name = preset_data.get('n', f'Preset {preset_id}')
                    preset_list.append({int(preset_id): preset_name})
            return preset_list
        except Exception as e:
            logger.error(f"Fehler beim Abrufen der WLED-Presets von {self.ip}: {e}")
            return None
    
    def get_effects(self) -> Optional[List]:
        try:
            response = requests.get(f"{self.base_url}/json/eff", timeout=2)
            response.raise_for_status()
            eff_data = response.json()
            eff_list = []
            for eff_id, eff_name in enumerate(eff_data):
                if not eff_name:
                    eff_name = f"Effect {eff_id}"
                eff_list.append({int(eff_id): eff_name})
            return eff_list
        except Exception as e:
            logger.error(f"Fehler beim Abrufen der WLED-Effects von {self.ip}: {e}")
            return None
    
    def get_state(self) -> Optional[Dict]:
        try:
            response = requests.get(f"{self.base_url}/json/state", timeout=2)
            response.raise_for_status()
            return response.json()
        except Exception as e:
            logger.error(f"Fehler beim Abrufen des WLED-State von {self.ip}: {e}")
            return None
    
    def get_device_data(self) -> Optional[Dict]:
        """Erstellt Geräte-Dict im vereinheitlichten Format"""
        info = self.get_info()
        state = self.get_state()
        
        if not info:
            return None
        
        name = info.get('name', f"WLED {self.ip}")
        preset_values = self.get_presets()
        eff_values = self.get_effects()
        
        commands = [
            {'command': 'on', 'parameters': []},
            {'command': 'off', 'parameters': []},
            {
                'command': 'setBrightness',
                'parameters': [
                    {'name': 'brightness', 'type': 'integer', 'min': 0, 'max': 255}
                ]
            },
            {
                'command': 'setColor',
                'parameters': [
                    {'name': 'red', 'type': 'integer', 'min': 0, 'max': 255},
                    {'name': 'green', 'type': 'integer', 'min': 0, 'max': 255},
                    {'name': 'blue', 'type': 'integer', 'min': 0, 'max': 255}
                ]
            },
            {
                'command': 'setEffect',
                'parameters': [
                    {'name': 'effect', 'type': 'integer', 'min': 0, 'max': 255, 'values': eff_values}
                ]
            },
            {
                'command': 'setPreset',
                'parameters': [
                    {'name': 'preset', 'type': 'integer', 'min': 1, 'max': 250, 'values': preset_values}
                ]
            }
        ]
        
        states = []
        if state:
            states.append({
                'name': 'power',
                'type': 'boolean',
                'current_value': state.get('on', False)
            })
            states.append({
                'name': 'brightness',
                'type': 'integer',
                'current_value': state.get('bri', 0)
            })
            
            segments = state.get('seg', [])
            if segments and len(segments) > 0:
                colors = segments[0].get('col', [[0,0,0]])
                if colors and len(colors) > 0:
                    states.append({
                        'name': 'color_rgb',
                        'type': 'array',
                        'current_value': colors[0]
                    })
        
        return {
            'type': 'WLED',
            'name': name,
            'url': f"wled://{self.ip}",
            'commands': commands,
            'states': states
        }


class WLEDModule(BaseModule):
    """
    WLED Modul - Implementiert BaseModule Interface
    Gibt nur Actors zurück, KEINE DB-Operationen
    """
    
    def is_enabled(self) -> bool:
        """Prüft ob WLED aktiviert ist"""
        return self.config.wled_enable
    
    def discover(self) -> Tuple[List[Dict], List[Dict]]:
        """
        Führt WLED Discovery durch
        
        Returns:
            Tuple (actors, sensors) - WLED sind immer Actors
        """
        logger.info("\n" + "=" * 60)
        logger.info("WLED-GERÄTE WERDEN GESUCHT")
        logger.info("=" * 60)
        
        actors = []
        sensors = []
        
        # Discovery
        wled_ips = WLEDDiscovery.discover_devices(timeout=self.config.wled_discovery_timeout)
        
        # Manuelle IPs hinzufügen
        if self.config.wled_manual_ips:
            logger.info(f"Füge {len(self.config.wled_manual_ips)} manuelle WLED-IPs hinzu...")
            for manual_ip in self.config.wled_manual_ips:
                if manual_ip not in wled_ips:
                    if WLEDDiscovery.is_wled_device(manual_ip):
                        wled_ips.append(manual_ip)
                        logger.info(f"✓ Manuelles WLED-Gerät: {manual_ip}")
                    else:
                        logger.warning(f"⚠ {manual_ip} ist kein WLED-Gerät")
        
        if not wled_ips:
            logger.info("Keine WLED-Geräte gefunden")
            return actors, sensors
        
        logger.info(f"{len(wled_ips)} WLED-Geräte gefunden")
        
        # Gerätedaten abrufen
        for ip in wled_ips:
            try:
                wled = WLEDAPI(ip)
                device_data = wled.get_device_data()
                
                if device_data:
                    actors.append(device_data)
                else:
                    logger.warning(f"⚠ Konnte keine Daten von WLED {ip} abrufen")
            except Exception as e:
                logger.error(f"✗ Fehler beim Verarbeiten von WLED {ip}: {e}")
        
        logger.info(f"WLED: {len(actors)} Aktoren gefunden")
        return actors, sensors
