"""
Classe de base pour les GUI thématiques
Fournit les fonctionnalités communes à tous les thèmes
"""

import tkinter as tk
from tkinter import messagebox
from abc import ABC, abstractmethod
from typing import List, Optional

from .data_manager import get_data_manager
from .scan_controller import ScanController


class BaseWindow(ABC):
    """
    Classe de base abstraite pour tous les GUI thématiques.
    Définit l'interface commune et les méthodes partagées.
    """
    
    def __init__(self, title: str = "Web Sentinel"):
        # Fenêtre principale
        self.root = tk.Tk()
        self.root.title(title)
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
        
        # Gestionnaires
        self.data_manager = get_data_manager()
        self.scan_controller = ScanController()
        
        # Configuration des callbacks du contrôleur
        self.scan_controller.on_scan_start = self.on_scan_start
        self.scan_controller.on_scan_progress = self.on_scan_progress
        self.scan_controller.on_scan_complete = self.on_scan_complete
        self.scan_controller.on_scan_error = self.on_scan_error
        
        # État de l'UI
        self.is_scanning = False
        
        # Restaurer la géométrie de la fenêtre
        saved_geometry = self.data_manager.get_window_geometry()
        if saved_geometry:
            try:
                self.root.geometry(saved_geometry)
            except:
                self.root.geometry("1200x800")
        else:
            self.root.geometry("1200x800")
        
        self.root.minsize(1000, 600)
    
    # ===== MÉTHODES ABSTRAITES (à implémenter par les sous-classes) =====
    
    @abstractmethod
    def setup_ui(self):
        """Créer l'interface utilisateur du thème."""
        pass
    
    @abstractmethod
    def update_status(self, message: str):
        """Mettre à jour la barre de statut."""
        pass
    
    @abstractmethod
    def display_results(self, findings: List):
        """Afficher les résultats du scan."""
        pass
    
    @abstractmethod
    def clear_results(self):
        """Effacer les résultats affichés."""
        pass
    
    # ===== MÉTHODES COMMUNES =====
    
    def run(self):
        """Lance la boucle principale de l'interface."""
        self.setup_ui()
        self.root.mainloop()
    
    def on_closing(self):
        """Appelé à la fermeture de la fenêtre."""
        # Sauvegarder la géométrie
        try:
            geometry = self.root.geometry()
            self.data_manager.set_window_geometry(geometry)
        except:
            pass
        
        # Fermer la fenêtre
        self.root.destroy()
    
    # ===== CALLBACKS DU SCAN =====
    
    def on_scan_start(self):
        """Appelé au démarrage du scan."""
        self.is_scanning = True
        self.clear_results()
        self.update_status("🔍 Scan en cours...")
    
    def on_scan_progress(self, message: str):
        """Appelé pendant le scan pour afficher la progression."""
        self.update_status(message)
    
    def on_scan_complete(self, findings: List):
        """Appelé à la fin du scan."""
        self.is_scanning = False
        self.display_results(findings)
        
        # Mettre à jour la date du dernier scan
        from datetime import datetime
        self.data_manager.set_last_scan_date(datetime.now().isoformat())
        
        # Message de statut
        count = len(findings)
        if count == 0:
            self.update_status("✅ Scan terminé - Aucune vulnérabilité détectée")
        else:
            self.update_status(f"✅ Scan terminé - {count} vulnérabilités détectées")
    
    def on_scan_error(self, error: str):
        """Appelé en cas d'erreur pendant le scan."""
        self.is_scanning = False
        self.update_status(f"❌ Erreur: {error}")
        messagebox.showerror("Erreur de scan", error)
    
    # ===== ACTIONS UTILISATEUR =====
    
    def start_scan(self):
        """Lance un scan avec les paramètres actuels."""
        # Récupérer les paramètres
        domains = self.get_domains_from_ui()
        if not domains:
            messagebox.showwarning(
                "Domaines manquants",
                "Veuillez entrer au moins un domaine à scanner."
            )
            return
        
        modules = self.data_manager.get_enabled_modules()
        allow_invasive = self.data_manager.get_allow_invasive()
        timeout = self.data_manager.get_timeout()
        use_history = self.data_manager.get_history_enabled()
        
        # Sauvegarder les domaines
        self.data_manager.set_domains(domains)
        
        # Lancer le scan
        self.scan_controller.start_scan(
            domains=domains,
            modules=modules,
            allow_invasive=allow_invasive,
            timeout=timeout,
            use_history=use_history
        )
    
    @abstractmethod
    def get_domains_from_ui(self) -> List[str]:
        """Récupère la liste des domaines depuis l'UI."""
        pass
    
    def export_results(self, format: str = "json"):
        """Exporte les résultats du dernier scan."""
        if not self.scan_controller.scan_results:
            messagebox.showwarning(
                "Aucun résultat",
                "Aucun résultat à exporter. Effectuez d'abord un scan."
            )
            return
        
        from tkinter import filedialog
        
        # Demander le chemin du fichier
        if format == "json":
            filepath = filedialog.asksaveasfilename(
                defaultextension=".json",
                filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
            )
        else:  # html
            filepath = filedialog.asksaveasfilename(
                defaultextension=".html",
                filetypes=[("HTML files", "*.html"), ("All files", "*.*")]
            )
        
        if filepath:
            try:
                self.scan_controller.export_results(filepath, format)
                messagebox.showinfo(
                    "Export réussi",
                    f"Résultats exportés vers:\n{filepath}"
                )
            except Exception as e:
                messagebox.showerror(
                    "Erreur d'export",
                    f"Impossible d'exporter les résultats:\n{str(e)}"
                )
    
    def show_about(self):
        """Affiche la boîte de dialogue À propos."""
        messagebox.showinfo(
            "À propos de Web Sentinel",
            "Web Sentinel v3.0\n\n"
            "Outil de diagnostic de sécurité web\n\n"
            "© 2025 Taaazzz-prog\n"
            "https://web-sentinel.taaazzz-prog.fr"
        )
    
    def change_theme(self):
        """Permet de changer de thème GUI."""
        import tkinter.simpledialog as simpledialog
        
        current_theme = self.data_manager.get_gui_theme()
        
        # Créer une fenêtre de sélection personnalisée
        dialog = tk.Toplevel(self.root)
        dialog.title("Changer de thème")
        dialog.geometry("400x300")
        dialog.transient(self.root)
        dialog.grab_set()
        
        # Centrer la fenêtre
        dialog.update_idletasks()
        x = (dialog.winfo_screenwidth() // 2) - (400 // 2)
        y = (dialog.winfo_screenheight() // 2) - (300 // 2)
        dialog.geometry(f"400x300+{x}+{y}")
        
        # Variable pour stocker le choix
        selected_theme = tk.StringVar(value=current_theme)
        
        # Titre
        tk.Label(
            dialog,
            text="Sélectionnez un thème",
            font=("Segoe UI", 14, "bold")
        ).pack(pady=20)
        
        # Options de thèmes
        themes = [
            ("classic", "🔵 Classic - Interface originale avec onglets"),
            ("dark", "🌑 Dark - Design moderne sombre et élégant"),
            ("cyberpunk", "⚡ Cyberpunk - Esthétique futuriste avec néons"),
            ("minimal", "☀️ Minimal - Design épuré et minimaliste")
        ]
        
        for value, text in themes:
            rb = tk.Radiobutton(
                dialog,
                text=text,
                variable=selected_theme,
                value=value,
                font=("Segoe UI", 10),
                anchor=tk.W
            )
            rb.pack(pady=5, padx=30, fill=tk.X)
        
        # Boutons
        btn_frame = tk.Frame(dialog)
        btn_frame.pack(side=tk.BOTTOM, pady=20)
        
        def apply_and_close():
            new_theme = selected_theme.get()
            if new_theme != current_theme:
                self.data_manager.set_gui_theme(new_theme)
                
                # Proposer de redémarrer
                restart = messagebox.askyesno(
                    "Thème modifié",
                    f"Le thème '{new_theme}' a été enregistré.\n\n"
                    "Voulez-vous redémarrer l'application maintenant\n"
                    "pour appliquer le nouveau thème ?"
                )
                
                if restart:
                    dialog.destroy()
                    self.root.destroy()
                    # Relancer l'application
                    import sys
                    import os
                    if getattr(sys, 'frozen', False):
                        # Exécutable
                        os.execv(sys.executable, [sys.executable])
                    else:
                        # Python script
                        os.execv(sys.executable, ['python'] + sys.argv)
            dialog.destroy()
        
        def cancel():
            dialog.destroy()
        
        tk.Button(
            btn_frame,
            text="Appliquer",
            command=apply_and_close,
            width=12,
            font=("Segoe UI", 10)
        ).pack(side=tk.LEFT, padx=5)
        
        tk.Button(
            btn_frame,
            text="Annuler",
            command=cancel,
            width=12,
            font=("Segoe UI", 10)
        ).pack(side=tk.LEFT, padx=5)
        
        # Attendre la fermeture de la fenêtre
        dialog.wait_window()
