"""
Daemon principal Lescopr avec shutdown automatique
VERSION REFACTORISÉE - CLEAN ARCHITECTURE SANS SOCKET
"""
import os
import sys
import time
import signal
import json
import socket
import threading
from typing import Optional, Dict, Any

from ..core.lescopr import Lescopr
from ..monitoring.logger import setup_daemon_logging, logger


class LescoprDaemon:
    """
    Daemon principal avec gRPC pur et shutdown automatique
    
    RESPONSABILITÉS CLARIFIÉES:
    - Gestion du cycle de vie daemon
    - Connexion gRPC unique (pas de socket)
    - Shutdown automatique (callback depuis gRPC)
    - Signal handling système
    """
    
    SHUTDOWN_CHECK_INTERVAL = 10
    DAEMON_SHUTDOWN_TIMEOUT = 30

    def __init__(self):
        self.client: Optional[Lescopr] = None
        self.logger = setup_daemon_logging()
        self.running = True
        
        self._shutdown_requested = False
        self._shutdown_lock = threading.Lock()
        self._shutdown_reason = ""
    
    def start(self):
        """Démarre le daemon avec gestion complète des erreurs"""
        self.logger.info(f"[DAEMON] Démarrage Lescopr Daemon (PID: {os.getpid()})")
        
        # ✅ SETUP SIGNAL HANDLERS ROBUSTES
        self._setup_signal_handlers()
        
        try:
            # 1. Initialiser le client avec callback shutdown
            self._initialize_client_with_shutdown_callback()
            
            # 2. Démarrer le mode gRPC pur
            self._start_grpc_mode()
            
            # 3. ✅ BOUCLE PRINCIPALE MAINTENANT OBLIGATOIRE
            self._main_loop_with_shutdown_detection()
            
        except KeyboardInterrupt:
            self.logger.info("[DAEMON] Interruption utilisateur détectée")
            self._request_shutdown("Interruption utilisateur")
        except Exception as e:
            self.logger.critical(f"[DAEMON] Erreur fatale: {e}")
            self._emergency_shutdown(1)
        finally:
            # ✅ NETTOYAGE OBLIGATOIRE
            self._cleanup_and_exit()
    
    def _setup_signal_handlers(self):
        """Configure les gestionnaires de signaux système"""
        def signal_handler(signum, frame):
            try:
                signal_name = signal.Signals(signum).name
            except (ValueError, AttributeError):
                signal_name = str(signum)
            
            self.logger.info(f"[DAEMON] Signal {signal_name} ({signum}) reçu")
            self._request_shutdown(f"Signal {signal_name}")
        
        # Signaux standards UNIX
        signal.signal(signal.SIGTERM, signal_handler)
        signal.signal(signal.SIGINT, signal_handler)
        
        # Signal optionnel pour shutdown automatique
        if hasattr(signal, 'SIGUSR1'):
            signal.signal(signal.SIGUSR1, signal_handler)
    
    def _initialize_client_with_shutdown_callback(self):
        """Initialise le client avec callback CORRECT"""
        try:
            config = self._load_config()
            
            self.client = Lescopr(
                api_key=config.get("api_key"),
                sdk_key=config.get("sdk_key"),
                environment=config.get("environment", "development"),
                auto_logging=False, 
                auto_grpc=False 
            )
            
            self.client.sdk_id = config.get("sdk_id")
            self.client.project_name = config.get("project_name")
            
            # ✅ CONFIGURER LE CALLBACK CORRECT
            self.client._daemon_shutdown_callback = self._handle_grpc_shutdown
            
            self.logger.info(f"[DAEMON] Client initialisé - Projet: {self.client.project_name}")
            self.logger.info(f"[DAEMON] SDK ID: {self.client.sdk_id}")
            self.logger.debug("[DAEMON] Callback shutdown configuré sur client principal")
            
        except Exception as e:
            self.logger.error(f"[DAEMON] Erreur initialisation client: {e}")
            raise
    
    def _ensure_shutdown_callback_configured(self):
        """S'assure que TOUS les niveaux ont le callback"""
        try:
            if not hasattr(self.client, '_daemon_shutdown_callback'):
                self.client._daemon_shutdown_callback = self._handle_grpc_shutdown
                self.logger.info("[DAEMON] ✅ Callback configuré niveau client")
            
            if hasattr(self.client, 'grpc_client') and self.client.grpc_client:
                grpc_client = self.client.grpc_client
                
                if not hasattr(grpc_client, '_daemon_shutdown_callback'):
                    grpc_client._daemon_shutdown_callback = self._handle_grpc_shutdown
                    self.logger.info("[DAEMON] ✅ Callback configuré niveau grpc_client")
            
            if (hasattr(self.client, 'grpc_client') and 
                self.client.grpc_client and 
                hasattr(self.client.grpc_client, 'connection') and 
                self.client.grpc_client.connection):
                
                connection = self.client.grpc_client.connection
                grpc_client = self.client.grpc_client
                
                expected_callback = grpc_client._trigger_shutdown
                current_callback = getattr(connection, '_shutdown_callback', None)
                
                if current_callback != expected_callback:
                    connection._shutdown_callback = expected_callback
                    self.logger.info("[DAEMON] ✅ Callback connection configuré vers GrpcClient")
                else:
                    self.logger.info("[DAEMON] ✅ Callback connection déjà correct")
            
        except Exception as e:
            self.logger.error(f"[DAEMON] Erreur configuration callbacks: {e}")
    
    def _request_shutdown(self, reason: str):
        """Demande un arrêt propre du daemon (thread-safe)"""
        with self._shutdown_lock:
            if self._shutdown_requested:
                return
            
            self._shutdown_requested = True
            self._shutdown_reason = reason
            self.logger.info(f"[DAEMON] Arrêt demandé: {reason}")
        
        self.running = False
    
    def _start_grpc_mode(self):
        """Démarre le mode gRPC avec listeners pour réception logs"""
        self.logger.info("[DAEMON] Démarrage mode gRPC avec listeners...")
        
        grpc_available = self._establish_grpc_connection()
        
        if grpc_available:
            self._setup_socket_listener()
            self._setup_file_queue_listener()
            self.logger.info("[DAEMON] ✅ Mode gRPC actif avec listeners")
        else:
            self.logger.error("[DAEMON] ❌ Échec gRPC - daemon non opérationnel")
            self._request_shutdown("Échec gRPC critique")
    
    def _setup_socket_listener(self):
        """Démarre le listener pour socket Unix"""
        try:
            def socket_listener():
                try:
                    socket_path = '/tmp/lescopr_daemon.sock'
                    
                    if os.path.exists(socket_path):
                        os.unlink(socket_path)
                    
                    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
                    sock.bind(socket_path)
                    sock.listen(5)
                    sock.settimeout(1.0)
                    
                    self.logger.info(f"[DAEMON] Socket listener actif: {socket_path}")
                    
                    while self.running and not self._shutdown_requested:
                        try:
                            conn, addr = sock.accept()
                            
                            # Recevoir données
                            data = conn.recv(4096)
                            if data:
                                try:
                                    log_data = json.loads(data.decode('utf-8'))
                                    # ✅ AJOUTER À LA QUEUE CENTRALE
                                    self.client.add_pending_log(log_data)
                                    self.logger.debug(f"[DAEMON] Log reçu via socket: {log_data.get('level', 'unknown')}")
                                except json.JSONDecodeError as e:
                                    self.logger.warning(f"[DAEMON] JSON invalide via socket: {e}")
                            
                            conn.close()
                            
                        except socket.timeout:
                            continue
                        except Exception as e:
                            if self.running:
                                self.logger.error(f"[DAEMON] Erreur socket listener: {e}")
                    
                    sock.close()
                    if os.path.exists(socket_path):
                        os.unlink(socket_path)
                        
                except Exception as e:
                    self.logger.error(f"[DAEMON] Erreur setup socket listener: {e}")
            
            thread = threading.Thread(target=socket_listener, daemon=True, name="SocketListener")
            thread.start()
            self.logger.info("[DAEMON] Socket listener démarré")
            
        except Exception as e:
            self.logger.error(f"[DAEMON] Erreur démarrage socket listener: {e}")
    
    def _setup_file_queue_listener(self):
        """Démarre le listener pour file queue"""
        try:
            import threading
            
            def file_queue_listener():
                queue_file = '/tmp/lescopr_logs_queue.json'
                
                self.logger.info(f"[DAEMON] File queue listener actif: {queue_file}")
                
                while self.running and not self._shutdown_requested:
                    try:
                        if os.path.exists(queue_file):
                            with open(queue_file, 'r') as f:
                                content = f.read().strip()
                            
                            if content:
                                try:
                                    for line in content.split('\n'):
                                        if line.strip():
                                            log_data = json.loads(line.strip())
                                            self.client.add_pending_log(log_data)
                                            self.logger.debug(f"[DAEMON] Log reçu via file: {log_data.get('level', 'unknown')}")
                                    
                                    # Vider le fichier après lecture
                                    open(queue_file, 'w').close()
                                    
                                except json.JSONDecodeError as e:
                                    self.logger.warning(f"[DAEMON] JSON invalide dans file queue: {e}")
                        
                        time.sleep(2)
                        
                    except Exception as e:
                        if self.running:
                            self.logger.error(f"[DAEMON] Erreur file queue listener: {e}")
                        time.sleep(5)
                
                self.logger.info("[DAEMON] File queue listener arrêté")
            
            # Démarrer le thread
            thread = threading.Thread(target=file_queue_listener, daemon=True, name="FileQueueListener")
            thread.start()
            self.logger.info("[DAEMON] File queue listener démarré")
            
        except Exception as e:
            self.logger.error(f"[DAEMON] Erreur démarrage file queue listener: {e}")

    def _establish_grpc_connection(self) -> bool:
        """Établit la connexion gRPC avec gestion d'erreurs ET logging détaillé"""
        self.logger.info(f"[DAEMON] Client disponible - SDK ID: {self.client.sdk_id}")
        self.logger.info(f"[DAEMON] SDK Key présent: {bool(self.client.sdk_key)}")
        self.logger.info(f"[DAEMON] Projet: {self.client.project_name}")
        
        try:
            self.logger.info("[DAEMON] Tentative connexion gRPC...")
            success = self.client.start_grpc_streaming()
            self.logger.info(f"[DAEMON] Résultat start_grpc_streaming(): {success}")
            
            if success:
                self.logger.info("[DAEMON] ✅ Connexion gRPC établie avec succès")
                
                if hasattr(self.client, 'grpc_client') and self.client.grpc_client:
                    grpc_client = self.client.grpc_client
                    self.logger.info(f"[DAEMON] GrpcClient créé: {type(grpc_client).__name__}")
                    
                    if hasattr(grpc_client, 'connection') and grpc_client.connection:
                        connection = grpc_client.connection
                        
                        if hasattr(connection, '_shutdown_callback'):
                            callback_name = getattr(connection._shutdown_callback, '__name__', 'unknown')
                            self.logger.info(f"[DAEMON] ✅ Callback shutdown configuré: {callback_name}")
                        else:
                            self.logger.warning("[DAEMON] ⚠️ Callback shutdown manquant")
                        
                        connection._shutdown_callback = grpc_client._trigger_shutdown
                        self.logger.info("[DAEMON] ✅ Callback shutdown configuré vers GrpcClient")
                    else:
                        self.logger.warning("[DAEMON] ⚠️ GrpcConnection non créée")
                else:
                    self.logger.warning("[DAEMON] ⚠️ GrpcClient non créé")
                
                return True
            else:
                self.logger.warning("[DAEMON] ⚠️ Connexion gRPC échouée - Mode dégradé")
                return False
                
        except Exception as e:
            self.logger.error(f"[DAEMON] ❌ Exception connexion gRPC: {e}")
            import traceback
            self.logger.debug(f"[DAEMON] Traceback: {traceback.format_exc()}")
            return False
    
    def _main_loop_with_shutdown_detection(self):
        """
        ✅ BOUCLE PRINCIPALE OBLIGATOIRE - MAINTIENT LE DAEMON EN VIE
        
        Cette méthode était SUPPRIMÉE à tort !
        """
        self.logger.info("[DAEMON] Boucle principale avec surveillance shutdown automatique")
        
        try:
            while self.running and not self._shutdown_requested:
                # ✅ 1. PRIORITÉ ABSOLUE: Shutdown automatique
                if self._check_automatic_shutdown():
                    shutdown_reason = self._shutdown_reason or "Shutdown automatique détecté"
                    self.logger.warning(f"[DAEMON] 🛑 {shutdown_reason}")
                    break
                
                # ✅ 2. ATTENTE INTERRUPTIBLE
                self._interruptible_sleep(self.SHUTDOWN_CHECK_INTERVAL)
                
        except KeyboardInterrupt:
            self.logger.info("[DAEMON] Interruption clavier dans boucle principale")
        except Exception as e:
            self.logger.error(f"[DAEMON] Erreur boucle principale: {e}")
        finally:
            self.logger.info("[DAEMON] Sortie boucle principale")
    
    def _check_automatic_shutdown(self) -> bool:
        """
        Vérifie EFFICACEMENT si un shutdown automatique est demandé
        """
        # ✅ 1. VÉRIFICATION DIRECTE DU FLAG
        if self._shutdown_requested:
            return True
        
        # ✅ 2. VÉRIFICATION DÉTAILLÉE VIA CLIENT gRPC
        try:
            if not self.client:
                return False
            
            # Vérifier si le client a un grpc_client
            if not hasattr(self.client, 'grpc_client') or not self.client.grpc_client:
                return False
            
            grpc_client = self.client.grpc_client
            
            # ✅ VÉRIFICATION MULTIPLE DES CONDITIONS DE SHUTDOWN
            
            # Condition 1: should_shutdown() du client
            if hasattr(grpc_client, 'should_shutdown') and grpc_client.should_shutdown():
                self.logger.warning("[DAEMON] GrpcClient.should_shutdown() = True")
                self._shutdown_reason = "GrpcClient demande shutdown"
                return True
            
            # Condition 2: Client plus en fonctionnement
            if (hasattr(grpc_client, 'is_running') and 
                not grpc_client.is_running()):
                
                self.logger.warning("[DAEMON] GrpcClient n'est plus en fonctionnement")
                self._shutdown_reason = "GrpcClient arrêté"
                return True
            
            # ✅ CONDITION 3: VÉRIFIER VIA LE HANDLER PLUTÔT
            if (hasattr(grpc_client, 'control_handler') and 
                grpc_client.control_handler and 
                hasattr(grpc_client.control_handler, '_consecutive_failures')):
                
                handler = grpc_client.control_handler
                failure_count = getattr(handler, '_consecutive_failures', 0)
                max_attempts = getattr(handler, 'MAX_RECONNECT_ATTEMPTS', 5)
                
                if failure_count >= max_attempts:
                    self.logger.critical(f"[DAEMON] Limite d'échecs handler atteinte: {failure_count}/{max_attempts}")
                    self._shutdown_reason = f"Limite d'échecs handler ({failure_count}/{max_attempts})"
                    return True
            
        except Exception as e:
            self.logger.debug(f"[DAEMON] Erreur vérification shutdown gRPC: {e}")
        
        return False
    
    def _interruptible_sleep(self, duration: int):
        """Attente interruptible OPTIMISÉE pour sortie rapide"""
        for i in range(duration):
            if not self.running or self._shutdown_requested:
                self.logger.debug(f"[DAEMON] Sleep interrompu après {i}s")
                break
            
            # ✅ VÉRIFICATION SHUTDOWN PENDANT LE SLEEP (chaque 5s)
            if i > 0 and i % 5 == 0:
                if self._check_automatic_shutdown():
                    self.logger.debug(f"[DAEMON] Shutdown détecté pendant sleep après {i}s")
                    break
            
            time.sleep(1)
    
    def _handle_grpc_shutdown(self):
        """
        Callback AMÉLIORÉ appelé par GrpcClient en cas de shutdown automatique
        """
        with self._shutdown_lock:
            if self._shutdown_requested:
                return
            
            self._shutdown_requested = True
            self._shutdown_reason = "Shutdown automatique gRPC (5 échecs)"
            self.logger.critical(f"[DAEMON] 🛑 {self._shutdown_reason}")
        
        # ✅ ARRÊTER LA BOUCLE PRINCIPALE IMMÉDIATEMENT
        self.running = False
        
        # ✅ LOG SUPPLÉMENTAIRE POUR DEBUG
        self.logger.info("[DAEMON] Flags de shutdown mis à jour:")
        self.logger.info(f"[DAEMON]   ├─ running: {self.running}")
        self.logger.info(f"[DAEMON]   ├─ _shutdown_requested: {self._shutdown_requested}")
        self.logger.info(f"[DAEMON]   └─ reason: {self._shutdown_reason}")
        
        # ✅ FORCER RÉVEIL DE LA BOUCLE PRINCIPALE (optionnel)
        try:
            os.kill(os.getpid(), signal.SIGUSR1)
        except Exception as e:
            self.logger.debug(f"[DAEMON] Erreur signal SIGUSR1: {e}")
    
    def _cleanup_and_exit(self):
        """✅ NETTOYAGE COMPLET - OBLIGATOIRE"""
        shutdown_start = time.time()
        self.logger.info("[DAEMON] Début procédure d'arrêt propre...")
        
        try:
            # ✅ 1. DÉCONNECTER LE CLIENT gRPC
            self._disconnect_client_gracefully()
            
            # ✅ 2. NETTOYAGE FINAL
            self._final_cleanup()
            
            shutdown_duration = time.time() - shutdown_start
            reason = self._shutdown_reason or "Arrêt normal"
            self.logger.info(f"[DAEMON] 🛑 Arrêt propre terminé en {shutdown_duration:.2f}s - {reason}")
            
        except Exception as e:
            self.logger.error(f"[DAEMON] Erreur pendant cleanup: {e}")
    
    def _disconnect_client_gracefully(self):
        """Déconnecte le client avec son propre système de timeout"""
        if not self.client:
            return
        
        try:
            if hasattr(self.client, 'grpc_client') and self.client.grpc_client:
                # ✅ LE CLIENT GÈRE SON PROPRE TIMEOUT
                self.client.grpc_client.disconnect()
                self.logger.debug("[DAEMON] Client gRPC déconnecté")
            else:
                self.logger.debug("[DAEMON] Pas de client gRPC à déconnecter")
                
        except Exception as e:
            self.logger.warning(f"[DAEMON] Erreur déconnexion client: {e}")
    
    def _final_cleanup(self):
        """Nettoyage final des ressources"""
        try:
            # Nettoyer les références
            self.client = None
            
            # Nettoyage optionnel des fichiers temporaires
            temp_files = [
                '/tmp/lescopr_daemon.sock',
                '/tmp/lescopr.pid'
            ]
            
            for temp_file in temp_files:
                try:
                    if os.path.exists(temp_file):
                        os.unlink(temp_file)
                        self.logger.debug(f"[DAEMON] Supprimé {temp_file}")
                except Exception:
                    pass
                    
        except Exception as e:
            self.logger.debug(f"[DAEMON] Erreur cleanup final: {e}")
    
    def _emergency_shutdown(self, exit_code: int = 1):
        """Arrêt d'urgence en cas d'erreur critique"""
        self.logger.critical(f"[DAEMON] 🆘 Arrêt d'urgence (code {exit_code})")
        sys.exit(exit_code)
    
    def _load_config(self) -> Dict[str, Any]:
        """
        Charge la configuration depuis .lescopr.json
        
        Returns:
            dict: Configuration chargée
            
        Raises:
            FileNotFoundError: Si aucune configuration trouvée
            ValueError: Si erreur de parsing
        """
        # ✅ PRIORITÉ: Variable d'environnement
        config_path = os.environ.get('LESCOPR_CONFIG_PATH')
        
        # ✅ FALLBACK: Répertoire courant
        if not config_path or not os.path.exists(config_path):
            config_path = os.path.join(os.getcwd(), '.lescopr.json')
            
        if not os.path.exists(config_path):
            raise FileNotFoundError(f"Configuration non trouvée: {config_path}")
        
        try:
            with open(config_path, 'r', encoding='utf-8') as f:
                config = json.load(f)
            
            # ✅ VALIDATION BASIQUE
            required_fields = ['api_key', 'sdk_key', 'sdk_id']
            missing_fields = [field for field in required_fields if not config.get(field)]
            
            if missing_fields:
                raise ValueError(f"Champs manquants dans la configuration: {missing_fields}")
            
            self.logger.info(f"[DAEMON] Configuration chargée: {config_path}")
            return config
            
        except json.JSONDecodeError as e:
            raise ValueError(f"Erreur parsing JSON: {e}")
        except Exception as e:
            raise ValueError(f"Erreur lecture configuration: {e}")



def main():
    """
    Point d'entrée du daemon avec gestion d'erreurs globale
    
    Configure l'environnement daemon et démarre le processus principal
    """
    try:
        os.environ['LESCOPR_DAEMON_MODE'] = 'true'
        
        daemon = LescoprDaemon()
        daemon.start()
        
    except KeyboardInterrupt:
        print("\n[DAEMON] Interruption par l'utilisateur")
        sys.exit(0)
    except Exception as e:
        print(f"[DAEMON] Erreur fatale: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()