"""
Handler spécialisé pour l'envoi de logs - VERSION REFACTORISÉE
"""

import time
import json
import os
from datetime import datetime
from typing import Optional, Dict, Any
from .base import BaseHandler
from ....monitoring.logger import logger
from ....protos import sdk_service_pb2


class LogHandler(BaseHandler):
    """
    Handler pour l'envoi optimisé de logs
    
    Responsabilités UNIQUES:
    - Collecte et mise en buffer des logs
    - Envoi par batch au serveur
    - Persistance locale en cas d'échec
    - Retry intelligent des logs échoués
    """
    
    def __init__(self, grpc_client):
        super().__init__(grpc_client, "LogHandler")
        
        # Configuration du batching
        self.batch_size = 50
        self.flush_interval = 30  # secondes
        self.max_queue_size = 1000
        
        # Queue en mémoire
        self.log_queue = []
        
        # Timing
        self._last_flush = time.time()
        
        # Persistance locale
        self.persistent_file = self._get_persistent_file_path()
        self._load_persistent_logs()
        
        # Statistiques
        self._logs_sent_count = 0
        self._logs_failed_count = 0
    
    def run(self):
        """Traitement des logs en batch"""
        self.start()
        
        while self.is_running():
            try:
                current_time = time.time()
                
                # Flush périodique ou si queue pleine
                if (current_time - self._last_flush >= self.flush_interval or 
                    len(self.log_queue) >= self.batch_size):
                    self._flush_logs()
                    self._last_flush = current_time
                
                time.sleep(5)  # Vérification toutes les 5 secondes
                
            except Exception as e:
                self._handle_error(e, "traitement logs")
                time.sleep(2)
        
        # Flush final
        self._flush_logs()
        self.stop()
    
    def add_log(self, level: str, message: str, context: Dict = None) -> bool:
        """
        Ajoute un log à la queue avec validation
        
        Args:
            level: Niveau du log
            message: Message
            context: Contexte optionnel
            
        Returns:
            bool: True si ajouté avec succès
        """
        try:
            # Validation
            if not self._validate_log_params(level, message):
                return False
            
            # Éviter la surcharge
            if len(self.log_queue) >= self.max_queue_size:
                self._rotate_queue()
            
            # Créer l'entrée
            log_entry = self._create_log_entry(level, message, context)
            
            # Ajouter à la queue
            self.log_queue.append(log_entry)
            
            # Sauvegarder si nécessaire
            self._save_if_needed()
            
            return True
            
        except Exception as e:
            self._handle_error(e, "ajout log")
            return False
    
    def _flush_logs(self):
        """Envoie les logs par batch"""
        if not self.log_queue:
            return
        
        try:
            # Préparer le batch
            batch = self.log_queue[:self.batch_size]
            
            if not self.is_connected():
                logger('debug', f"[{self.name}] Connexion indisponible, report flush")
                return
            
            # Envoyer via le canal de contrôle
            success = self._send_log_batch(batch)
            
            if success:
                # Supprimer les logs envoyés
                self.log_queue = self.log_queue[len(batch):]
                self._logs_sent_count += len(batch)
                
                logger('info', f"[{self.name}] ✅ {len(batch)} logs envoyés")
                
                # Nettoyer le fichier persistant
                self._save_persistent_logs()
            else:
                self._logs_failed_count += len(batch)
                logger('warning', f"[{self.name}] ❌ Échec envoi {len(batch)} logs")
            
        except Exception as e:
            self._handle_error(e, "flush logs")
    
    
        
    def _send_log_batch(self, batch: list) -> bool:
        """Envoie DIRECTEMENT au serveur via ControlHandler, pas via queue centrale"""
        try:
            #print(f"🔵 DEBUG: LogHandler._send_log_batch() - {len(batch)} logs")
            
            if not hasattr(self.grpc_client, 'control_handler') or not self.grpc_client.control_handler:
                #print("🔵 DEBUG: Pas de control_handler disponible")
                return False
            
            control_handler = self.grpc_client.control_handler
            
            sdk_id = self._get_sdk_id()
            if not sdk_id:
                return False
            
            logs_command = self._build_logs_command(sdk_id, batch)
            if not logs_command:
                return False

            try:
                response = self.stub.SendCommand(logs_command)
                success = response and getattr(response, 'success', False)
                return success
            except Exception as e:
                return False
            
        except Exception as e:
            logger('error', f"[{self.name}] Erreur envoi batch: {e}")
            return False
    
    def _build_logs_command(self, sdk_id: str, batch: list):
        """Construit une commande logs pour le serveur"""
        try:
            import json
            from datetime import datetime
            
            logs_data = []
            for log_entry in batch:
                logs_data.append({
                    'id': log_entry.get('id'),
                    'timestamp': log_entry.get('timestamp'),
                    'level': log_entry.get('level', 'INFO'),
                    'message': log_entry.get('message', ''),
                    'context': log_entry.get('context', {}),
                    'metadata': log_entry.get('metadata', {})
                })
            
            command = sdk_service_pb2.ControlCommand(
                command_type="sdk_logs",
                target="server",
                parameters={
                    "sdk_id": str(sdk_id),
                    "logs_batch": json.dumps(logs_data),
                    "timestamp": datetime.now().isoformat(),
                    "batch_size": str(len(logs_data))
                }
            )
            
            return command
            
        except Exception as e:
            logger('error', f"[{self.name}] Erreur construction commande: {e}")
            return None
        

    def _create_log_entry(self, level: str, message: str, context: Dict = None) -> Dict:
        """Crée une entrée de log structurée"""
        timestamp = datetime.now()

        return {
            'id': f"{timestamp.timestamp()}_{hash(message) % 10000}",
            'timestamp': timestamp.isoformat(),
            'level': level.lower(),
            'message': message.strip(),
            'context': context or {},
            'retry_count': 0,
            'created_at': timestamp.timestamp()
        }
    
    def _validate_log_params(self, level: str, message: str) -> bool:
        """Valide les paramètres d'un log"""
        valid_levels = ['debug', 'info', 'warning', 'error', 'critical']
        
        if not level or level.lower() not in valid_levels:
            return False
        
        if not message or not message.strip():
            return False
        
        if len(message) > 10000:  # Limite raisonnable
            return False
        
        return True
    
    def _rotate_queue(self):
        """Rotation de la queue en cas de surcharge"""
        try:
            # Sauvegarder avant rotation
            self._save_persistent_logs()
            
            # Garder seulement les 70% plus récents
            keep_count = int(self.max_queue_size * 0.7)
            self.log_queue = sorted(
                self.log_queue,
                key=lambda x: x.get('created_at', 0)
            )[-keep_count:]
            
            logger('warning', f"[{self.name}] Queue rotée: {len(self.log_queue)} logs gardés")
            
        except Exception as e:
            self._handle_error(e, "rotation queue")
    
    def _get_persistent_file_path(self) -> str:
        """Retourne le chemin du fichier de persistance"""
        try:
            from ....monitoring.logger import get_log_directory
            log_dir = get_log_directory()
            return str(log_dir / 'pending_logs.json')
        except:
            return './pending_logs.json'
    
    def _save_persistent_logs(self):
        """Sauvegarde la queue sur disque"""
        try:
            if not self.log_queue:
                # Supprimer le fichier s'il n'y a plus de logs
                if os.path.exists(self.persistent_file):
                    os.remove(self.persistent_file)
                return
            
            # Sauvegarde atomique
            temp_file = f"{self.persistent_file}.tmp"
            
            with open(temp_file, 'w', encoding='utf-8') as f:
                json.dump(self.log_queue, f, indent=2, default=str)
            
            # Renommage atomique
            if os.path.exists(self.persistent_file):
                os.remove(self.persistent_file)
            os.rename(temp_file, self.persistent_file)
            
        except Exception as e:
            logger('debug', f"[{self.name}] Erreur sauvegarde: {e}")
    
    def _load_persistent_logs(self):
        """Charge les logs depuis le disque"""
        try:
            if not os.path.exists(self.persistent_file):
                return
            
            with open(self.persistent_file, 'r', encoding='utf-8') as f:
                data = json.load(f)
            
            if isinstance(data, list):
                # Valider les entrées
                valid_logs = [log for log in data if self._is_valid_log_entry(log)]
                self.log_queue.extend(valid_logs)
                
                if valid_logs:
                    logger('info', f"[{self.name}] {len(valid_logs)} logs restaurés")
            
        except Exception as e:
            logger('debug', f"[{self.name}] Erreur chargement: {e}")
    
    def _is_valid_log_entry(self, entry: Dict) -> bool:
        """Valide une entrée de log"""
        required_fields = ['id', 'timestamp', 'level', 'message']
        return (isinstance(entry, dict) and 
                all(field in entry for field in required_fields) and
                entry.get('level') in ['debug', 'info', 'warning', 'error', 'critical'])
    
    def _save_if_needed(self):
        """Sauvegarde intelligente"""
        # Sauvegarder toutes les 10 entrées ou si logs critiques
        if (len(self.log_queue) % 10 == 0 or 
            any(log.get('level') == 'critical' for log in self.log_queue[-3:])):
            self._save_persistent_logs()
    
    def _get_sdk_id(self) -> Optional[str]:
        """Obtient le SDK ID depuis le client"""
        try:
            lescopr_client = getattr(self.grpc_client, 'lescopr_client', None)
            if lescopr_client:
                return getattr(lescopr_client, 'sdk_id', None)
        except:
            pass
        return None
    
    def get_stats(self) -> Dict[str, Any]:
        """Statistiques étendues du handler"""
        stats = super().get_stats()
        stats.update({
            'queue_size': len(self.log_queue),
            'logs_sent_total': self._logs_sent_count,
            'logs_failed_total': self._logs_failed_count,
            'last_flush': self._last_flush,
            'persistent_file_exists': os.path.exists(self.persistent_file)
        })
        return stats
    
    def cleanup(self):
        """Nettoyage lors de l'arrêt"""
        try:
            # Flush final
            self._flush_logs()
            
            # Sauvegarder les logs restants
            if self.log_queue:
                self._save_persistent_logs()
                logger('info', f"[{self.name}] {len(self.log_queue)} logs sauvegardés à l'arrêt")
            
        except Exception as e:
            logger('error', f"[{self.name}] Erreur cleanup: {e}")
        
        super().cleanup()


class DaemonLogHandler(BaseHandler):
    """Handler spécialisé pour l'envoi vers daemon local"""
    
    def __init__(self, grpc_client):
        super().__init__(grpc_client, "DaemonLogHandler")
        
        self.batch_size = 20
        self.flush_interval = 5
        
        self.log_queue = []
        self._last_flush = time.time()
        
        self._init_daemon_communication()

    def _get_sdk_id(self) -> Optional[str]:
        """Obtient le SDK ID depuis le client"""
        try:
            if hasattr(self.grpc_client, 'lescopr_client'):
                lescopr_client = self.grpc_client.lescopr_client
                
                if hasattr(lescopr_client, 'sdk_id'):
                    sdk_id = lescopr_client.sdk_id
                    return sdk_id
            
            return "unknown_sdk_id"
            
        except Exception as e:
            return "error_sdk_id"

    
    def _init_daemon_communication(self):
        """Initialise la communication avec le daemon local"""
        self.communication_methods = [
            self._send_via_unix_socket,
            self._send_via_shared_memory,
            self._send_via_file_queue
        ]
    
    def add_log(self, level: str, message: str, metadata: Dict = None) -> bool:
        """Ajoute un log à la queue et déclenche envoi immédiat."""
        try:
            
            if not self.is_running():
                return False
            
            log_entry = {
                'level': level.upper(),
                'message': message,
                'metadata': metadata or {},
                'timestamp': time.time(),
                'sdk_id': self._get_sdk_id()  # ✅ AJOUTER SDK_ID
            }
            
            # ✅ UTILISER LA BONNE QUEUE (log_queue, pas _log_queue)
            self.log_queue.append(log_entry)
            #print(f"🔵 DEBUG: Log ajouté à la queue - taille: {len(self.log_queue)}")
            
            # ✅ FLUSH IMMÉDIAT (PAS D'ÉVÉNEMENT)
            self._flush_to_daemon()
            #print("🔵 DEBUG: Flush immédiat déclenché")

            return True
            
        except Exception as e:
            #print(f"🔵 DEBUG: Exception add_log(): {e}")
            import traceback
            return False
    

    # Dans logs.py - CORRIGER _flush_to_daemon() ligne 390
    def _flush_to_daemon(self):
        """Flush vers daemon avec DEBUG complet"""
        if not self.log_queue:
            #print("🔵 DEBUG: Queue vide, pas de flush")
            return

        #print(f"🔵 DEBUG: _flush_to_daemon() - {len(self.log_queue)} logs à envoyer")
    
        for i, method in enumerate(self.communication_methods):
            method_name = method.__name__
            #print(f"🔵 DEBUG: Tentative méthode {i+1}: {method_name}")

            try:
                success = method(self.log_queue.copy())
                #print(f"🔵 DEBUG: {method_name} = {success}")

                if success:
                    #print(f"✅ Envoi réussi via {method_name}")
                    self.log_queue.clear()
                    self._last_flush = time.time()
                    return
            except Exception as e:
                #print(f"🔵 DEBUG: {method_name} échoué: {e}")
                continue

        #print("❌ Toutes les méthodes de communication ont échoué")
        return
    
    def _send_via_unix_socket(self, logs: list) -> bool:
        """Envoi via socket Unix (le plus rapide)"""
        try:
            import socket
            import json

            #print(f"🔵 DEBUG: _send_via_unix_socket() - {len(logs)} logs")

            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
            sock.settimeout(2.0)
            socket_path = '/tmp/lescopr_daemon.sock'
            #print(f"🔵 DEBUG: Connexion à {socket_path}")

            # ✅ TEST EXISTENCE SOCKET
            if not os.path.exists(socket_path):
                #print(f"🔵 DEBUG: Socket {socket_path} n'existe pas")
                return False
            
            sock.connect(socket_path)
            #print("🔵 DEBUG: Socket connecté")

            # ✅ ENVOYER CHAQUE LOG INDIVIDUELLEMENT
            for log in logs:
                data = {
                    'type': 'sdk_log',
                    'sdk_id': log.get('sdk_id', 'unknown'),
                    'level': log['level'],
                    'message': log['message'],
                    'metadata': log.get('metadata', {}),
                    'timestamp': log['timestamp']
                }
                
                json_data = json.dumps(data).encode('utf-8')
                sock.send(json_data + b'\n')  # ✅ DÉLIMITEUR LIGNE
                #print(f"🔵 DEBUG: Log envoyé: {log['level']} - {log['message'][:30]}")

            sock.close()
            #print("✅ Socket fermé - envoi réussi")
            return True
            
        except Exception as e:
            #print(f"🔵 DEBUG: Erreur socket: {e}")
            try:
                sock.close()
            except:
                pass
            return False
        
    def _send_via_file_queue(self, logs: list) -> bool:
        """Fallback : file d'attente daemon"""
        try:
            import json
            import os
            queue_file = '/tmp/lescopr_logs_queue.json'  # ✅ MÊME NOM QUE CONNECTION.PY
            #print(f"🔵 DEBUG: _send_via_file_queue() vers {queue_file}")

            # ✅ CRÉER DIRECTORY SI NÉCESSAIRE
            os.makedirs(os.path.dirname(queue_file), exist_ok=True)
            
            # ✅ APPEND CHAQUE LOG
            with open(queue_file, 'a', encoding='utf-8') as f:
                for log in logs:
                    f.write(json.dumps(log) + '\n')
                f.flush()

            #print(f"✅ {len(logs)} logs écrits dans {queue_file}")
            return True
            
        except Exception as e:
            #print(f"🔵 DEBUG: Erreur file queue: {e}")
            return False
    
    def _send_via_shared_memory(self, logs: list) -> bool:
        """Méthode avancée : mémoire partagée (optionnel)"""
        # ✅ IMPLEMENTATION FUTURE SI NÉCESSAIRE
        return False
    
    def run(self):
        """Boucle daemon handler"""
        self.start()
        
        while self.is_running():
            try:
                current_time = time.time()
                
                if (current_time - self._last_flush >= self.flush_interval and 
                    self.log_queue):
                    self._flush_to_daemon()
                
                time.sleep(2)  # Check fréquent pour daemon
                
            except Exception as e:
                logger('debug', f"[{self.name}] Erreur boucle daemon: {e}")
                time.sleep(1)
        
        # Flush final
        if self.log_queue:
            self._flush_to_daemon()
        
        self.stop()