#!/usr/bin/env python3
"""
🎛️ Panneau d'administration Web Sentinel

Interface graphique permettant :
  - la consultation et la mise à jour de la licence embarquée,
  - la gestion des comptes utilisateurs stockés dans la base locale,
  - l'affichage de statistiques d'utilisation (abonnements & historique de scans).
"""

from __future__ import annotations

import os
import platform
import sys
import tempfile
import tkinter as tk
from datetime import datetime, timezone, timedelta
from pathlib import Path
from tkinter import filedialog, messagebox, simpledialog, ttk
from typing import Dict, Optional

sys.path.insert(0, str(Path(__file__).parent.parent))

from web_sentinel.subscription.models import SubscriptionTier
from web_sentinel.gui.license_dialog import LicenceOperationError, show_license_status_dialog
from web_sentinel.license.license_manager import LicenceError
from web_sentinel.license.crypto_manager import CryptoManager
from web_sentinel.license.license_generator import LicenseGenerator, licence_to_json

from real_app_connector import RealAppConnector
from real_db_connector import RealDatabaseConnector
from usage_metrics import compute_history_metrics


class AdminPanel:
    """Interface graphique principale."""

    def __init__(self) -> None:
        self.root = tk.Tk()
        self.root.title("Web Sentinel • Panneau d'administration")
        self.root.geometry("1200x780")
        self.root.minsize(980, 640)

        # Connecteurs
        self.app_connector = RealAppConnector()
        self.db_connector = RealDatabaseConnector()
        self.tier_profiles = {
            "FREE": {"users": 1, "domains": 3, "features": {}},
            "PRO": {
                "users": 5,
                "domains": -1,
                "features": {"invasive_tests": True, "html_export": True},
            },
            "ENTERPRISE": {
                "users": 50,
                "domains": -1,
                "features": {
                    "invasive_tests": True,
                    "html_export": True,
                    "api_access": True,
                    "multi_user": True,
                },
            },
            "GOVERNMENT": {
                "users": 200,
                "domains": -1,
                "features": {
                    "invasive_tests": True,
                    "html_export": True,
                    "api_access": True,
                    "multi_user": True,
                    "air_gap": True,
                },
            },
            "FINANCE": {
                "users": 100,
                "domains": -1,
                "features": {
                    "invasive_tests": True,
                    "html_export": True,
                    "api_access": True,
                    "multi_user": True,
                    "air_gap": True,
                },
            },
            "MEDICAL": {
                "users": 100,
                "domains": -1,
                "features": {
                    "invasive_tests": True,
                    "html_export": True,
                    "api_access": True,
                    "multi_user": True,
                    "air_gap": True,
                },
            },
        }

        default_key_dir = Path(
            os.getenv(
                "WEB_SENTINEL_LICENSE_KEY_DIR",
                str(Path(__file__).parent / "licenses"),
            )
        )
        self.crypto_manager = CryptoManager(default_key_dir)
        self.crypto_manager.ensure_keys()
        self.default_private_key = self.crypto_manager.get_private_key_path()

        # État sélection
        self.selected_user: Optional[str] = None

        # Elements UI initialisés dans les méthodes build
        self.license_labels: Dict[str, ttk.Label] = {}
        self.license_feature_vars: Dict[str, ttk.Label] = {}
        self.license_tests_tree: ttk.Treeview

        self.users_tree: ttk.Treeview
        self.stats_labels: Dict[str, ttk.Label] = {}
        self.tier_stats_tree: ttk.Treeview
        self.history_labels: Dict[str, ttk.Label] = {}

        # Logs
        self.log_widget: tk.Text
        self.status_label: ttk.Label

        self._build_ui()
        self.refresh_all()
        self._schedule_refresh()

    # ------------------------------------------------------------------ #
    # Construction UI
    # ------------------------------------------------------------------ #

    def _build_ui(self) -> None:
        notebook = ttk.Notebook(self.root)
        notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=(10, 0))

        license_tab = ttk.Frame(notebook)
        users_tab = ttk.Frame(notebook)
        stats_tab = ttk.Frame(notebook)

        notebook.add(license_tab, text="🔐 Licence")
        notebook.add(users_tab, text="👥 Utilisateurs")
        notebook.add(stats_tab, text="📊 Statistiques")

        self._build_license_tab(license_tab)
        self._build_users_tab(users_tab)
        self._build_stats_tab(stats_tab)
        self._build_footer()

    def _build_license_tab(self, parent: ttk.Frame) -> None:
        info_frame = ttk.LabelFrame(parent, text="Statut de la licence", padding=15)
        info_frame.pack(fill=tk.X, padx=10, pady=(10, 5))

        fields = [
            ("🎯 Tier", "tier"),
            ("🆔 Identifiant", "license_id"),
            ("📅 Expiration", "valid_until"),
            ("⏳ Jours restants", "expires_in_days"),
            ("⏲️ Période de grâce (h)", "grace_hours"),
            ("🔍 Backend", "backend"),
            ("📄 Chemin du fichier", "path"),
        ]

        for row, (label, key) in enumerate(fields):
            ttk.Label(info_frame, text=label + ":", width=22, anchor="w").grid(row=row, column=0, sticky="w", pady=3)
            value_label = ttk.Label(info_frame, text="—", anchor="w")
            value_label.grid(row=row, column=1, sticky="w", pady=3)
            self.license_labels[key] = value_label

        feature_frame = ttk.LabelFrame(parent, text="Fonctionnalités actives", padding=15)
        feature_frame.pack(fill=tk.X, padx=10, pady=5)

        feature_map = [
            ("Tests invasifs", "invasive_tests"),
            ("Export HTML", "html_export"),
            ("API REST", "api_access"),
            ("Multi-utilisateurs", "multi_user"),
        ]
        for row, (label, key) in enumerate(feature_map):
            ttk.Label(feature_frame, text="• " + label, width=22, anchor="w").grid(row=row, column=0, sticky="w", pady=3)
            value_label = ttk.Label(feature_frame, text="—", anchor="w")
            value_label.grid(row=row, column=1, sticky="w", pady=3)
            self.license_feature_vars[key] = value_label

        actions_frame = ttk.Frame(parent)
        actions_frame.pack(fill=tk.X, padx=10, pady=(5, 0))

        ttk.Button(actions_frame, text="📂 Importer une licence…", command=self._import_license).pack(side=tk.LEFT)
        ttk.Button(actions_frame, text="🛠️ Générer une licence", command=self._open_license_generator).pack(side=tk.LEFT, padx=8)
        ttk.Button(actions_frame, text="🔁 Actualiser", command=self.refresh_license_tab).pack(side=tk.LEFT, padx=8)
        ttk.Button(actions_frame, text="🧪 Tester les permissions", command=self._run_license_tests).pack(
            side=tk.LEFT, padx=8
        )
        ttk.Button(actions_frame, text="ℹ️ Détails…", command=self._show_license_modal).pack(side=tk.LEFT, padx=8)

        ttk.Button(actions_frame, text="📁 Ouvrir le dossier licence", command=self._open_license_directory).pack(
            side=tk.RIGHT
        )

        tests_frame = ttk.LabelFrame(parent, text="Résultats des tests de permissions", padding=15)
        tests_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=(5, 10))

        columns = ("feature", "allowed", "details")
        self.license_tests_tree = ttk.Treeview(tests_frame, columns=columns, show="headings", height=8)
        self.license_tests_tree.heading("feature", text="Fonctionnalité")
        self.license_tests_tree.heading("allowed", text="Autorisé")
        self.license_tests_tree.heading("details", text="Détails")
        self.license_tests_tree.column("feature", width=180, anchor="w")
        self.license_tests_tree.column("allowed", width=120, anchor="center")
        self.license_tests_tree.column("details", width=460, anchor="w")
        self.license_tests_tree.pack(fill=tk.BOTH, expand=True)

    def _build_users_tab(self, parent: ttk.Frame) -> None:
        # === Section Outils ===
        tools_frame = ttk.LabelFrame(parent, text="🛠️ Outils de gestion", padding=15)
        tools_frame.pack(fill=tk.X, padx=10, pady=(10, 0))
        
        # Ligne 1 : Actions principales
        main_actions = ttk.Frame(tools_frame)
        main_actions.pack(fill=tk.X, pady=(0, 10))
        
        ttk.Button(main_actions, text="🔄 Actualiser la liste", command=self.refresh_users_tab).pack(side=tk.LEFT)
        ttk.Button(main_actions, text="➕ Nouvel utilisateur", command=self._show_create_user_dialog).pack(side=tk.LEFT, padx=(10, 0))
        
        # Ligne 2 : Gestion utilisateur sélectionné
        user_actions = ttk.LabelFrame(tools_frame, text="👤 Utilisateur sélectionné", padding=10)
        user_actions.pack(fill=tk.X, pady=(10, 0))
        
        # Affichage utilisateur sélectionné
        self.selected_user_label = ttk.Label(user_actions, text="Aucun utilisateur sélectionné", foreground="gray")
        self.selected_user_label.pack(anchor="w", pady=(0, 10))
        
        # Actions sur utilisateur sélectionné
        user_controls = ttk.Frame(user_actions)
        user_controls.pack(fill=tk.X)
        
        # Section changement de tier avec boutons visuels
        tier_frame = ttk.Frame(user_controls)
        tier_frame.pack(side=tk.LEFT, fill=tk.X, expand=True)
        
        ttk.Label(tier_frame, text="🎯 Changer le tier :").pack(anchor="w")
        tier_buttons = ttk.Frame(tier_frame)
        tier_buttons.pack(anchor="w", pady=(5, 0))
        
        # Boutons pour chaque tier avec couleurs visuelles
        tier_styles = {
            "FREE": ("� Gratuit", "#28a745"),
            "PRO": ("⭐ Professionnel", "#007bff"), 
            "ENTERPRISE": ("🏢 Entreprise", "#6610f2")
        }
        
        for tier, (label, color) in tier_styles.items():
            btn = ttk.Button(tier_buttons, text=label, 
                           command=lambda t=tier: self._set_user_tier_quick(t))
            btn.pack(side=tk.LEFT, padx=(0, 5))
        
        # Section expiration
        expiry_frame = ttk.Frame(user_controls)
        expiry_frame.pack(side=tk.LEFT, padx=(20, 0))
        
        ttk.Label(expiry_frame, text="⏱️ Expiration :").pack(anchor="w")
        expiry_buttons = ttk.Frame(expiry_frame)
        expiry_buttons.pack(anchor="w", pady=(5, 0))
        
        # Boutons d'expiration rapides
        expiry_options = [
            ("30 jours", 30),
            ("90 jours", 90),
            ("1 an", 365),
            ("Personnalisé", None)
        ]
        
        for label, days in expiry_options:
            btn = ttk.Button(expiry_buttons, text=label,
                           command=lambda d=days: self._set_user_expiry_quick(d))
            btn.pack(side=tk.LEFT, padx=(0, 5))

        # === Section Liste des utilisateurs ===
        list_frame = ttk.LabelFrame(parent, text="👥 Liste des utilisateurs", padding=10)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=(10, 10))

        self.users_tree = ttk.Treeview(
            list_frame,
            columns=("email", "tier", "expires", "created", "updated"),
            show="headings",
            height=16,
        )
        self.users_tree.heading("email", text="📧 Email")
        self.users_tree.heading("tier", text="🎯 Tier")
        self.users_tree.heading("expires", text="⏰ Expire le")
        self.users_tree.heading("created", text="📅 Créé le")
        self.users_tree.heading("updated", text="🔄 Mis à jour le")
        self.users_tree.column("email", width=260, anchor="w")
        self.users_tree.column("tier", width=120, anchor="center")
        self.users_tree.column("expires", width=160, anchor="center")
        self.users_tree.column("created", width=160, anchor="center")
        self.users_tree.column("updated", width=160, anchor="center")

        tree_scroll = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.users_tree.yview)
        self.users_tree.configure(yscrollcommand=tree_scroll.set)

        self.users_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        tree_scroll.pack(side=tk.RIGHT, fill=tk.Y)
        self.users_tree.bind("<<TreeviewSelect>>", self._on_user_select)

    def _build_stats_tab(self, parent: ttk.Frame) -> None:
        overview = ttk.LabelFrame(parent, text="Vue d'ensemble", padding=15)
        overview.pack(fill=tk.X, padx=10, pady=(10, 5))

        overview_fields = [
            ("Utilisateurs totaux", "total_users"),
            ("Licences expirées", "expired"),
            ("Expires ≤ 7 jours", "expiring_7_days"),
        ]
        for idx, (label, key) in enumerate(overview_fields):
            lbl = ttk.Label(overview, text=label + ":", width=22, anchor="w")
            lbl.grid(row=idx, column=0, sticky="w", pady=3)
            value = ttk.Label(overview, text="0", font=("Arial", 12, "bold"))
            value.grid(row=idx, column=1, sticky="w", pady=3)
            self.stats_labels[key] = value

        tier_frame = ttk.LabelFrame(parent, text="Répartition par tier", padding=15)
        tier_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

        self.tier_stats_tree = ttk.Treeview(
            tier_frame,
            columns=("tier", "count", "active", "soon", "expired", "next"),
            show="headings",
            height=8,
        )
        headers = [
            ("tier", "Tier"),
            ("count", "Utilisateurs"),
            ("active", "Actifs"),
            ("soon", "Expire ≤ 7 j"),
            ("expired", "Expirés"),
            ("next", "Prochaine expiration"),
        ]
        for name, label in headers:
            self.tier_stats_tree.heading(name, text=label)
            width = 180 if name == "next" else 120
            self.tier_stats_tree.column(name, width=width, anchor="center")
        self.tier_stats_tree.column("tier", width=140, anchor="center")
        self.tier_stats_tree.pack(fill=tk.BOTH, expand=True)

        history_frame = ttk.LabelFrame(parent, text="Historique des scans", padding=15)
        history_frame.pack(fill=tk.X, padx=10, pady=(5, 10))

        history_fields = [
            ("Fichier", "history_path"),
            ("Domaines suivis", "total_domains"),
            ("Scans enregistrés", "total_scans"),
            ("Findings détectés", "total_findings"),
            ("Moy. findings / scan", "average_findings_per_scan"),
            ("Dernier scan", "last_scan_at"),
        ]
        for idx, (label, key) in enumerate(history_fields):
            ttk.Label(history_frame, text=label + ":", width=22, anchor="w").grid(row=idx, column=0, sticky="w", pady=2)
            value = ttk.Label(history_frame, text="—", anchor="w")
            value.grid(row=idx, column=1, sticky="w", pady=2)
            self.history_labels[key] = value

    def _build_footer(self) -> None:
        footer = ttk.Frame(self.root)
        footer.pack(fill=tk.X, padx=10, pady=(0, 10))

        self.status_label = ttk.Label(footer, text="📌 Prêt.")
        self.status_label.pack(side=tk.LEFT)

        self.log_widget = tk.Text(footer, height=4, wrap=tk.WORD)
        self.log_widget.pack(side=tk.RIGHT, fill=tk.X, expand=True, padx=(10, 0))
        self.log_widget.configure(state=tk.DISABLED)

    # ------------------------------------------------------------------ #
    # Rafraîchissement
    # ------------------------------------------------------------------ #

    def refresh_all(self) -> None:
        """Mettre à jour l'ensemble de l'interface."""
        self.refresh_license_tab()
        self.refresh_users_tab()
        self.refresh_stats_tab()
        self._set_status(f"Dernière mise à jour : {datetime.now().strftime('%H:%M:%S')}")

    def refresh_license_tab(self) -> None:
        status = self.app_connector.get_license_status()
        self.license_labels["tier"].config(text=status.get("tier", "—"))
        self.license_labels["license_id"].config(text=status.get("license_id") or "—")
        valid_until = status.get("valid_until") or "—"
        self.license_labels["valid_until"].config(text=valid_until)

        expires_in = status.get("expires_in_days")
        expires_text = "—"
        if expires_in is not None:
            try:
                expires_text = f"{int(expires_in)} jour(s)"
            except (TypeError, ValueError):
                expires_text = str(expires_in)
        self.license_labels["expires_in_days"].config(text=expires_text)
        self.license_labels["grace_hours"].config(text=str(status.get("grace_hours", "—")))
        backend = status.get("backend", {})
        backend_text = backend.get("name", "none")
        if backend.get("verified"):
            backend_text = f"{backend_text} ✅"
        elif backend.get("name") and backend.get("name") != "none":
            backend_text = f"{backend_text} ⚠️"
        self.license_labels["backend"].config(text=backend_text)
        self.license_labels["path"].config(text=str(self.app_connector.get_license_file_path()))

        features = status.get("features_enabled", {})
        for key, widget in self.license_feature_vars.items():
            widget.config(text="✅" if features.get(key, False) else "❌")

        # vider les tests
        for row in self.license_tests_tree.get_children():
            self.license_tests_tree.delete(row)

    def refresh_users_tab(self) -> None:
        for row in self.users_tree.get_children():
            self.users_tree.delete(row)

        for record in self.db_connector.list_users():
            expires = record.expires_at or "—"
            created = record.created_at or "—"
            updated = record.updated_at or "—"
            self.users_tree.insert(
                "",
                tk.END,
                values=(record.email, record.tier, expires, created, updated),
            )
        self.selected_user = None

    def refresh_stats_tab(self) -> None:
        summary = self.db_connector.compute_subscription_stats()
        self.stats_labels["total_users"].config(text=str(summary.get("total_users", 0)))
        self.stats_labels["expired"].config(text=str(summary.get("expired", 0)))
        self.stats_labels["expiring_7_days"].config(text=str(summary.get("expiring_7_days", 0)))

        for row in self.tier_stats_tree.get_children():
            self.tier_stats_tree.delete(row)
        for tier, data in summary.get("tiers", {}).items():
            self.tier_stats_tree.insert(
                "",
                tk.END,
                values=(
                    tier,
                    data.get("count", 0),
                    data.get("active", 0),
                    data.get("expires_soon", 0),
                    data.get("expired", 0),
                    data.get("next_expiration") or "—",
                ),
            )

        history = compute_history_metrics()
        for key, widget in self.history_labels.items():
            value = history.get(key)
            if key == "average_findings_per_scan" and isinstance(value, float):
                widget.config(text=f"{value:.2f}")
            else:
                widget.config(text=str(value) if value not in (None, "") else "—")

    # ------------------------------------------------------------------ #
    # Actions Licence
    # ------------------------------------------------------------------ #

    def _import_license(self) -> None:
        file_path = filedialog.askopenfilename(
            title="Sélectionner un fichier de licence",
            filetypes=[("Licence Web Sentinel", "*.lic"), ("Tous les fichiers", "*.*")],
        )
        if not file_path:
            return
        try:
            self.app_connector.install_license(Path(file_path))
            self.log(f"Licence importée: {file_path}")
            self.refresh_license_tab()
        except LicenceError as exc:
            messagebox.showerror("Erreur licence", str(exc), parent=self.root)
            self.log(f"Erreur import licence: {exc}")

    def _open_license_generator(self) -> None:
        dialog = tk.Toplevel(self.root)
        dialog.title("Générer une licence")
        dialog.resizable(False, False)
        dialog.grab_set()

        ttk.Label(dialog, text="Email super admin:").grid(row=0, column=0, sticky="w", padx=10, pady=5)
        email_entry = ttk.Entry(dialog, width=40)
        email_entry.grid(row=0, column=1, padx=10, pady=5)

        ttk.Label(dialog, text="Tier:").grid(row=1, column=0, sticky="w", padx=10, pady=5)
        tier_combo = ttk.Combobox(dialog, values=[
            "FREE",
            "PRO",
            "ENTERPRISE",
            "GOVERNMENT",
            "FINANCE",
            "MEDICAL",
        ], state="readonly", width=20)
        tier_combo.set("FREE")
        tier_combo.grid(row=1, column=1, padx=10, pady=5)

        ttk.Label(dialog, text="Durée (jours):").grid(row=2, column=0, sticky="w", padx=10, pady=5)
        duration_entry = ttk.Entry(dialog, width=10)
        duration_entry.insert(0, "365")
        duration_entry.grid(row=2, column=1, sticky="w", padx=10, pady=5)

        ttk.Label(dialog, text="Max utilisateurs:").grid(row=3, column=0, sticky="w", padx=10, pady=5)
        users_entry = ttk.Entry(dialog, width=10)
        users_entry.insert(0, "1")
        users_entry.grid(row=3, column=1, sticky="w", padx=10, pady=5)

        ttk.Label(dialog, text="Max domaines (-1 = illimité):").grid(row=4, column=0, sticky="w", padx=10, pady=5)
        domains_entry = ttk.Entry(dialog, width=10)
        domains_entry.insert(0, "3")
        domains_entry.grid(row=4, column=1, sticky="w", padx=10, pady=5)

        features_frame = ttk.LabelFrame(dialog, text="Fonctionnalités", padding=10)
        features_frame.grid(row=5, column=0, columnspan=2, padx=10, pady=5, sticky="ew")

        feature_vars = {
            "invasive_tests": tk.BooleanVar(value=False),
            "html_export": tk.BooleanVar(value=False),
            "api_access": tk.BooleanVar(value=False),
            "multi_user": tk.BooleanVar(value=False),
            "air_gap": tk.BooleanVar(value=False),
        }

        grid_pos = [
            ("Tests invasifs", "invasive_tests"),
            ("Export HTML", "html_export"),
            ("API REST", "api_access"),
            ("Multi-utilisateurs", "multi_user"),
            ("Mode air gap", "air_gap"),
        ]
        for idx, (label, key) in enumerate(grid_pos):
            ttk.Checkbutton(features_frame, text=label, variable=feature_vars[key]).grid(
                row=idx // 2, column=idx % 2, padx=6, pady=4, sticky="w"
            )

        ttk.Label(dialog, text="Chemin clé privée:").grid(row=6, column=0, sticky="w", padx=10, pady=5)
        key_entry = ttk.Entry(dialog, width=40)
        key_entry.insert(0, str(self.default_private_key))
        key_entry.grid(row=6, column=1, padx=10, pady=5)

        def browse_key() -> None:
            path = filedialog.askopenfilename(
                parent=dialog,
                title="Sélectionner la clé privée",
                filetypes=[("PEM", "*.pem"), ("Tous les fichiers", "*.*")],
            )
            if path:
                key_entry.delete(0, tk.END)
                key_entry.insert(0, path)

        ttk.Button(dialog, text="Parcourir", command=browse_key).grid(row=6, column=2, padx=5, pady=5)

        button_frame = ttk.Frame(dialog)
        button_frame.grid(row=7, column=0, columnspan=3, pady=(10, 15))

        def generate_and_install() -> None:
            email = email_entry.get().strip()
            tier = tier_combo.get().strip().upper()
            if not email:
                messagebox.showwarning("Attention", "Email requis", parent=dialog)
                return
            try:
                duration = int(duration_entry.get())
                max_users = int(users_entry.get())
                max_domains = int(domains_entry.get())
            except ValueError:
                messagebox.showwarning("Attention", "Valeurs numériques invalides", parent=dialog)
                return

            key_path = Path(key_entry.get()).expanduser()
            if not key_path.exists():
                messagebox.showerror("Erreur", "Clé privée introuvable", parent=dialog)
                return

            features = {name: var.get() for name, var in feature_vars.items()}
            valid_until = datetime.now(timezone.utc) + timedelta(days=duration)
            licence_id = f"WS-{tier}-{datetime.now():%Y%m%d%H%M%S}"

            config = LicenseGenerator.default_config(
                licence_id=licence_id,
                tier=tier,
                valid_until=valid_until,
                max_users=max_users,
                max_domains=max_domains,
                features=features,
                super_admin_email=email,
            )

            save_path = filedialog.asksaveasfilename(
                parent=dialog,
                title="Enregistrer la licence",
                defaultextension=".lic",
                initialfile=f"{licence_id}.lic",
                filetypes=[("Licence", "*.lic"), ("Tous les fichiers", "*.*")],
            )
            if not save_path:
                return

            generator = LicenseGenerator(key_path)
            licence = generator.generate(config, Path(save_path))

            if messagebox.askyesno("Installer", "Installer immédiatement cette licence ?", parent=dialog):
                try:
                    self.app_connector.install_license(Path(save_path))
                except LicenceError as exc:
                    messagebox.showerror("Erreur", str(exc), parent=dialog)
                    return

            self.log(f"Licence générée: {save_path}")
            self.log(licence_to_json(licence)[:200] + "…")
            self.refresh_license_tab()
            dialog.destroy()

        def apply_defaults(*_args) -> None:
            selected = tier_combo.get().upper()
            defaults = self._profile_for_tier(selected)
            users_entry.delete(0, tk.END)
            users_entry.insert(0, str(defaults["users"]))
            domains_entry.delete(0, tk.END)
            domains_entry.insert(0, str(defaults["domains"]))
            for key, var in feature_vars.items():
                var.set(defaults["features"].get(key, False))

        tier_combo.bind("<<ComboboxSelected>>", apply_defaults)
        apply_defaults()

        ttk.Button(button_frame, text="Générer", command=generate_and_install).pack(side=tk.LEFT, padx=6)
        ttk.Button(button_frame, text="Annuler", command=dialog.destroy).pack(side=tk.LEFT, padx=6)

    def _run_license_tests(self) -> None:
        results = self.app_connector.run_permission_tests()
        for row in self.license_tests_tree.get_children():
            self.license_tests_tree.delete(row)
        for item in results["tests"]:
            allowed = "✅" if item["allowed"] else "❌"
            details = item.get("details") or ""
            self.license_tests_tree.insert(
                "",
                tk.END,
                values=(item["feature"], allowed, details),
            )
        caps = results.get("capabilities", {})
        summary = (
            f"tier={caps.get('tier')} | domain_limit={caps.get('domain_limit')} | "
            f"invasive={caps.get('can_use_invasive_tests')} | html={caps.get('can_export_html')} | "
            f"api={caps.get('can_use_api')}"
        )
        self.log("Tests de permissions exécutés.")
        self.log(summary)

    def _show_license_modal(self) -> None:
        def installer(path: Path) -> bool:
            try:
                return self.app_connector.install_license(path)
            except LicenceError as exc:
                raise LicenceOperationError(str(exc)) from exc

        show_license_status_dialog(self.root, self.app_connector.get_license_status, installer)
        self.refresh_license_tab()

    def _open_license_directory(self) -> None:
        directory = self.app_connector.get_license_file_path().parent
        try:
            if platform.system() == "Windows":
                os.startfile(directory)  # type: ignore[attr-defined]
            elif platform.system() == "Darwin":
                os.system(f"open '{directory}'")
            else:
                os.system(f"xdg-open '{directory}'")
        except Exception as exc:  # noqa: BLE001
            messagebox.showerror("Erreur", f"Impossible d'ouvrir le dossier : {exc}", parent=self.root)

    # ------------------------------------------------------------------ #
    # Actions utilisateurs
    # ------------------------------------------------------------------ #

    def _on_user_select(self, _event) -> None:
        selection = self.users_tree.selection()
        if not selection:
            self.selected_user = None
            if hasattr(self, 'selected_user_label'):
                self.selected_user_label.config(text="Aucun utilisateur sélectionné", foreground="gray")
            return
        values = self.users_tree.item(selection[0])["values"]
        self.selected_user = values[0]
        if hasattr(self, 'selected_user_label'):
            self.selected_user_label.config(text=f"👤 {self.selected_user} (Tier: {values[1]})", foreground="black")

    def _prompt_create_user(self) -> None:
        email = simpledialog.askstring("Nouvel utilisateur", "Adresse email :", parent=self.root)
        if not email:
            return
        record = self.db_connector.create_test_user(email)
        self.log(f"Utilisateur créé ou existant : {record.email}")
        self._generate_license_for_user(record.email, record.tier, record.expires_at)
        self.refresh_users_tab()

    def _prompt_update_tier(self) -> None:
        if not self.selected_user:
            messagebox.showinfo("Information", "Sélectionnez un utilisateur.", parent=self.root)
            return
        tier_value = simpledialog.askstring(
            "Modifier le tier",
            "Nouveau tier (free, pro, enterprise) :",
            parent=self.root,
        )
        if not tier_value:
            return
        try:
            tier = SubscriptionTier(tier_value.lower())
        except ValueError:
            messagebox.showerror("Erreur", "Tier invalide.", parent=self.root)
            return
        success = self.db_connector.set_user_tier(self.selected_user, tier)
        if success:
            self.log(f"Tier mis à jour pour {self.selected_user} → {tier.value.upper()}")
            record = self.db_connector.get_user(self.selected_user)
            expires = record.expires_at if record else None
            self._generate_license_for_user(self.selected_user, tier.value.upper(), expires)
            self.refresh_users_tab()
        else:
            messagebox.showerror("Erreur", "Impossible de modifier le tier.", parent=self.root)

    def _prompt_update_expiry(self) -> None:
        if not self.selected_user:
            messagebox.showinfo("Information", "Sélectionnez un utilisateur.", parent=self.root)
            return
        value = simpledialog.askinteger(
            "Définir expiration",
            "Nombre de jours avant expiration (0 pour expirer immédiatement) :",
            parent=self.root,
        )
        if value is None:
            return
        success = self.db_connector.set_user_expiry(self.selected_user, value)
        if success:
            self.log(f"Expiration mise à jour pour {self.selected_user} ({value} jours)")
            record = self.db_connector.get_user(self.selected_user)
            tier = record.tier if record else "FREE"
            expires = record.expires_at if record else None
            self._generate_license_for_user(self.selected_user, tier, expires)
            self.refresh_users_tab()
        else:
            messagebox.showerror("Erreur", "Impossible de mettre à jour l'expiration.", parent=self.root)

    # ------------------------------------------------------------------ #
    # Nouvelles méthodes interface simplifiée
    # ------------------------------------------------------------------ #

    def _show_create_user_dialog(self) -> None:
        """Dialogue moderne pour créer un utilisateur."""
        dialog = tk.Toplevel(self.root)
        dialog.title("➕ Créer un nouvel utilisateur")
        dialog.resizable(False, False)
        dialog.grab_set()
        
        # Centrer la fenêtre
        dialog.geometry("500x350")
        
        main_frame = ttk.Frame(dialog, padding=20)
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # En-tête
        header = ttk.Label(main_frame, text="👤 Création d'un nouvel utilisateur", 
                          font=("Arial", 14, "bold"))
        header.pack(pady=(0, 20))
        
        # Email
        ttk.Label(main_frame, text="📧 Adresse email :").pack(anchor="w", pady=(0, 5))
        email_entry = ttk.Entry(main_frame, width=50, font=("Arial", 11))
        email_entry.pack(fill=tk.X, pady=(0, 15))
        email_entry.focus()
        
        # Tier
        ttk.Label(main_frame, text="🎯 Type d'abonnement :").pack(anchor="w", pady=(0, 5))
        tier_frame = ttk.Frame(main_frame)
        tier_frame.pack(fill=tk.X, pady=(0, 15))
        
        tier_var = tk.StringVar(value="FREE")
        tier_options = [
            ("🆓 Gratuit (FREE)", "FREE"),
            ("⭐ Professionnel (PRO)", "PRO"),
            ("🏢 Entreprise (ENTERPRISE)", "ENTERPRISE")
        ]
        
        for text, value in tier_options:
            rb = ttk.Radiobutton(tier_frame, text=text, variable=tier_var, value=value)
            rb.pack(anchor="w", pady=2)
        
        # Durée
        ttk.Label(main_frame, text="⏱️ Durée de validité :").pack(anchor="w", pady=(0, 5))
        duration_frame = ttk.Frame(main_frame)
        duration_frame.pack(fill=tk.X, pady=(0, 20))
        
        duration_var = tk.StringVar(value="90")
        duration_options = [
            ("30 jours", "30"),
            ("90 jours", "90"),
            ("1 an (365 jours)", "365"),
            ("Illimité", "0")
        ]
        
        for text, value in duration_options:
            rb = ttk.Radiobutton(duration_frame, text=text, variable=duration_var, value=value)
            rb.pack(side=tk.LEFT, padx=(0, 15))
        
        # Boutons
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, pady=(10, 0))
        
        def create_user():
            email = email_entry.get().strip()
            if not email:
                messagebox.showwarning("Attention", "Veuillez saisir une adresse email.", parent=dialog)
                return
                
            try:
                tier = SubscriptionTier(tier_var.get().lower())
                duration = int(duration_var.get())
                
                # Créer l'utilisateur
                record = self.db_connector.create_test_user(email)
                
                # Définir le tier
                self.db_connector.set_user_tier(email, tier)
                
                # Définir l'expiration si nécessaire
                if duration > 0:
                    self.db_connector.set_user_expiry(email, duration)
                
                self.log(f"✅ Utilisateur créé : {email} ({tier.value.upper()}, {duration} jours)")
                self.refresh_users_tab()
                dialog.destroy()
                
            except Exception as e:
                messagebox.showerror("Erreur", f"Impossible de créer l'utilisateur :\n{e}", parent=dialog)
        
        ttk.Button(button_frame, text="✅ Créer l'utilisateur", command=create_user).pack(side=tk.RIGHT)
        ttk.Button(button_frame, text="❌ Annuler", command=dialog.destroy).pack(side=tk.RIGHT, padx=(0, 10))
        
        # Permettre la validation avec Entrée
        dialog.bind('<Return>', lambda e: create_user())

    def _set_user_tier_quick(self, tier_name: str) -> None:
        """Changer rapidement le tier d'un utilisateur sélectionné."""
        if not self.selected_user:
            messagebox.showinfo("Information", "👤 Veuillez sélectionner un utilisateur dans la liste.", parent=self.root)
            return
            
        try:
            tier = SubscriptionTier(tier_name.lower())
            success = self.db_connector.set_user_tier(self.selected_user, tier)
            
            if success:
                self.log(f"🎯 Tier mis à jour : {self.selected_user} → {tier.value.upper()}")
                self.refresh_users_tab()
                # Mettre à jour le label de sélection
                if hasattr(self, 'selected_user_label'):
                    self.selected_user_label.config(text=f"👤 {self.selected_user} (Tier: {tier.value.upper()})")
            else:
                messagebox.showerror("Erreur", "❌ Impossible de modifier le tier.", parent=self.root)
                
        except ValueError:
            messagebox.showerror("Erreur", f"❌ Tier invalide : {tier_name}", parent=self.root)

    def _set_user_expiry_quick(self, days: Optional[int]) -> None:
        """Définir rapidement l'expiration d'un utilisateur."""
        if not self.selected_user:
            messagebox.showinfo("Information", "👤 Veuillez sélectionner un utilisateur dans la liste.", parent=self.root)
            return
            
        if days is None:
            # Mode personnalisé
            days = simpledialog.askinteger(
                "⏱️ Expiration personnalisée",
                "Nombre de jours avant expiration (0 = illimité) :",
                parent=self.root,
                minvalue=0,
                maxvalue=3650
            )
            if days is None:
                return
        
        success = self.db_connector.set_user_expiry(self.selected_user, days)
        
        if success:
            if days == 0:
                self.log(f"⏱️ Expiration mise à jour : {self.selected_user} → Illimité")
            else:
                self.log(f"⏱️ Expiration mise à jour : {self.selected_user} → {days} jours")
            self.refresh_users_tab()
        else:
            messagebox.showerror("Erreur", "❌ Impossible de mettre à jour l'expiration.", parent=self.root)

    # ------------------------------------------------------------------ #
    # Outils
    # ------------------------------------------------------------------ #

    def log(self, message: str) -> None:
        timestamp = datetime.now().strftime("%H:%M:%S")
        self.log_widget.configure(state=tk.NORMAL)
        self.log_widget.insert(tk.END, f"[{timestamp}] {message}\n")
        self.log_widget.see(tk.END)
        self.log_widget.configure(state=tk.DISABLED)

    def _set_status(self, message: str) -> None:
        self.status_label.config(text=f"📌 {message}")

    def _profile_for_tier(self, tier: str) -> Dict[str, object]:
        return self.tier_profiles.get(tier.upper(), self.tier_profiles["FREE"])

    def _generate_license_for_user(self, email: str, tier: str, expires_at: Optional[str] = None) -> None:
        profile = self._profile_for_tier(tier)
        features = {
            key: bool(profile["features"].get(key, False))
            for key in ["invasive_tests", "html_export", "api_access", "multi_user", "air_gap"]
        }
        max_users = int(profile["users"])
        max_domains = int(profile["domains"])

        if expires_at:
            try:
                valid_until = datetime.fromisoformat(str(expires_at).replace("Z", "+00:00"))
            except ValueError:
                valid_until = datetime.now(timezone.utc) + timedelta(days=365)
        else:
            valid_until = datetime.now(timezone.utc) + timedelta(days=365)

        try:
            private_key = self.crypto_manager.get_private_key_path()
            generator = LicenseGenerator(private_key)
        except Exception as exc:  # noqa: BLE001
            messagebox.showerror("Erreur", f"Clé privée indisponible: {exc}", parent=self.root)
            self.log(f"Erreur clé privée: {exc}")
            return

        licence_id = f"WS-{tier}-{datetime.now():%Y%m%d%H%M%S}"
        config = LicenseGenerator.default_config(
            licence_id=licence_id,
            tier=tier,
            valid_until=valid_until,
            max_users=max_users,
            max_domains=max_domains,
            features=features,
            super_admin_email=email,
        )

        with tempfile.NamedTemporaryFile(delete=False, suffix=".lic") as tmp:
            tmp_path = Path(tmp.name)

        try:
            generator.generate(config, tmp_path)
            self.app_connector.install_license(tmp_path)
            self.log(f"Licence régénérée et installée pour {email} ({tier})")
        except LicenceError as exc:
            messagebox.showerror("Erreur licence", str(exc), parent=self.root)
            self.log(f"Erreur installation licence: {exc}")
        except Exception as exc:  # noqa: BLE001
            messagebox.showerror("Erreur", str(exc), parent=self.root)
            self.log(f"Erreur génération licence: {exc}")
        finally:
            try:
                tmp_path.unlink()
            except OSError:
                pass
        self.refresh_license_tab()

    def _schedule_refresh(self) -> None:
        # Rafraîchissement automatique toutes les 2 minutes
        self.root.after(120_000, self._on_auto_refresh)

    def _on_auto_refresh(self) -> None:
        self.refresh_all()
        self._schedule_refresh()

    # ------------------------------------------------------------------ #
    # Boucle principale
    # ------------------------------------------------------------------ #

    def run(self) -> None:
        self.root.mainloop()


def main() -> None:
    app = AdminPanel()
    app.run()


if __name__ == "__main__":
    main()
