"""
Point d'entrée principal pour RPA Vision V2.
Initialise tous les composants et lance l'application.
"""

import sys
import signal
import asyncio
from pathlib import Path
from typing import Optional
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QThread, pyqtSignal

from core.config import CONFIG
from core.logger import Logger
from core.embeddings_manager import EmbeddingsManager
from core.llm_manager import LLMManager
from core.learning_manager import LearningManager
from core.orchestrator import Orchestrator
from core.whitelist_manager import WhitelistManager
from core.ui_change_detector import UIChangeDetector
from core.metrics_collector import MetricsCollector
from core.utils.vision_utils import VisionUtils
from core.utils.input_utils import InputUtils
from core.replay_async import ReplayEngine
from gui.minimal_gui import MinimalGUI
from gui import setup_gui_for_orchestrator


class OrchestratorThread(QThread):
    """Thread pour exécuter l'orchestrateur de manière asynchrone."""
    
    error_signal = pyqtSignal(str)
    status_signal = pyqtSignal(str)
    
    def __init__(self, orchestrator: Orchestrator):
        super().__init__()
        self.orchestrator = orchestrator
        self.running = False
    
    def run(self):
        """Exécute la boucle cognitive."""
        self.running = True
        self.status_signal.emit("started")
        
        try:
            # Exécuter l'orchestrateur (méthode synchrone)
            self.orchestrator.run()
            
        except Exception as e:
            self.error_signal.emit(str(e))
        finally:
            self.running = False
            self.status_signal.emit("stopped")
    
    def stop(self):
        """Arrête l'orchestrateur."""
        self.running = False
        self.orchestrator.stop()


class RPAVisionApp:
    """Application principale RPA Vision V2."""
    
    def __init__(self):
        """Initialise l'application."""
        self.config = None
        self.logger = None
        self.embeddings_manager = None
        self.llm_manager = None
        self.vision_utils = None
        self.input_utils = None
        self.replay_engine = None
        self.whitelist_manager = None
        self.ui_change_detector = None
        self.metrics_collector = None
        self.learning_manager = None
        self.orchestrator = None
        self.gui = None
        self.gui_bridge = None
        self.orchestrator_thread = None
        self.qt_app = None
    
    def initialize(self):
        """Initialise tous les composants."""
        print("🚀 Initialisation de RPA Vision V2...")
        
        # 1. Charger la configuration
        print("📋 Chargement de la configuration...")
        self.config = CONFIG
        config_dict = CONFIG
        
        # 2. Initialiser le logger
        print("📝 Initialisation du logger...")
        self.logger = Logger()
        self.logger.log_action({
            "action": "application_startup",
            "version": "2.0"
        })
        
        # 3. Initialiser le gestionnaire d'embeddings
        print("🧠 Chargement du modèle OpenCLIP...")
        self.embeddings_manager = EmbeddingsManager(
            model_name=CONFIG["models"]["clip"],
            logger=self.logger
        )
        
        # 4. Initialiser le gestionnaire LLM
        print("💬 Connexion à Ollama...")
        self.llm_manager = LLMManager(
            model_name=CONFIG["models"]["llm"],
            logger=self.logger
        )
        
        # 5. Initialiser les utilitaires de vision
        print("👁️  Chargement des modèles de vision...")
        self.vision_utils = VisionUtils(config=CONFIG)
        
        # 6. Initialiser les utilitaires d'entrée
        print("⌨️  Initialisation des utilitaires d'entrée...")
        self.input_utils = InputUtils(logger=self.logger, config=CONFIG)
        
        # 7. Initialiser le moteur de rejeu
        print("🔄 Initialisation du moteur de rejeu...")
        self.replay_engine = ReplayEngine(
            input_utils=self.input_utils,
            logger=self.logger,
            config=CONFIG
        )
        
        # 8. Initialiser le gestionnaire de liste blanche
        print("🛡️  Chargement de la liste blanche...")
        self.whitelist_manager = WhitelistManager(logger=self.logger)
        
        # 9. Initialiser le détecteur de changements UI
        print("🔍 Initialisation du détecteur de changements UI...")
        self.ui_change_detector = UIChangeDetector(
            embeddings_manager=self.embeddings_manager,
            logger=self.logger,
            config=CONFIG
        )
        
        # 10. Initialiser le collecteur de métriques
        print("📊 Initialisation du collecteur de métriques...")
        self.metrics_collector = MetricsCollector(logger=self.logger, config=CONFIG)
        
        # 11. Initialiser le gestionnaire d'apprentissage
        print("🎓 Initialisation du gestionnaire d'apprentissage...")
        self.learning_manager = LearningManager(
            embeddings_manager=self.embeddings_manager,
            logger=self.logger,
            config=CONFIG
        )
        
        # 12. Initialiser l'orchestrateur
        print("🎯 Initialisation de l'orchestrateur...")
        self.orchestrator = Orchestrator(
            learning_manager=self.learning_manager,
            vision_utils=self.vision_utils,
            llm_manager=self.llm_manager,
            logger=self.logger,
            config=CONFIG,
            whitelist_manager=self.whitelist_manager,
            input_utils=self.input_utils
        )
        
        print("✅ Tous les composants initialisés avec succès!")
    
    def setup_gui(self):
        """Configure l'interface graphique."""
        print("🖥️  Initialisation de l'interface graphique améliorée...")
        
        # Créer l'application Qt
        self.qt_app = QApplication(sys.argv)
        
        # Utiliser la nouvelle GUI améliorée avec intégration automatique
        self.gui_bridge = setup_gui_for_orchestrator(self.orchestrator)
        self.gui = self.gui_bridge.gui
        
        # Connecter les signaux (déjà fait par le bridge, mais on garde pour compatibilité)
        self.gui.start_requested.connect(self.start_orchestrator)
        self.gui.stop_requested.connect(self.stop_orchestrator)
        self.gui.pause_requested.connect(self.pause_orchestrator)
        
        # Envoyer un message de bienvenue
        self.orchestrator.log_to_gui("✅", "Système initialisé et prêt", "success")
        self.orchestrator.update_gui_stats(
            actions_count=0,
            patterns_count=0,
            workflows_count=0
        )
        
        # Activer les boutons Pause et Arrêter (car démarrage automatique)
        self.gui.pause_button.setEnabled(True)
        self.gui.stop_button.setEnabled(True)
        
        print("✅ Interface graphique améliorée prête!")
    
    def start_orchestrator(self):
        """Démarre l'orchestrateur dans un thread séparé."""
        if self.orchestrator_thread is None or not self.orchestrator_thread.running:
            self.orchestrator_thread = OrchestratorThread(self.orchestrator)
            
            # Connecter les signaux
            self.orchestrator_thread.error_signal.connect(self.on_orchestrator_error)
            self.orchestrator_thread.status_signal.connect(self.on_orchestrator_status)
            
            # Démarrer le thread
            self.orchestrator_thread.start()
            
            self.logger.log_action({
                "action": "orchestrator_started"
            })
    
    def stop_orchestrator(self):
        """Arrête l'orchestrateur."""
        # Arrêter la capture d'événements
        if hasattr(self, 'event_capture') and self.event_capture:
            self.event_capture.stop()
        
        # Arrêter le thread de l'orchestrateur
        if self.orchestrator_thread and self.orchestrator_thread.running:
            self.orchestrator_thread.stop()
            self.orchestrator_thread.wait()  # Attendre la fin du thread
            
            self.logger.log_action({
                "action": "orchestrator_stopped"
            })
    
    def pause_orchestrator(self):
        """Met en pause l'orchestrateur."""
        if self.orchestrator:
            self.orchestrator.pause()
            
            self.logger.log_action({
                "action": "orchestrator_paused"
            })
    
    def on_orchestrator_error(self, error: str):
        """Gère les erreurs de l'orchestrateur."""
        self.logger.log_action({
            "action": "orchestrator_error",
            "error": error
        })
        
        # Logger l'erreur dans la GUI
        if self.orchestrator and hasattr(self.orchestrator, 'log_to_gui'):
            self.orchestrator.log_to_gui("❌", f"Erreur: {error}", "error")
    
    def on_orchestrator_status(self, status: str):
        """Gère les changements de statut de l'orchestrateur."""
        self.logger.log_action({
            "action": "orchestrator_status_changed",
            "status": status
        })
    
    def run(self, mode='shadow', headless=False):
        """
        Lance l'application.
        
        Args:
            mode: Mode opérationnel (shadow, assist, auto)
            headless: Si True, lance sans GUI
        """
        try:
            # Initialiser les composants
            self.initialize()
            
            # Définir le mode
            if self.learning_manager:
                # Mode progressif: démarre en shadow, propose assist après patterns
                if mode == 'progressive':
                    self.orchestrator.enable_progressive_mode()
                    actual_mode = 'shadow'  # Démarre en shadow
                else:
                    actual_mode = mode
                    self.learning_manager.mode = actual_mode
                
                self.logger.log_action({
                    "action": "mode_set",
                    "mode": mode,
                    "actual_mode": actual_mode if mode == 'progressive' else mode,
                    "headless": headless
                })
            
            if headless:
                # Mode headless: juste l'orchestrateur
                print(f"🔇 Mode headless activé - Mode: {mode.upper()}")
                self.orchestrator.run()
            else:
                # Mode normal avec GUI
                self.setup_gui()
                self.gui.show()
                
                # Configurer le gestionnaire de signaux pour arrêt gracieux
                signal.signal(signal.SIGINT, self.signal_handler)
                signal.signal(signal.SIGTERM, self.signal_handler)
                
                print("\n" + "="*60)
                print("🎉 RPA Vision V2 est prêt!")
                print("="*60)
                print(f"\n🎯 Mode: {mode.upper()}")
                print("\n📌 Raccourcis clavier:")
                print("  - Ctrl+Pause : Arrêt d'urgence")
                print("  - Entrée : Valider une suggestion")
                print("  - Échap : Rejeter une suggestion")
                print("  - Alt+C : Corriger une action")
                print("\n� CDémarrage automatique de l'observation...")
                print("="*60 + "\n")
                
                # Envoyer un log de démarrage
                self.orchestrator.log_to_gui("🚀", "Démarrage de l'observation...", "info")
                
                # Démarrer automatiquement l'orchestrator
                self.start_orchestrator()
                
                # Confirmer le démarrage
                self.orchestrator.log_to_gui("👀", "Observation active - En attente d'actions...", "success")
                self.gui.signals.emit_status_change(True)
                
                # Lancer la boucle d'événements Qt
                sys.exit(self.qt_app.exec_())
        
        except KeyboardInterrupt:
            print("\n🛑 Arrêt demandé...")
            self.shutdown()
        except Exception as e:
            print(f"\n❌ Erreur fatale: {e}")
            import traceback
            traceback.print_exc()
            self.logger.log_action({
                "action": "application_fatal_error",
                "error": str(e)
            })
            sys.exit(1)
    
    def signal_handler(self, signum, frame):
        """Gère les signaux d'arrêt (Ctrl+C, etc.)."""
        print("\n\n🛑 Arrêt demandé...")
        self.shutdown()
    
    def shutdown(self):
        """Arrête proprement l'application avec timeout de 5 secondes."""
        import time
        from threading import Thread
        
        print("🔄 Arrêt en cours...")
        start_time = time.time()
        
        # Arrêter la capture d'événements (IMPORTANT pour libérer les listeners pynput)
        if self.orchestrator and hasattr(self.orchestrator, 'event_capture'):
            print("  - Arrêt de la capture d'événements...")
            try:
                self.orchestrator.event_capture.stop()
            except Exception as e:
                print(f"    ⚠️  Erreur lors de l'arrêt de la capture: {e}")
        
        # Arrêter l'orchestrateur avec timeout
        if self.orchestrator_thread:
            print("  - Arrêt de l'orchestrateur...")
            try:
                self.stop_orchestrator()
                # Attendre max 3 secondes que le thread se termine
                if self.orchestrator_thread.isRunning():
                    self.orchestrator_thread.wait(3000)  # 3 secondes en ms
                    if self.orchestrator_thread.isRunning():
                        print("    ⚠️  Timeout: forçage de l'arrêt")
                        self.orchestrator_thread.terminate()
            except Exception as e:
                print(f"    ⚠️  Erreur lors de l'arrêt de l'orchestrateur: {e}")
        
        # Sauvegarder l'état d'apprentissage
        if self.learning_manager:
            print("  - Sauvegarde de l'état d'apprentissage...")
            try:
                # Le learning_manager sauvegarde automatiquement via le logger
                pass
            except Exception as e:
                print(f"    ⚠️  Erreur lors de la sauvegarde: {e}")
        
        # Sauvegarder la liste blanche
        if self.whitelist_manager:
            print("  - Sauvegarde de la liste blanche...")
            try:
                self.whitelist_manager.save_whitelist()
            except Exception as e:
                print(f"    ⚠️  Erreur lors de la sauvegarde: {e}")
        
        # Finaliser les logs
        if self.logger:
            print("  - Finalisation des logs...")
            try:
                self.logger.log_action({
                    "action": "application_shutdown",
                    "graceful": True,
                    "duration_seconds": time.time() - start_time
                })
            except Exception as e:
                print(f"    ⚠️  Erreur lors de la finalisation des logs: {e}")
        
        elapsed = time.time() - start_time
        print(f"✅ Arrêt terminé en {elapsed:.1f}s. Au revoir!")
        
        # Quitter l'application Qt
        if self.qt_app:
            self.qt_app.quit()


def main():
    """Point d'entrée principal."""
    import argparse
    
    # Parser les arguments
    parser = argparse.ArgumentParser(description='RPA Vision V2 - Automatisation intelligente')
    parser.add_argument(
        '--mode',
        type=str,
        choices=['shadow', 'assist', 'auto', 'progressive'],
        default='progressive',
        help='Mode opérationnel: progressive (adaptatif), shadow (observation), assist (suggestions), auto (automatique)'
    )
    parser.add_argument(
        '--headless',
        action='store_true',
        help='Lancer sans interface graphique (pour tests)'
    )
    
    args = parser.parse_args()
    
    # Créer et lancer l'application
    app = RPAVisionApp()
    app.run(mode=args.mode, headless=args.headless)


if __name__ == "__main__":
    main()
