"""
Gestionnaire de liste blanche pour RPA Vision V2
Gère la liste des fenêtres d'application autorisées pour l'automatisation
avec persistance et confirmation administrateur pour les modifications.
"""

import json
import os
from pathlib import Path
from typing import List, Optional, Dict, Any
from datetime import datetime

try:
    from .logger import Logger
    from .config import get_data_paths, get_security_config
except ImportError:
    # Pour tests standalone
    import sys
    sys.path.insert(0, str(Path(__file__).parent))
    from logger import Logger
    from config import get_data_paths, get_security_config


class WhitelistManager:
    """
    Gestionnaire de liste blanche pour contrôler les fenêtres autorisées
    
    Attributes:
        whitelist_path: Chemin du fichier de liste blanche
        whitelist: Liste des patterns de fenêtres autorisées
        logger: Logger pour journalisation des modifications
        require_admin_confirmation: Si True, nécessite confirmation pour ajouts
    """
    
    def __init__(
        self,
        whitelist_path: Optional[str] = None,
        logger: Optional[Logger] = None,
        require_admin_confirmation: bool = True
    ):
        """
        Initialise le gestionnaire de liste blanche
        
        Args:
            whitelist_path: Chemin du fichier de liste blanche (utilise config par défaut si None)
            logger: Logger pour journalisation (crée un nouveau si None)
            require_admin_confirmation: Si True, nécessite confirmation pour ajouts
        """
        # Configuration des chemins
        data_paths = get_data_paths()
        if whitelist_path:
            self.whitelist_path = Path(whitelist_path)
        else:
            # Utiliser le répertoire user_profiles pour stocker la liste blanche
            profiles_dir = Path(data_paths["user_profiles"])
            profiles_dir.mkdir(parents=True, exist_ok=True)
            self.whitelist_path = profiles_dir / "whitelist.json"
        
        # Logger
        self.logger = logger or Logger()
        
        # Configuration
        self.require_admin_confirmation = require_admin_confirmation
        self.security_config = get_security_config()
        
        # Charger la liste blanche
        self.whitelist: List[str] = []
        self.metadata: Dict[str, Any] = {}
        self.load_whitelist()
        
        self.logger.log_action({
            "action": "whitelist_manager_initialized",
            "whitelist_path": str(self.whitelist_path),
            "whitelist_size": len(self.whitelist),
            "require_admin_confirmation": self.require_admin_confirmation
        })
    
    def is_window_allowed(self, window_title: str) -> bool:
        """
        Vérifie si une fenêtre est autorisée dans la liste blanche
        
        Args:
            window_title: Titre de la fenêtre à vérifier
            
        Returns:
            True si la fenêtre est autorisée, False sinon
        """
        if not window_title:
            return False
        
        # Si la liste blanche est vide, tout est bloqué par défaut
        if not self.whitelist:
            self.logger.log_security_event({
                "event_type": "whitelist_check",
                "window": window_title,
                "allowed": False,
                "reason": "empty_whitelist"
            })
            return False
        
        # Vérifier si le titre correspond à un pattern de la liste blanche
        window_title_lower = window_title.lower()
        
        for pattern in self.whitelist:
            pattern_lower = pattern.lower()
            
            # Support pour wildcards simples
            if pattern_lower.endswith("*"):
                # Pattern de type "Firefox*" - correspondance de préfixe
                prefix = pattern_lower[:-1]
                if window_title_lower.startswith(prefix):
                    return True
            elif pattern_lower.startswith("*"):
                # Pattern de type "*Firefox" - correspondance de suffixe
                suffix = pattern_lower[1:]
                if window_title_lower.endswith(suffix):
                    return True
            elif "*" in pattern_lower:
                # Pattern de type "Fire*fox" - correspondance avec wildcard au milieu
                parts = pattern_lower.split("*")
                if all(part in window_title_lower for part in parts):
                    # Vérifier l'ordre des parties
                    pos = 0
                    for part in parts:
                        idx = window_title_lower.find(part, pos)
                        if idx == -1:
                            break
                        pos = idx + len(part)
                    else:
                        return True
            else:
                # Correspondance exacte (insensible à la casse)
                if pattern_lower == window_title_lower:
                    return True
                # Ou correspondance partielle
                if pattern_lower in window_title_lower:
                    return True
        
        # Aucune correspondance trouvée
        self.logger.log_security_event({
            "event_type": "whitelist_check",
            "window": window_title,
            "allowed": False,
            "reason": "no_match"
        })
        
        return False

    def add_to_whitelist(
        self,
        window_title: str,
        admin_confirmed: bool = False,
        added_by: str = "user"
    ) -> bool:
        """
        Ajoute une fenêtre à la liste blanche avec confirmation admin optionnelle
        
        Args:
            window_title: Pattern de titre de fenêtre à autoriser
            admin_confirmed: Si True, bypass la confirmation admin
            added_by: Identifiant de l'utilisateur qui ajoute (pour audit)
            
        Returns:
            True si ajouté avec succès, False sinon
        """
        if not window_title or not window_title.strip():
            self.logger.log_action({
                "action": "whitelist_add_failed",
                "reason": "empty_window_title"
            })
            return False
        
        window_title = window_title.strip()
        
        # Vérifier si déjà dans la liste blanche
        if window_title in self.whitelist:
            self.logger.log_action({
                "action": "whitelist_add_skipped",
                "window": window_title,
                "reason": "already_exists"
            })
            return True  # Déjà présent, considéré comme succès
        
        # Vérifier la confirmation admin si requise
        if self.require_admin_confirmation and not admin_confirmed:
            self.logger.log_security_event({
                "event_type": "whitelist_add_pending",
                "window": window_title,
                "added_by": added_by,
                "details": "Confirmation administrateur requise"
            })
            return False
        
        # Ajouter à la liste blanche
        self.whitelist.append(window_title)
        
        # Mettre à jour les métadonnées
        if "entries" not in self.metadata:
            self.metadata["entries"] = {}
        
        self.metadata["entries"][window_title] = {
            "added_at": datetime.now().isoformat(),
            "added_by": added_by,
            "admin_confirmed": admin_confirmed
        }
        
        # Sauvegarder
        self.save_whitelist()
        
        self.logger.log_action({
            "action": "whitelist_add_success",
            "window": window_title,
            "added_by": added_by,
            "admin_confirmed": admin_confirmed,
            "whitelist_size": len(self.whitelist)
        })
        
        return True
    
    def remove_from_whitelist(self, window_title: str) -> bool:
        """
        Retire une fenêtre de la liste blanche
        
        Args:
            window_title: Pattern de titre de fenêtre à retirer
            
        Returns:
            True si retiré avec succès, False si non trouvé
        """
        if window_title not in self.whitelist:
            self.logger.log_action({
                "action": "whitelist_remove_failed",
                "window": window_title,
                "reason": "not_found"
            })
            return False
        
        # Retirer de la liste blanche
        self.whitelist.remove(window_title)
        
        # Retirer des métadonnées
        if "entries" in self.metadata and window_title in self.metadata["entries"]:
            del self.metadata["entries"][window_title]
        
        # Sauvegarder
        self.save_whitelist()
        
        self.logger.log_action({
            "action": "whitelist_remove_success",
            "window": window_title,
            "whitelist_size": len(self.whitelist)
        })
        
        return True
    
    def load_whitelist(self) -> bool:
        """
        Charge la liste blanche depuis le fichier
        
        Returns:
            True si chargé avec succès, False sinon
        """
        try:
            if not self.whitelist_path.exists():
                # Créer une liste blanche par défaut
                self.whitelist = self.security_config.get("default_whitelist", [])
                self.metadata = {
                    "created_at": datetime.now().isoformat(),
                    "version": "1.0",
                    "entries": {}
                }
                
                # Sauvegarder la liste blanche par défaut
                self.save_whitelist()
                
                self.logger.log_action({
                    "action": "whitelist_created",
                    "path": str(self.whitelist_path),
                    "default_size": len(self.whitelist)
                })
                
                return True
            
            # Charger depuis le fichier
            with open(self.whitelist_path, 'r', encoding='utf-8') as f:
                data = json.load(f)
            
            self.whitelist = data.get("whitelist", [])
            self.metadata = data.get("metadata", {})
            
            self.logger.log_action({
                "action": "whitelist_loaded",
                "path": str(self.whitelist_path),
                "whitelist_size": len(self.whitelist)
            })
            
            return True
        
        except Exception as e:
            self.logger.log_action({
                "action": "whitelist_load_failed",
                "path": str(self.whitelist_path),
                "error": str(e)
            })
            
            # En cas d'erreur, utiliser une liste vide
            self.whitelist = []
            self.metadata = {}
            
            return False
    
    def save_whitelist(self) -> bool:
        """
        Sauvegarde la liste blanche dans le fichier
        
        Returns:
            True si sauvegardé avec succès, False sinon
        """
        try:
            # Mettre à jour les métadonnées
            self.metadata["last_modified"] = datetime.now().isoformat()
            self.metadata["version"] = self.metadata.get("version", "1.0")
            
            # Préparer les données
            data = {
                "whitelist": self.whitelist,
                "metadata": self.metadata
            }
            
            # S'assurer que le répertoire parent existe
            self.whitelist_path.parent.mkdir(parents=True, exist_ok=True)
            
            # Sauvegarder dans le fichier
            with open(self.whitelist_path, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=2, ensure_ascii=False)
            
            # Restreindre les permissions (lecture/écriture propriétaire uniquement)
            os.chmod(self.whitelist_path, 0o600)
            
            self.logger.log_action({
                "action": "whitelist_saved",
                "path": str(self.whitelist_path),
                "whitelist_size": len(self.whitelist)
            })
            
            return True
        
        except Exception as e:
            self.logger.log_action({
                "action": "whitelist_save_failed",
                "path": str(self.whitelist_path),
                "error": str(e)
            })
            
            return False
    
    def get_whitelist(self) -> List[str]:
        """
        Retourne une copie de la liste blanche actuelle
        
        Returns:
            Liste des patterns de fenêtres autorisées
        """
        return self.whitelist.copy()
    
    def clear_whitelist(self) -> bool:
        """
        Vide complètement la liste blanche
        
        Returns:
            True si vidé avec succès
        """
        old_size = len(self.whitelist)
        self.whitelist = []
        self.metadata["entries"] = {}
        
        self.save_whitelist()
        
        self.logger.log_security_event({
            "event_type": "whitelist_cleared",
            "previous_size": old_size,
            "details": "Liste blanche complètement vidée"
        })
        
        return True
    
    def get_metadata(self) -> Dict[str, Any]:
        """
        Retourne les métadonnées de la liste blanche
        
        Returns:
            Dictionnaire contenant les métadonnées
        """
        return self.metadata.copy()
    
    def get_entry_info(self, window_title: str) -> Optional[Dict[str, Any]]:
        """
        Retourne les informations sur une entrée spécifique
        
        Args:
            window_title: Pattern de fenêtre à rechercher
            
        Returns:
            Dictionnaire avec les informations ou None si non trouvé
        """
        if window_title not in self.whitelist:
            return None
        
        entry_metadata = self.metadata.get("entries", {}).get(window_title, {})
        
        return {
            "window_title": window_title,
            "in_whitelist": True,
            **entry_metadata
        }
    
    def export_whitelist(self, export_path: str) -> bool:
        """
        Exporte la liste blanche vers un fichier
        
        Args:
            export_path: Chemin du fichier d'export
            
        Returns:
            True si exporté avec succès
        """
        try:
            export_data = {
                "whitelist": self.whitelist,
                "metadata": self.metadata,
                "exported_at": datetime.now().isoformat()
            }
            
            with open(export_path, 'w', encoding='utf-8') as f:
                json.dump(export_data, f, indent=2, ensure_ascii=False)
            
            self.logger.log_action({
                "action": "whitelist_exported",
                "export_path": export_path,
                "whitelist_size": len(self.whitelist)
            })
            
            return True
        
        except Exception as e:
            self.logger.log_action({
                "action": "whitelist_export_failed",
                "export_path": export_path,
                "error": str(e)
            })
            
            return False
    
    def import_whitelist(
        self,
        import_path: str,
        merge: bool = False,
        admin_confirmed: bool = False
    ) -> bool:
        """
        Importe une liste blanche depuis un fichier
        
        Args:
            import_path: Chemin du fichier à importer
            merge: Si True, fusionne avec la liste existante; sinon remplace
            admin_confirmed: Si True, bypass la confirmation admin
            
        Returns:
            True si importé avec succès
        """
        try:
            with open(import_path, 'r', encoding='utf-8') as f:
                import_data = json.load(f)
            
            imported_whitelist = import_data.get("whitelist", [])
            
            if not merge:
                # Remplacer complètement
                self.whitelist = imported_whitelist
                self.metadata = import_data.get("metadata", {})
            else:
                # Fusionner
                for window_title in imported_whitelist:
                    if window_title not in self.whitelist:
                        self.add_to_whitelist(
                            window_title,
                            admin_confirmed=admin_confirmed,
                            added_by="import"
                        )
            
            self.save_whitelist()
            
            self.logger.log_action({
                "action": "whitelist_imported",
                "import_path": import_path,
                "merge": merge,
                "whitelist_size": len(self.whitelist)
            })
            
            return True
        
        except Exception as e:
            self.logger.log_action({
                "action": "whitelist_import_failed",
                "import_path": import_path,
                "error": str(e)
            })
            
            return False
    
    def get_statistics(self) -> Dict[str, Any]:
        """
        Retourne des statistiques sur la liste blanche
        
        Returns:
            Dictionnaire contenant les statistiques
        """
        stats = {
            "total_entries": len(self.whitelist),
            "created_at": self.metadata.get("created_at"),
            "last_modified": self.metadata.get("last_modified"),
            "version": self.metadata.get("version"),
            "entries_with_wildcards": sum(1 for w in self.whitelist if "*" in w),
            "entries_exact": sum(1 for w in self.whitelist if "*" not in w)
        }
        
        return stats


if __name__ == "__main__":
    """Tests du gestionnaire de liste blanche"""
    print("Test du WhitelistManager RPA Vision V2")
    print("=" * 50)
    
    import tempfile
    import shutil
    
    # Créer un répertoire de test
    test_dir = tempfile.mkdtemp()
    test_whitelist_path = os.path.join(test_dir, "whitelist.json")
    
    try:
        # Test 1: Initialisation
        print("\n1. Test d'initialisation:")
        manager = WhitelistManager(
            whitelist_path=test_whitelist_path,
            require_admin_confirmation=False  # Désactiver pour les tests
        )
        print(f"   ✓ WhitelistManager initialisé")
        print(f"   - Chemin: {manager.whitelist_path}")
        print(f"   - Taille initiale: {len(manager.whitelist)}")
        
        # Test 2: Ajout d'entrées
        print("\n2. Test d'ajout d'entrées:")
        test_windows = [
            "Dolibarr*",
            "Firefox*",
            "*Chrome",
            "Visual Studio Code"
        ]
        
        for window in test_windows:
            success = manager.add_to_whitelist(window, admin_confirmed=True)
            status = "✓" if success else "✗"
            print(f"   {status} Ajouté: {window}")
        
        print(f"   - Taille après ajouts: {len(manager.whitelist)}")
        
        # Test 3: Vérification is_window_allowed
        print("\n3. Test is_window_allowed:")
        test_cases = [
            ("Dolibarr - Facturation", True),
            ("Firefox - Mozilla", True),
            ("Google Chrome", True),
            ("Visual Studio Code", True),
            ("Unknown Application", False),
            ("Notepad", False)
        ]
        
        for window, expected in test_cases:
            allowed = manager.is_window_allowed(window)
            status = "✓" if allowed == expected else "✗"
            result = "Autorisé" if allowed else "Bloqué"
            print(f"   {status} {result}: {window}")
        
        # Test 4: Sauvegarde et rechargement
        print("\n4. Test sauvegarde et rechargement:")
        manager.save_whitelist()
        print("   ✓ Liste blanche sauvegardée")
        
        # Créer un nouveau manager pour tester le chargement
        manager2 = WhitelistManager(
            whitelist_path=test_whitelist_path,
            require_admin_confirmation=False
        )
        print(f"   ✓ Liste blanche rechargée")
        print(f"   - Taille après rechargement: {len(manager2.whitelist)}")
        assert len(manager2.whitelist) == len(test_windows), "Taille incorrecte après rechargement!"
        
        # Test 5: Suppression d'entrée
        print("\n5. Test de suppression:")
        removed = manager.remove_from_whitelist("Firefox*")
        print(f"   ✓ Supprimé: Firefox* (succès={removed})")
        print(f"   - Taille après suppression: {len(manager.whitelist)}")
        
        # Test 6: Métadonnées
        print("\n6. Test des métadonnées:")
        metadata = manager.get_metadata()
        print(f"   ✓ Métadonnées récupérées:")
        print(f"   - Version: {metadata.get('version')}")
        print(f"   - Dernière modification: {metadata.get('last_modified')}")
        
        # Test 7: Informations sur une entrée
        print("\n7. Test get_entry_info:")
        info = manager.get_entry_info("Dolibarr*")
        if info:
            print(f"   ✓ Informations pour 'Dolibarr*':")
            print(f"   - Ajouté le: {info.get('added_at')}")
            print(f"   - Ajouté par: {info.get('added_by')}")
        
        # Test 8: Statistiques
        print("\n8. Test des statistiques:")
        stats = manager.get_statistics()
        print(f"   ✓ Statistiques:")
        print(f"   - Total entrées: {stats['total_entries']}")
        print(f"   - Avec wildcards: {stats['entries_with_wildcards']}")
        print(f"   - Exactes: {stats['entries_exact']}")
        
        # Test 9: Export/Import
        print("\n9. Test export/import:")
        export_path = os.path.join(test_dir, "whitelist_export.json")
        manager.export_whitelist(export_path)
        print(f"   ✓ Exporté vers: {export_path}")
        
        # Vider et réimporter
        manager.clear_whitelist()
        print(f"   ✓ Liste blanche vidée (taille={len(manager.whitelist)})")
        
        manager.import_whitelist(export_path, admin_confirmed=True)
        print(f"   ✓ Réimporté (taille={len(manager.whitelist)})")
        
        # Test 10: Patterns avec wildcards
        print("\n10. Test patterns avec wildcards:")
        manager.clear_whitelist()
        manager.add_to_whitelist("Fire*fox", admin_confirmed=True)
        
        test_wildcard_cases = [
            ("Firefox", True),
            ("Firefoxes", True),
            ("Fire123fox", True),
            ("Chrome", False)
        ]
        
        for window, expected in test_wildcard_cases:
            allowed = manager.is_window_allowed(window)
            status = "✓" if allowed == expected else "✗"
            result = "Autorisé" if allowed else "Bloqué"
            print(f"   {status} {result}: {window}")
        
        print("\n✓ Tous les tests réussis!")
        
    finally:
        # Nettoyer les fichiers de test
        shutil.rmtree(test_dir)
        print(f"\n✓ Fichiers de test nettoyés")
