"""
Interface graphique moderne pour le client desktop MyMind avec systray
Utilise CustomTkinter pour une interface moderne avec coins arrondis et thème sombre
"""
import customtkinter as ctk
import tkinter as tk
from tkinter import messagebox
import threading
import sys
import os
import time
from pathlib import Path
from typing import Optional, Callable
from PIL import Image, ImageTk
import pystray
from pystray import MenuItem as item

# Configuration de CustomTkinter
ctk.set_appearance_mode("dark")  # Mode sombre
ctk.set_default_color_theme("blue")  # Thème bleu

class MyMindGUI:
    """Interface graphique moderne pour le client MyMind avec systray"""
    
    def __init__(self, config, api_client, shortcut_handler, on_close: Optional[Callable] = None, updater=None):
        self.config = config
        self.api_client = api_client
        self.shortcut_handler = shortcut_handler
        self.on_close = on_close
        self.updater = updater
        
        # Systray
        self.tray_icon = None
        self.tray_thread = None
        self.minimized_to_tray = False
        
        # Fenêtre principale avec CustomTkinter
        self.root = ctk.CTk()
        self.root.title("")  # Titre vide pour une interface épurée
        self.root.geometry("380x480")  # Taille réduite pour une interface plus compacte
        self.root.resizable(False, False)
        
        # Charger l'icône
        self.setup_icon()
        
        # Variables
        self.clients = []
        self.groups = []
        self.selected_client_id = tk.StringVar(value=str(config.get('client_id', '')) if config.get('client_id') else '')
        self.selected_group_id = tk.StringVar(value=str(config.get('group_id', '')) if config.get('group_id') else '')
        self.enabled_var = tk.BooleanVar(value=config.get('enabled', True))
        self.status_var = tk.StringVar(value="Chargement...")
        self.shortcuts_count_var = tk.StringVar(value="0")
        self.server_status_var = tk.StringVar(value="Vérification...")
        self.update_status_var = tk.StringVar(value="Vérification...")
        # Authentification
        self.auth_email_var = tk.StringVar(value=config.get('auth_email', '') or '')
        # Par sécurité, on ne pré-remplit pas le mot de passe dans le champ
        self.auth_password_var = tk.StringVar(value='')
        if self.config.get('auth_email'):
            self.auth_status_var = tk.StringVar(value="Connecté")
            self.is_connected = True
        else:
            self.auth_status_var = tk.StringVar(value="Non connecté")
            self.is_connected = False
        
        # Variables pour les mises à jour
        self.update_info = None
        
        # Définir les couleurs pour les indicateurs (avant setup_ui)
        self.success_color = "#10b981"  # Vert
        self.error_color = "#ef4444"  # Rouge
        
        self.setup_ui()
        self.setup_tray()
        self.load_data()
        
        # Gérer la fermeture/minimisation
        self.root.protocol("WM_DELETE_WINDOW", self.on_window_close)
        
        # Actualiser périodiquement
        self.refresh_interval = config.get('reload_interval', 30) * 1000
        self.schedule_refresh()
        
        # Vérifier les mises à jour périodiquement (pour les mises à jour forcées)
        self.update_check_interval = config.get('update_check_interval', 300) * 1000  # 5 minutes par défaut
        self.schedule_update_check()
        
        # Si un utilisateur est déjà configuré, passer en mode connecté
        if self.config.get('auth_email'):
            email = self.config.get('auth_email')
            self.user_label_var.set(email)
            self._switch_to_connected_ui()
            # Lancer la vérification des mises à jour après connexion
            if self.updater:
                self.root.after(1000, self.check_updates_ui)
        else:
            # Pas connecté : vérifier les mises à jour quand même (pour info)
            if self.updater:
                self.root.after(2000, self.check_updates_ui)
    
    def setup_styles(self):
        """CustomTkinter gère automatiquement les styles et le thème sombre"""
        # Cette méthode est conservée pour compatibilité mais les couleurs sont définies dans __init__
        pass
    
    def setup_icon(self):
        """Configure l'icône de la fenêtre"""
        # Chercher le logo.png dans plusieurs emplacements (mode dev et portable)
        current_file = Path(__file__)
        
        # Si on est dans portable/app/, le logo est dans le même dossier
        if current_file.parent.name == 'app':
            logo_paths = [
                current_file.parent / "logo.png",
                current_file.parent.parent / "images" / "logo.png",
            ]
        else:
            # Mode développement - chercher dans plusieurs emplacements
            base_dir = current_file.parent
            logo_paths = [
                base_dir / "logo.png",
                base_dir / "images" / "logo.png",
                base_dir.parent / "templates" / "logo.png",
                base_dir.parent / "chrome-extension" / "logo.png",
            ]
        
        for logo_path in logo_paths:
            if logo_path.exists():
                try:
                    img = Image.open(logo_path)
                    # Redimensionner si nécessaire pour l'icône de fenêtre (Windows préfère 32x32 ou 48x48)
                    if img.size[0] > 48 or img.size[1] > 48:
                        img = img.resize((48, 48), Image.Resampling.LANCZOS)
                    # Convertir en format tkinter
                    photo = ImageTk.PhotoImage(img)
                    self.root.iconphoto(False, photo)
                    self.window_icon_path = logo_path
                    return
                except Exception as e:
                    print(f"Erreur lors du chargement du logo {logo_path}: {e}")
                    continue
    
    def setup_tray(self):
        """Configure l'icône du systray"""
        # Chercher le logo.png pour le systray
        current_file = Path(__file__)
        
        # Si on est dans portable/app/, le logo est dans le même dossier
        if current_file.parent.name == 'app':
            logo_paths = [
                current_file.parent / "logo.png",
                current_file.parent.parent / "images" / "logo.png",
            ]
        else:
            # Mode développement - chercher dans plusieurs emplacements
            base_dir = current_file.parent
            logo_paths = [
                base_dir / "logo.png",
                base_dir / "images" / "logo.png",
                base_dir.parent / "templates" / "logo.png",
                base_dir.parent / "chrome-extension" / "logo.png",
            ]
        
        tray_icon_path = None
        for logo_path in logo_paths:
            if logo_path.exists():
                tray_icon_path = logo_path
                break
        
        if not tray_icon_path:
            # Créer une icône simple si aucune n'est trouvée
            tray_icon_path = self._create_simple_icon()
        
        try:
            # Créer le menu du systray
            menu = pystray.Menu(
                item('Ouvrir MyMind', self.show_window),
                item('Activer/Désactiver', self.toggle_enabled, 
                     checked=lambda item: self.enabled_var.get()),
                pystray.Menu.SEPARATOR,
                item('Quitter', self.quit_application)
            )
            
            # Charger l'image pour le systray et redimensionner si nécessaire
            image = Image.open(tray_icon_path)
            # Redimensionner pour le systray (généralement 16x16 ou 32x32)
            if image.size[0] > 32 or image.size[1] > 32:
                image = image.resize((32, 32), Image.Resampling.LANCZOS)
            self.tray_icon = pystray.Icon("MyMind", image, "MyMind", menu)
            
            # Démarrer le systray dans un thread séparé
            self.tray_thread = threading.Thread(target=self.tray_icon.run, daemon=True)
            self.tray_thread.start()
        except Exception as e:
            print(f"Erreur lors de la création du systray: {e}")
    
    def _create_simple_icon(self):
        """Crée une icône simple si aucune n'est disponible"""
        try:
            # Utiliser la couleur primaire de CustomTkinter
            primary_color = ctk.ThemeManager.theme["CTkButton"]["fg_color"][1]
            img = Image.new('RGB', (32, 32), color=primary_color)
            icon_path = Path(__file__).parent / "icon_temp.png"
            img.save(icon_path)
            return icon_path
        except:
            return None
    
    def setup_ui(self):
        """Configure l'interface utilisateur moderne avec CustomTkinter"""
        # Frame principal avec padding réduit
        self.main_frame = ctk.CTkFrame(self.root, fg_color="transparent")
        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=15, pady=15)
        
        # Header avec utilisateur connecté et bouton Dashboard
        header_frame = ctk.CTkFrame(self.main_frame, fg_color="transparent")
        header_frame.pack(fill=tk.X, pady=(0, 12))

        # Bouton Dashboard (à gauche) - créé mais caché si non connecté
        self.dashboard_btn = ctk.CTkButton(
            header_frame,
            text="Dashboard",
            command=self.open_dashboard,
            font=('Segoe UI', 11),
            width=100,
            height=32
        )
        if self.is_connected:
            self.dashboard_btn.pack(side=tk.LEFT)

        # Zone utilisateur (en haut à droite) - visible seulement si connecté
        self.user_header_frame = ctk.CTkFrame(header_frame, fg_color="transparent")
        if self.is_connected:
            self.user_header_frame.pack(side=tk.RIGHT, anchor='e')

        self.user_label_var = tk.StringVar(value="Non connecté")
        self.user_label = ctk.CTkLabel(
            self.user_header_frame,
            textvariable=self.user_label_var,
            font=('Segoe UI', 11)
        )
        self.user_label.pack(anchor='e')

        # Bouton de déconnexion discret sous le compte
        self.user_logout_btn = ctk.CTkButton(
            self.user_header_frame,
            text="Déconnexion",
            command=self.on_logout_click,
            font=('Segoe UI', 10),
            width=100,
            height=28,
            fg_color=("gray75", "gray25"),
            hover_color=("gray65", "gray35"),
            corner_radius=6
        )
        if self.is_connected:
            self.user_logout_btn.pack(anchor='e', pady=(2, 0))
        
        # Card: Authentification (visible seulement si non connecté)
        self.auth_card = ctk.CTkFrame(self.main_frame)
        if not self.is_connected:
            self.auth_card.pack(fill=tk.X, pady=(0, 10))

        self.auth_frame = ctk.CTkFrame(self.auth_card, fg_color="transparent")
        self.auth_frame.pack(fill=tk.X, padx=12, pady=10)

        subtitle_label = ctk.CTkLabel(self.auth_frame, text="Connexion au serveur MyMind", 
                                     font=('Segoe UI', 12))
        subtitle_label.pack(anchor=tk.W, pady=(0, 6))

        # Email
        self.auth_email_label = ctk.CTkLabel(self.auth_frame, text="Email :", font=('Segoe UI', 11))
        self.auth_email_label.pack(anchor=tk.W)
        self.auth_email_entry = ctk.CTkEntry(self.auth_frame, textvariable=self.auth_email_var, width=300, 
                                             font=('Segoe UI', 11))
        self.auth_email_entry.pack(fill=tk.X, pady=(0, 6))

        # Mot de passe
        self.auth_password_label = ctk.CTkLabel(self.auth_frame, text="Mot de passe :", font=('Segoe UI', 11))
        self.auth_password_label.pack(anchor=tk.W)
        self.auth_password_entry = ctk.CTkEntry(self.auth_frame, textvariable=self.auth_password_var, 
                                                show='*', width=300, font=('Segoe UI', 11))
        self.auth_password_entry.pack(fill=tk.X, pady=(0, 6))

        # Statut d'authentification
        auth_status_label = ctk.CTkLabel(self.auth_frame, textvariable=self.auth_status_var,
                                      font=('Segoe UI', 10))
        auth_status_label.pack(anchor=tk.W, pady=(0, 6))

        # Boutons de connexion
        auth_buttons = ctk.CTkFrame(self.auth_frame, fg_color="transparent")
        auth_buttons.pack(fill=tk.X)

        self.login_btn = ctk.CTkButton(auth_buttons, text="Se connecter",
                                   command=self.on_login_click,
                                   font=('Segoe UI', 11),
                                   width=120,
                                   height=36)
        self.login_btn.pack(side=tk.LEFT)
        
        # Card: Mises à jour (toujours visible)
        self.update_card = ctk.CTkFrame(self.main_frame)
        self.update_card.pack(fill=tk.X, pady=(0, 10))
        
        self.setup_update_section()

        # Card: Configuration (visible seulement si connecté)
        self.config_card = ctk.CTkFrame(self.main_frame)
        if self.is_connected:
            self.config_card.pack(fill=tk.X, pady=(0, 10))
        
        # Badge de statut MyMind (visible seulement si connecté)
        self.status_badge = ctk.CTkFrame(self.config_card, fg_color="transparent")
        if self.is_connected:
            self.status_badge.pack(fill=tk.X, pady=(0, 0))
        
        status_container = ctk.CTkFrame(self.status_badge, fg_color="transparent")
        status_container.pack(fill=tk.X, padx=12, pady=10)
        
        # Indicateur de statut (cercle coloré)
        self.status_indicator = tk.Canvas(status_container, width=12, height=12, 
                                         bg=ctk.ThemeManager.theme["CTkFrame"]["fg_color"][1], 
                                         highlightthickness=0)
        self.status_indicator.pack(side=tk.LEFT, padx=(0, 8))
        self.status_indicator.create_oval(2, 2, 10, 10, fill=self.error_color, outline="")
        
        status_info = ctk.CTkFrame(status_container, fg_color="transparent")
        status_info.pack(side=tk.LEFT, expand=True, fill=tk.X)
        
        status_label = ctk.CTkLabel(status_info, text="Statut:", font=('Segoe UI', 11))
        status_label.pack(side=tk.LEFT)
        
        self.status_text = ctk.CTkLabel(status_info, textvariable=self.status_var,
                                    font=('Segoe UI', 11, 'bold'))
        self.status_text.pack(side=tk.LEFT, padx=(5, 0))
        
        shortcuts_label = ctk.CTkLabel(status_info, text="• Raccourcis:",
                                   font=('Segoe UI', 11))
        shortcuts_label.pack(side=tk.LEFT, padx=(15, 0))
        
        self.shortcuts_count_label = ctk.CTkLabel(status_info, textvariable=self.shortcuts_count_var,
                                              font=('Segoe UI', 11, 'bold'))
        self.shortcuts_count_label.pack(side=tk.LEFT, padx=(5, 0))
        
        # Activer/Désactiver
        self.enable_frame = ctk.CTkFrame(self.config_card, fg_color="transparent")
        if self.is_connected:
            self.enable_frame.pack(fill=tk.X, padx=12, pady=10)
        
        enable_check = ctk.CTkCheckBox(self.enable_frame, text="Activer MyMind",
                                     variable=self.enabled_var,
                                     command=self.on_enable_toggle,
                                     font=('Segoe UI', 11))
        enable_check.pack(anchor=tk.W)
        
        # Séparateur
        self.separator1 = ctk.CTkFrame(self.config_card, height=1, fg_color=("gray70", "gray30"))
        if self.is_connected:
            self.separator1.pack(fill=tk.X, padx=12)
        
        # Client et Groupe dans une grille
        self.select_frame = ctk.CTkFrame(self.config_card, fg_color="transparent")
        if self.is_connected:
            self.select_frame.pack(fill=tk.X, padx=12, pady=10)
        
        ctk.CTkLabel(self.select_frame, text="Client:", font=('Segoe UI', 11)).pack(anchor=tk.W, pady=(0, 4))
        self.client_combo = ctk.CTkComboBox(self.select_frame,
                                        font=('Segoe UI', 11),
                                        width=300,
                                        state="readonly")
        self.client_combo.pack(fill=tk.X, pady=(0, 8))
        self.client_combo.configure(command=self.on_client_change)
        
        ctk.CTkLabel(self.select_frame, text="Groupe:", font=('Segoe UI', 11)).pack(anchor=tk.W, pady=(0, 4))
        self.group_combo = ctk.CTkComboBox(self.select_frame,
                                       font=('Segoe UI', 11),
                                       width=300,
                                       state="readonly")
        self.group_combo.pack(fill=tk.X)
        self.group_combo.configure(command=self.on_group_change)
        
        # Card: Actions (visible seulement si connecté)
        self.actions_card = ctk.CTkFrame(self.main_frame)
        if self.is_connected:
            self.actions_card.pack(fill=tk.X, pady=(0, 10))
        
        buttons_frame = ctk.CTkFrame(self.actions_card, fg_color="transparent")
        if self.is_connected:
            buttons_frame.pack(fill=tk.X, padx=12, pady=10)
        
        reload_btn = ctk.CTkButton(buttons_frame, text="🔄 Recharger",
                              command=self.reload_shortcuts,
                              font=('Segoe UI', 11),
                              width=100,
                              height=36)
        reload_btn.pack(side=tk.LEFT, padx=(0, 6))
        
        test_btn = ctk.CTkButton(buttons_frame, text="🔗 Tester",
                            command=self.test_connection,
                            font=('Segoe UI', 11),
                            width=80,
                            height=36,
                            fg_color=("gray75", "gray25"),
                            hover_color=("gray65", "gray35"))
        test_btn.pack(side=tk.LEFT, padx=(0, 6))
        
        quit_btn = ctk.CTkButton(buttons_frame, text="Quitter",
                            command=self.quit_completely,
                            font=('Segoe UI', 11),
                            width=80,
                            height=36,
                            fg_color=self.error_color,
                            hover_color='#dc2626')
        quit_btn.pack(side=tk.LEFT)
        
        # Info minimale (visible seulement si connecté)
        self.info_frame = ctk.CTkFrame(self.main_frame, fg_color="transparent")
        if self.is_connected:
            self.info_frame.pack(fill=tk.BOTH, expand=True)
        
        info_text = "💡 Tapez un mot-clé puis Espace/Entrée/Tab pour remplacer"
        self.info_label = ctk.CTkLabel(self.info_frame, text=info_text,
                              font=('Segoe UI', 10))
        if self.is_connected:
            self.info_label.pack(anchor=tk.W, pady=(3, 0))
    
    def setup_update_section(self):
        """Configure la section des mises à jour"""
        update_frame = ctk.CTkFrame(self.update_card, fg_color="transparent")
        update_frame.pack(fill=tk.X, padx=12, pady=10)
        
        update_title = ctk.CTkLabel(update_frame, text="Mises à jour", 
                                    font=('Segoe UI', 12))
        update_title.pack(anchor=tk.W, pady=(0, 6))
        
        # Frame pour le contenu des mises à jour
        self.update_content_frame = ctk.CTkFrame(update_frame, fg_color="transparent")
        self.update_content_frame.pack(fill=tk.X)
        
        # Afficher le statut initial
        self.update_status_label = ctk.CTkLabel(self.update_content_frame, 
                                             textvariable=self.update_status_var,
                                             font=('Segoe UI', 11))
        self.update_status_label.pack(anchor=tk.W)
        
        # Frame pour les détails de mise à jour (caché par défaut)
        self.update_details_frame = ctk.CTkFrame(update_frame, fg_color="transparent")
    
    def open_dashboard(self):
        """Ouvre le portail Dashboard dans le navigateur"""
        try:
            import webbrowser
            # Récupérer l'URL du serveur depuis la config ou l'API client
            api_url = self.config.get('api_url') or self.api_client.find_working_url()
            if api_url:
                # Extraire l'URL de base (sans /api)
                dashboard_url = api_url.split('/api')[0] if '/api' in api_url else api_url
                webbrowser.open(dashboard_url)
            else:
                # URL par défaut si aucune URL n'est trouvée
                webbrowser.open("http://mymind.myndkor.fr:5005")
        except Exception as e:
            messagebox.showerror("Erreur", f"Impossible d'ouvrir le Dashboard: {e}")
    
    def update_status_indicator(self, enabled: bool):
        """Met à jour l'indicateur de statut"""
        self.status_indicator.delete("all")
        color = self.success_color if enabled else self.error_color
        self.status_indicator.create_oval(2, 2, 10, 10, fill=color, outline="")
    
    def load_data(self):
        """Charge les données (clients, groupes, raccourcis)"""
        threading.Thread(target=self._load_data_thread, daemon=True).start()
    
    def _load_data_thread(self):
        """Charge les données dans un thread séparé"""
        try:
            # Charger les clients
            self.clients = self.api_client.get_clients()
            client_names = ['Tous les clients'] + [c['name'] for c in self.clients]
            
            self.root.after(0, lambda: self._update_client_combo(client_names))
            
            # Charger les groupes si un client est sélectionné
            client_id = self.config.get('client_id')
            if client_id:
                self.groups = self.api_client.get_groups(client_id)
                group_names = ['Tous les groupes'] + [g['name'] for g in self.groups]
                
                self.root.after(0, lambda: self._update_group_combo(group_names))
            
            # Recharger les raccourcis
            self.reload_shortcuts()
            
        except Exception as e:
            self.root.after(0, lambda: messagebox.showerror("Erreur", 
                f"Erreur lors du chargement des données: {e}"))
    
    def _update_client_combo(self, names):
        """Met à jour la combobox des clients"""
        # `names` contient déjà "Tous les clients" + noms réels
        self.client_combo.configure(values=names)
        
        current_id = self.config.get('client_id')
        if current_id:
            # Trouver le client correspondant à l'ID pour sélectionner son nom
            for idx, client in enumerate(self.clients, start=1):  # offset 1 pour "Tous les clients"
                if client.get('id') == current_id:
                    try:
                        self.client_combo.set(names[idx])
                    except Exception:
                        pass
                    break
    
    def _update_group_combo(self, names):
        """Met à jour la combobox des groupes"""
        # `names` contient déjà "Tous les groupes" + noms réels
        self.group_combo.configure(values=names)
        
        current_id = self.config.get('group_id')
        if current_id:
            for idx, group in enumerate(self.groups, start=1):  # offset 1 pour "Tous les groupes"
                if group.get('id') == current_id:
                    try:
                        self.group_combo.set(names[idx])
                    except Exception:
                        pass
                    break
    
    def on_client_change(self, selection=None):
        """Gère le changement de client"""
        if selection is None:
            selection = self.client_combo.get()
        if not selection or selection == "Tous les clients":
            client_id = None
        else:
            # Retrouver l'ID à partir du nom sélectionné
            client_id = None
            for client in self.clients:
                if client.get('name') == selection:
                    client_id = client.get('id')
                    break
        
        self.config.set('client_id', client_id)
        self.selected_client_id.set(str(client_id) if client_id else '')
        
        if client_id:
            threading.Thread(target=self._load_groups_thread, args=(client_id,), daemon=True).start()
        else:
            self.group_combo['values'] = ['Tous les groupes']
            self.group_combo.current(0)
            self.config.set('group_id', None)
        
        self.reload_shortcuts()
    
    def _load_groups_thread(self, client_id):
        """Charge les groupes dans un thread"""
        try:
            self.groups = self.api_client.get_groups(client_id)
            group_names = ['Tous les groupes'] + [g['name'] for g in self.groups]
            
            self.root.after(0, lambda: self._update_group_combo(group_names))
        except Exception as e:
            print(f"Erreur lors du chargement des groupes: {e}")
    
    def on_group_change(self, selection=None):
        """Gère le changement de groupe"""
        if selection is None:
            selection = self.group_combo.get()
        if not selection or selection == "Tous les groupes":
            group_id = None
        else:
            # Retrouver l'ID à partir du nom sélectionné
            group_id = None
            for group in self.groups:
                if group.get('name') == selection:
                    group_id = group.get('id')
                    break
        
        self.config.set('group_id', group_id)
        self.selected_group_id.set(str(group_id) if group_id else '')
        self.reload_shortcuts()
    
    def on_enable_toggle(self):
        """Gère l'activation/désactivation"""
        enabled = self.enabled_var.get()
        self.shortcut_handler.set_enabled(enabled)
        self.update_status()
        self.update_tray_menu()
    
    def reload_shortcuts(self):
        """Recharge les raccourcis"""
        threading.Thread(target=self._reload_shortcuts_thread, daemon=True).start()
    
    def _reload_shortcuts_thread(self):
        """Recharge les raccourcis dans un thread"""
        try:
            self.shortcut_handler.reload_shortcuts()
            count = len(self.shortcut_handler.shortcuts_cache)
            self.root.after(0, lambda: self.shortcuts_count_var.set(str(count)))
            self.root.after(0, self.update_status)
        except Exception as e:
            self.root.after(0, lambda: messagebox.showerror("Erreur", 
                f"Erreur lors du rechargement: {e}"))
    
    def test_connection(self):
        """Teste la connexion à l'API"""
        threading.Thread(target=self._test_connection_thread, daemon=True).start()
    
    def _test_connection_thread(self):
        """Teste la connexion dans un thread"""
        try:
            url = self.api_client.find_working_url()
            if url:
                self.root.after(0, lambda: messagebox.showinfo("Succès", 
                    f"Connexion réussie à :\n{url}"))
            else:
                self.root.after(0, lambda: messagebox.showerror("Erreur", 
                    "Impossible de se connecter à l'API"))
        except Exception as e:
            self.root.after(0, lambda: messagebox.showerror("Erreur", 
                f"Erreur de connexion: {e}"))
    
    def _hide_login_fields(self):
        """Masque les champs email/mot de passe et le bouton de connexion"""
        self.auth_card.pack_forget()

    def _show_login_fields(self):
        """Affiche les champs email/mot de passe et le bouton de connexion"""
        self.auth_card.pack(fill=tk.X, pady=(0, 10), before=self.update_card)

    def on_login_click(self):
        """Lance la tentative de connexion avec les identifiants saisis"""
        threading.Thread(target=self._login_thread, daemon=True).start()

    def _login_thread(self):
        """Effectue la connexion dans un thread séparé"""
        email = (self.auth_email_var.get() or "").strip()
        password = self.auth_password_var.get() or ""

        if not email or not password:
            self.root.after(0, lambda: messagebox.showwarning(
                "Identifiants manquants",
                "Merci de renseigner l'email et le mot de passe."
            ))
            return

        self.root.after(0, lambda: self.auth_status_var.set("Connexion en cours..."))
        success = self.api_client.login(email, password)

        if success:
            # Sauvegarder dans la config
            self.config.set('auth_email', email)
            self.config.set('auth_password', password)
            self.is_connected = True
            
            self.root.after(0, lambda: self.auth_status_var.set("Connecté"))
            self.root.after(0, lambda: self.user_label_var.set(email))
            
            # Changer l'interface après connexion
            self.root.after(0, self._switch_to_connected_ui)
            
            # Lancer la vérification des mises à jour après connexion
            if self.updater:
                self.root.after(500, self.check_updates_ui)
            
            # Recharger les données (clients, groupes, raccourcis)
            self.root.after(0, self.load_data)
        else:
            self.root.after(0, lambda: self.auth_status_var.set("Échec de la connexion"))
            self.root.after(0, lambda: messagebox.showerror(
                "Connexion échouée",
                "Impossible de se connecter.\nVérifiez l'email, le mot de passe et l'accès au serveur."
            ))
    
    def _switch_to_connected_ui(self):
        """Change l'interface pour afficher le mode connecté"""
        # Masquer les champs de connexion
        self._hide_login_fields()
        
        # Afficher le bouton Dashboard
        if hasattr(self, 'dashboard_btn'):
            self.dashboard_btn.pack(side=tk.LEFT)
        
        # Afficher l'utilisateur en haut à droite
        self.user_header_frame.pack(side=tk.RIGHT, anchor='e')
        self.user_logout_btn.pack(anchor='e', pady=(2, 0))
        
        # Afficher la configuration
        self.config_card.pack(fill=tk.X, pady=(0, 10), after=self.update_card)
        
        # S'assurer que tous les éléments de config_card sont affichés dans le bon ordre
        # Utiliser pack_forget puis pack pour forcer l'affichage dans le bon ordre
        
        # 1. Status badge
        self.status_badge.pack_forget()
        self.status_badge.pack(fill=tk.X, pady=(0, 0))
        
        # 2. Enable frame
        self.enable_frame.pack_forget()
        self.enable_frame.pack(fill=tk.X, padx=12, pady=10)
        
        # 3. Separator
        self.separator1.pack_forget()
        self.separator1.pack(fill=tk.X, padx=12)
        
        # 4. Select frame (client/groupe) - IMPORTANT : doit être affiché
        self.select_frame.pack_forget()
        self.select_frame.pack(fill=tk.X, padx=12, pady=10)
        
        # Afficher les actions
        self.actions_card.pack(fill=tk.X, pady=(0, 10))
        
        # Afficher l'info
        self.info_frame.pack(fill=tk.BOTH, expand=True)
        self.info_label.pack(anchor=tk.W, pady=(5, 0))
        
        # Mettre à jour le statut
        self.update_status()
        
        # Recharger les données pour remplir les comboboxes (après un court délai pour s'assurer que l'UI est prête)
        self.root.after(100, self.load_data)

    def on_logout_click(self):
        """Déconnecte localement (efface les identifiants stockés)"""
        # Effacer les identifiants en config
        self.config.set('auth_email', '')
        self.config.set('auth_password', '')
        self.auth_email_var.set('')
        self.auth_password_var.set('')
        self.auth_status_var.set("Non connecté")
        self.user_label_var.set("Non connecté")
        self.is_connected = False
        
        # Changer l'interface pour le mode non connecté
        self._switch_to_disconnected_ui()
    
    def _switch_to_disconnected_ui(self):
        """Change l'interface pour afficher le mode non connecté"""
        # Cacher l'utilisateur en haut à droite
        self.user_header_frame.pack_forget()
        self.user_logout_btn.pack_forget()
        
        # Cacher la configuration
        self.config_card.pack_forget()
        self.status_badge.pack_forget()
        self.actions_card.pack_forget()
        self.info_frame.pack_forget()
        
        # Réafficher les champs de connexion
        self._show_login_fields()
    
    def update_status(self):
        """Met à jour le statut affiché"""
        enabled = self.enabled_var.get()
        if enabled:
            self.status_var.set("Actif")
        else:
            self.status_var.set("Inactif")
        self.update_status_indicator(enabled)
    
    def schedule_refresh(self):
        """Planifie le rafraîchissement automatique"""
        self.reload_shortcuts()
        self.root.after(self.refresh_interval, self.schedule_refresh)
    
    def schedule_update_check(self):
        """Planifie la vérification périodique des mises à jour"""
        if self.updater and self.is_connected:
            self.check_updates_ui()
        # Planifier la prochaine vérification
        self.root.after(self.update_check_interval, self.schedule_update_check)
    
    def show_window(self, icon=None, item=None):
        """Affiche la fenêtre depuis le systray"""
        self.root.after(0, self._show_window)
    
    def _show_window(self):
        """Affiche la fenêtre (méthode thread-safe)"""
        self.root.deiconify()
        self.root.lift()
        self.root.focus_force()
        self.minimized_to_tray = False
    
    def toggle_enabled(self, icon=None, item=None):
        """Bascule l'état activé/désactivé depuis le systray"""
        self.root.after(0, lambda: self.enabled_var.set(not self.enabled_var.get()))
        self.root.after(0, self.on_enable_toggle)
    
    def update_tray_menu(self):
        """Met à jour le menu du systray"""
        if self.tray_icon:
            menu = pystray.Menu(
                item('Ouvrir MyMind', self.show_window),
                item('Activer/Désactiver', self.toggle_enabled,
                     checked=lambda item: self.enabled_var.get()),
                pystray.Menu.SEPARATOR,
                item('Quitter', self.quit_application)
            )
            self.tray_icon.menu = menu
    
    def quit_application(self, icon=None, item=None):
        """Quitte complètement l'application depuis le systray"""
        self.root.after(0, self.quit_completely)
    
    def quit_completely(self):
        """Ferme complètement l'application"""
        # Arrêter le gestionnaire de raccourcis
        if self.on_close:
            self.on_close()
        # Arrêter le systray
        if self.tray_icon:
            self.tray_icon.stop()
        # Fermer la fenêtre
        self.root.quit()
        self.root.destroy()
    
    def on_window_close(self):
        """Gère la fermeture de la fenêtre (peut minimiser dans le systray)"""
        if self.config.get('minimize_to_tray', True):
            # Minimiser dans le systray
            self.root.withdraw()
            self.minimized_to_tray = True
            if self.tray_icon:
                self.tray_icon.visible = True
        else:
            # Fermer complètement si minimize_to_tray est désactivé
            self.quit_completely()
    
    def check_updates_ui(self, force_update: bool = False):
        """Vérifie les mises à jour et met à jour l'interface"""
        if not self.updater:
            self.update_status_var.set("Système de mise à jour non disponible")
            return
        
        def check_thread():
            self.root.after(0, lambda: self.update_status_var.set("Vérification des mises à jour..."))
            update_info = self.updater.check_for_updates()
            if update_info:
                self.update_info = update_info
                
                # Vérifier si c'est une mise à jour récente (pas une nouvelle mise à jour disponible)
                if update_info.get('recently_updated', False):
                    updated_at = update_info.get('updated_at', '')
                    self.root.after(0, lambda: self._display_recently_updated(updated_at))
                elif update_info.get('available', False):
                    mandatory = update_info.get('mandatory', False)
                    force_on_mandatory = self.config.get('force_update_on_mandatory', True)
                    
                    # Si mise à jour obligatoire et force_update activé, lancer automatiquement
                    if mandatory and force_on_mandatory:
                        self.root.after(0, lambda: self._handle_mandatory_update(update_info))
                    else:
                        self.root.after(0, lambda: self._display_update_available(update_info))
            else:
                self.root.after(0, lambda: self.update_status_var.set("Aucune mise à jour disponible"))
                # Cacher les détails de mise à jour
                self.update_details_frame.pack_forget()
        
        threading.Thread(target=check_thread, daemon=True).start()
    
    def _handle_mandatory_update(self, update_info):
        """Gère une mise à jour obligatoire - lance automatiquement"""
        from tkinter import messagebox
        
        changelog = update_info.get('changelog', 'Aucune information disponible')
        message = f"⚠️ MISE À JOUR OBLIGATOIRE ⚠️\n\n"
        message += f"Version actuelle: {update_info['current_version']}\n"
        message += f"Nouvelle version: {update_info['new_version']}\n\n"
        message += f"Changelog:\n{changelog}\n\n"
        message += f"La mise à jour va commencer automatiquement..."
        
        # Afficher un message d'information (non bloquant)
        self.root.after(0, lambda: messagebox.showinfo("Mise à jour obligatoire", message))
        
        # Mettre à jour l'affichage dans l'interface
        self._display_update_available(update_info)
        
        # Lancer la mise à jour automatiquement après un court délai
        self.root.after(3000, lambda: self.apply_update(update_info))
    
    def _display_recently_updated(self, updated_at: str = ''):
        """Affiche un message indiquant que l'application a été récemment mise à jour"""
        if updated_at:
            # Formater la date pour l'affichage
            try:
                from datetime import datetime
                dt = datetime.strptime(updated_at, '%Y-%m-%d %H:%M:%S')
                formatted_date = dt.strftime('%d/%m/%Y à %H:%M')
                self.update_status_var.set(f"✓ MyMind a été mis à jour le {formatted_date}")
            except:
                self.update_status_var.set(f"✓ MyMind a été mis à jour")
        else:
            self.update_status_var.set("✓ MyMind a été mis à jour")
        
        # Cacher les détails de mise à jour
        self.update_details_frame.pack_forget()
    
    def _display_update_available(self, update_info):
        """Affiche les détails de la mise à jour disponible"""
        mandatory = update_info.get('mandatory', False)
        
        if mandatory:
            self.update_status_var.set(f"⚠️ Mise à jour OBLIGATOIRE: version {update_info['new_version']}")
        else:
            self.update_status_var.set(f"Mise à jour disponible: version {update_info['new_version']}")
        
        # Nettoyer le frame des détails
        for widget in self.update_details_frame.winfo_children():
            widget.destroy()
        
        # Afficher les détails
        self.update_details_frame.pack(fill=tk.X, pady=(10, 0))
        
        # Informations de version
        version_info = ctk.CTkFrame(self.update_details_frame, fg_color="transparent")
        version_info.pack(fill=tk.X, pady=(0, 8))
        
        ctk.CTkLabel(version_info, text=f"Version actuelle: {update_info['current_version']}", 
                 font=('Segoe UI', 11)).pack(anchor=tk.W)
        
        version_label_text = f"Nouvelle version: {update_info['new_version']}"
        if mandatory:
            version_label_text += " (OBLIGATOIRE)"
        version_label = ctk.CTkLabel(version_info, text=version_label_text, 
                 font=('Segoe UI', 11, 'bold'),
                 text_color=self.error_color if mandatory else None)
        version_label.pack(anchor=tk.W)
        
        # Changelog
        if update_info.get('changelog'):
            changelog_label = ctk.CTkLabel(self.update_details_frame, 
                                       text=f"Changelog: {update_info['changelog']}",
                                       font=('Segoe UI', 10),
                                       wraplength=400)
            changelog_label.pack(anchor=tk.W, pady=(0, 8))
        
        # Bouton de mise à jour
        btn_text = "Mettre à jour maintenant (OBLIGATOIRE)" if mandatory else "Mettre à jour maintenant"
        btn_color = self.error_color if mandatory else None
        update_btn = ctk.CTkButton(self.update_details_frame, text=btn_text,
                              command=lambda: self.apply_update(update_info),
                              font=('Segoe UI', 11, 'bold' if mandatory else 'normal'),
                              fg_color=btn_color,
                              width=300,
                              height=36)
        update_btn.pack(anchor=tk.W)
    
    def apply_update(self, update_info):
        """Applique les mises à jour avec fenêtre de progression"""
        # Créer une fenêtre de progression avec CustomTkinter
        progress_window = ctk.CTkToplevel(self.root)
        progress_window.title("Mise à jour en cours...")
        progress_window.geometry("600x400")
        progress_window.resizable(False, False)
        progress_window.transient(self.root)
        progress_window.grab_set()  # Fenêtre modale
        
        # Centrer la fenêtre
        progress_window.update_idletasks()
        x = (progress_window.winfo_screenwidth() // 2) - (600 // 2)
        y = (progress_window.winfo_screenheight() // 2) - (400 // 2)
        progress_window.geometry(f"600x400+{x}+{y}")
        
        # Frame principal
        main_frame = ctk.CTkFrame(progress_window, fg_color="transparent")
        main_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        # Titre
        title_label = ctk.CTkLabel(main_frame, text="Mise à jour de MyMind", 
                               font=('Segoe UI', 14, 'bold'))
        title_label.pack(pady=(0, 10))
        
        # Informations de version
        version_frame = ctk.CTkFrame(main_frame, fg_color="transparent")
        version_frame.pack(fill=tk.X, pady=(0, 15))
        
        ctk.CTkLabel(version_frame, text=f"Version actuelle: {update_info['current_version']}", 
                 font=('Segoe UI', 11)).pack(anchor=tk.W)
        ctk.CTkLabel(version_frame, text=f"Nouvelle version: {update_info['new_version']}", 
                 font=('Segoe UI', 11, 'bold')).pack(anchor=tk.W)
        
        # Barre de progression
        progress_label = ctk.CTkLabel(main_frame, text="Initialisation...", 
                                  font=('Segoe UI', 11))
        progress_label.pack(anchor=tk.W, pady=(0, 5))
        
        progress_bar = ctk.CTkProgressBar(main_frame, width=560)
        progress_bar.pack(fill=tk.X, pady=(0, 15))
        progress_bar.set(0)
        
        # Zone de logs avec scrollbar
        log_frame = ctk.CTkFrame(main_frame, fg_color="transparent")
        log_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
        
        log_text = ctk.CTkTextbox(log_frame, height=200, width=560,
                          font=('Consolas', 9))
        log_text.pack(fill=tk.BOTH, expand=True)
        
        # Fonction pour mettre à jour la progression
        def update_progress(status_message: str, progress_percent: int):
            """Met à jour la barre de progression et les logs"""
            self.root.after(0, lambda: _update_ui(status_message, progress_percent))
        
        def _update_ui(status_message: str, progress_percent: int):
            """Met à jour l'interface (appelé dans le thread principal)"""
            progress_label.configure(text=status_message)
            progress_bar.set(progress_percent / 100.0)
            
            # Ajouter le message aux logs
            timestamp = time.strftime('%H:%M:%S')
            log_text.insert(tk.END, f"[{timestamp}] {status_message}\n")
            log_text.see(tk.END)  # Scroll vers le bas
            progress_window.update_idletasks()
        
        # Bouton annuler (désactivé pendant la mise à jour)
        cancel_button = ctk.CTkButton(main_frame, text="Annuler", 
                                 command=lambda: progress_window.destroy(),
                                 state=tk.DISABLED,
                                 width=120,
                                 height=32,
                                 fg_color=("gray75", "gray25"))
        cancel_button.pack(pady=(10, 0))
        
        # Thread de mise à jour
        def update_thread():
            try:
                _update_ui("Début de la mise à jour...", 0)
                success, message = self.updater.apply_updates(update_info, update_progress)
                
                self.root.after(0, lambda: _finish_update(success, message, progress_window))
            except Exception as e:
                self.root.after(0, lambda: _finish_update(False, f"Erreur: {str(e)}", progress_window))
        
        def _finish_update(success: bool, message: str, window):
            """Termine la mise à jour et affiche le résultat"""
            if success:
                progress_bar.set(1.0)
                progress_label.configure(text="Mise à jour terminée avec succès!", 
                                     text_color=self.success_color)
                _update_ui("✓ " + message, 100)
                
                # Changer le bouton en "Fermer"
                cancel_button.configure(text="Fermer et redémarrer", 
                                   command=lambda: self.root.quit(),
                                   state=tk.NORMAL,
                                   fg_color=self.success_color,
                                   hover_color='#059669')
                
                # Redémarrer automatiquement après 3 secondes
                self.root.after(3000, lambda: self.root.quit())
            else:
                progress_label.configure(text="Erreur lors de la mise à jour", 
                                     text_color=self.error_color)
                _update_ui("✗ " + message, int(progress_bar.get() * 100))
                cancel_button.configure(text="Fermer", 
                                   command=lambda: window.destroy(),
                                   state=tk.NORMAL,
                                   fg_color=self.error_color,
                                   hover_color='#dc2626')
        
        # Démarrer le thread de mise à jour
        threading.Thread(target=update_thread, daemon=True).start()
    
    def run(self):
        """Lance l'interface graphique"""
        self.update_status()
        self.root.mainloop()
