"""
SecureVault GUI: Post-Quantum Encryption Interface
Hybrid X25519 + ML-KEM-768 Quantum-Resistant Encryption
"""

import customtkinter as ctk
from tkinter import filedialog
import securevault
import os
import json
import base64
from datetime import datetime
import webbrowser
import traceback
import subprocess


# Application configuration
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")


class ThemeManager:
    """Centralized theme management for light/dark modes"""

    DARK = {
        "bg": "#070b12",
        "panel": "#0b1220",
        "panel2": "#0a1020",
        "card": "#0e1830",
        "border": "#22304a",
        "text": "#eaf1ff",
        "muted": "#a5b4cf",
        "muted2": "#7f91b2",
        "input": "#081224",
        "btn_primary": "#3b82f6",
        "btn_primary_hover": "#2563eb",
        "btn_secondary": "#121c32",
        "btn_secondary_hover": "#0d1628",
        "success": "#22c55e",
        "warn": "#f59e0b",
        "danger": "#ef4444",
        "neutral": "#64748b",
        "toast_bg": "#0b1220",
    }

    LIGHT = {
        "bg": "#e2e8f0",
        "panel": "#ffffff",
        "panel2": "#f8fafc",
        "card": "#ffffff",
        "border": "#cbd5e1",
        "text": "#0f172a",
        "muted": "#475569",
        "muted2": "#64748b",
        "input": "#f8fafc",
        "btn_primary": "#3b82f6",
        "btn_primary_hover": "#2563eb",
        "btn_secondary": "#e2e8f0",
        "btn_secondary_hover": "#cbd5e1",
        "success": "#10b981",
        "warn": "#f59e0b",
        "danger": "#ef4444",
        "neutral": "#64748b",
        "toast_bg": "#ffffff",
    }

    @classmethod
    def get_theme(cls, mode="dark"):
        return cls.DARK if mode == "dark" else cls.LIGHT


class Toast(ctk.CTkToplevel):
    """Non-blocking notification toast"""

    def __init__(self, parent, title, message, kind="neutral", duration_ms=2600):
        super().__init__(parent)
        self.overrideredirect(True)
        self.attributes("-topmost", True)

        colors = parent.current_theme
        bg = colors["toast_bg"]
        border = colors["border"]
        accent = {
            "neutral": colors["neutral"],
            "success": colors["success"],
            "warn": colors["warn"],
            "danger": colors["danger"],
        }.get(kind, colors["neutral"])

        self.configure(fg_color=bg)
        w, h = 380, 116
        self.geometry(f"{w}x{h}")

        try:
            parent.update_idletasks()
            px = parent.winfo_x()
            py = parent.winfo_y()
            pw = parent.winfo_width()
            self.geometry(f"+{px + pw - w - 24}+{py + 24}")
        except Exception:
            pass

        outer = ctk.CTkFrame(
            self,
            fg_color=bg,
            corner_radius=16,
            border_width=1,
            border_color=border,
        )
        outer.pack(fill="both", expand=True)

        top = ctk.CTkFrame(outer, fg_color="transparent")
        top.pack(fill="x", padx=12, pady=(12, 6))

        pill = ctk.CTkFrame(top, fg_color=accent, corner_radius=999)
        pill.pack(side="left", padx=(0, 10))
        ctk.CTkLabel(pill, text=" ", width=10, height=10).pack(padx=6, pady=6)

        ctk.CTkLabel(
            top,
            text=title,
            font=parent.FONTS["h2"],
            text_color=colors["text"],
        ).pack(side="left")

        ctk.CTkLabel(
            outer,
            text=message,
            font=parent.FONTS["body"],
            text_color=colors["muted"],
            justify="left",
            wraplength=w - 24,
        ).pack(fill="x", padx=12, pady=(0, 10))

        self.after(duration_ms, self.destroy)


class SuccessModal(ctk.CTkToplevel):
    """Consistent success/info modal dialog"""

    def __init__(self, parent, title, message, icon="✓", kind="success", size=None, footer_text=""):
        super().__init__(parent)
        self.title("")
        self.resizable(False, False)
        self.configure(fg_color=parent.current_theme["bg"])

        try:
            self.transient(parent)
        except Exception:
            pass

        # Bigger default; you can override with size=(w,h)
        if size is None:
            w, h = 600, 360
        else:
            w, h = size

        # auto-fit height so content stays visible without scrollbars
        if size is None:
            approx_lines = message.count("\n") + 1
            base = 320
            extra = approx_lines * 18
            h = min(540, max(360, base + extra))

        self.geometry(f"{w}x{h}")

        try:
            parent.update_idletasks()
            x = parent.winfo_x() + (parent.winfo_width() // 2) - (w // 2)
            y = parent.winfo_y() + (parent.winfo_height() // 2) - (h // 2)
            self.geometry(f"+{x}+{y}")
        except Exception:
            pass

        card = ctk.CTkFrame(
            self,
            fg_color=parent.current_theme["panel"],
            corner_radius=20,
            border_width=1,
            border_color=parent.current_theme["border"],
        )
        card.pack(fill="both", expand=True, padx=20, pady=20)

        icon_color = {
            "success": parent.current_theme["success"],
            "warn": parent.current_theme["warn"],
            "danger": parent.current_theme["danger"],
            "neutral": parent.current_theme["neutral"],
        }.get(kind, parent.current_theme["success"])

        icon_frame = ctk.CTkFrame(
            card,
            fg_color=icon_color,
            corner_radius=999,
            width=72,
            height=72,
        )
        icon_frame.pack(pady=(24, 16))
        icon_frame.pack_propagate(False)

        ctk.CTkLabel(
            icon_frame,
            text=icon,
            font=("Segoe UI", 34, "bold"),
            text_color="white",
        ).pack(expand=True)

        ctk.CTkLabel(
            card,
            text=title,
            font=parent.FONTS["h1"],
            text_color=parent.current_theme["text"],
        ).pack(pady=(0, 10))

        msg_frame = ctk.CTkFrame(card, fg_color="transparent")
        msg_frame.pack(fill="both", expand=True, padx=24, pady=(0, 10))

        self.msg_label = ctk.CTkLabel(
            msg_frame,
            text=message,
            font=parent.FONTS["body"],
            text_color=parent.current_theme["muted"],
            wraplength=w - 80,
            justify="left",
        )
        self.msg_label.pack(fill="both", expand=True)

        self.footer_label = ctk.CTkLabel(
            card,
            text=footer_text or "",
            font=parent.FONTS["small"],
            text_color=parent.current_theme["muted2"],
            justify="center",
        )
        self.footer_label.pack(fill="x", padx=24, pady=(0, 8))

        ctk.CTkLabel(
            card,
            text="Tip: Press Enter to close",
            font=parent.FONTS["small"],
            text_color=parent.current_theme["muted2"],
        ).pack(pady=(0, 10))

        # FIX: OK button was too long + not visually centered.
        # Use a centered button with a sane width.
        btn_row = ctk.CTkFrame(card, fg_color="transparent")
        btn_row.pack(fill="x", padx=24, pady=(0, 5))
        btn_row.grid_columnconfigure(0, weight=1)
        btn_row.grid_columnconfigure(1, weight=0)
        btn_row.grid_columnconfigure(2, weight=1)

        ctk.CTkButton(
            btn_row,
            text="OK",
            height=14,
            width=180,
            corner_radius=14,
            fg_color=icon_color,
            hover_color=icon_color,
            font=parent.FONTS["h2"],
            command=self.destroy,
        ).grid(row=0, column=1)

        self.bind("<Return>", lambda _: self.destroy())
        self.bind("<Escape>", lambda _: self.destroy())

        self.update_idletasks()
        self.deiconify()
        self.lift()
        self.attributes("-topmost", True)

        def _safe_grab():
            try:
                self.grab_set()
            except Exception:
                self.after(50, _safe_grab)

        self.after(30, _safe_grab)


class HelpModal(ctk.CTkToplevel):
    """Scrollable help dialog with clean formatting"""

    def __init__(self, parent):
        super().__init__(parent)
        self.title("")
        self.resizable(False, False)
        self.configure(fg_color=parent.current_theme["bg"])

        try:
            self.transient(parent)
        except Exception:
            pass

        w, h = 620, 720
        self.geometry(f"{w}x{h}")

        try:
            parent.update_idletasks()
            x = parent.winfo_x() + (parent.winfo_width() // 2) - (w // 2)
            y = parent.winfo_y() + (parent.winfo_height() // 2) - (h // 2)
            self.geometry(f"+{x}+{y}")
        except Exception:
            pass

        card = ctk.CTkFrame(
            self,
            fg_color=parent.current_theme["panel"],
            corner_radius=20,
            border_width=1,
            border_color=parent.current_theme["border"],
        )
        card.pack(fill="both", expand=True, padx=20, pady=20)

        header = ctk.CTkFrame(card, fg_color="transparent")
        header.pack(fill="x", padx=24, pady=(20, 16))

        icon_frame = ctk.CTkFrame(
            header,
            fg_color=parent.current_theme["btn_primary"],
            corner_radius=999,
            width=48,
            height=48,
        )
        icon_frame.pack(side="left", padx=(0, 12))
        icon_frame.pack_propagate(False)

        ctk.CTkLabel(
            icon_frame,
            text="?",
            font=("Segoe UI", 28, "bold"),
            text_color="white",
        ).pack(expand=True)

        ctk.CTkLabel(
            header,
            text="SecureVault: Quick Guide",
            font=parent.FONTS["logo"],
            text_color=parent.current_theme["text"],
        ).pack(side="left")

        scroll_frame = ctk.CTkScrollableFrame(
            card,
            fg_color=parent.current_theme["card"],
            corner_radius=16,
            border_width=1,
            border_color=parent.current_theme["border"],
        )
        scroll_frame.pack(fill="both", expand=True, padx=24, pady=(0, 10))

        sections = [
            ("► Keys Tab", "Generate a new keypair. Keep private key secure.\nShare only the _public.key file."),
            ("▣ Encrypt Tab", "Choose file and recipient public key.\nAlso choose YOUR private key for signing.\nOutput: <filename>.vault"),
            ("▢ Decrypt Tab", "Choose .vault file and your private key.\nAlso choose the SENDER public key for verification.\nEnter your password when prompted."),
            ("◉ Info Tab", "View encryption details of .vault files."),
            ("⌨ Keyboard Shortcuts", "Ctrl+L: Clear activity log\nF1: Show help"),
            ("→ Links", "If links do not open in WSL: this build uses a WSL-safe opener."),
        ]

        for title, content in sections:
            section = ctk.CTkFrame(scroll_frame, fg_color="transparent")
            section.pack(fill="x", padx=16, pady=12)

            ctk.CTkLabel(
                section,
                text=title,
                font=parent.FONTS["h2"],
                text_color=parent.current_theme["text"],
                anchor="w",
            ).pack(anchor="w", pady=(0, 6))

            ctk.CTkLabel(
                section,
                text=content,
                font=parent.FONTS["body"],
                text_color=parent.current_theme["muted"],
                anchor="w",
                justify="left",
            ).pack(anchor="w")

        ctk.CTkLabel(
            card,
            text="Tip: Press Enter to close",
            font=parent.FONTS["small"],
            text_color=parent.current_theme["muted2"],
        ).pack(pady=(0, 8))

        ctk.CTkButton(
            card,
            text="Got it!",
            height=44,
            corner_radius=14,
            fg_color=parent.current_theme["btn_primary"],
            hover_color=parent.current_theme["btn_primary_hover"],
            font=parent.FONTS["h2"],
            command=self.destroy,
        ).pack(fill="x", padx=24, pady=(0, 20))

        self.bind("<Return>", lambda _: self.destroy())
        self.bind("<Escape>", lambda _: self.destroy())

        self.update_idletasks()
        self.deiconify()
        self.lift()

        def _safe_grab():
            try:
                self.grab_set()
            except Exception:
                self.after(50, _safe_grab)

        self.after(20, _safe_grab)


class ErrorModal(ctk.CTkToplevel):
    """Consistent error modal dialog"""

    def __init__(self, parent, title, message, user_friendly_msg=None):
        super().__init__(parent)
        self.title("")
        self.resizable(False, False)
        self.configure(fg_color=parent.current_theme["bg"])

        try:
            self.transient(parent)
        except Exception:
            pass

        w, h = 680, 460
        self.geometry(f"{w}x{h}")

        try:
            parent.update_idletasks()
            x = parent.winfo_x() + (parent.winfo_width() // 2) - (w // 2)
            y = parent.winfo_y() + (parent.winfo_height() // 2) - (h // 2)
            self.geometry(f"+{x}+{y}")
        except Exception:
            pass

        card = ctk.CTkFrame(
            self,
            fg_color=parent.current_theme["panel"],
            corner_radius=20,
            border_width=1,
            border_color=parent.current_theme["border"],
        )
        card.pack(fill="both", expand=True, padx=20, pady=20)

        icon_frame = ctk.CTkFrame(
            card,
            fg_color=parent.current_theme["danger"],
            corner_radius=999,
            width=64,
            height=64,
        )
        icon_frame.pack(pady=(20, 12))
        icon_frame.pack_propagate(False)

        ctk.CTkLabel(
            icon_frame,
            text="X",
            font=("Segoe UI", 32, "bold"),
            text_color="white",
        ).pack(expand=True)

        ctk.CTkLabel(
            card,
            text=title,
            font=parent.FONTS["h1"],
            text_color=parent.current_theme["text"],
        ).pack(pady=(0, 10))

        if user_friendly_msg:
            ctk.CTkLabel(
                card,
                text=user_friendly_msg,
                font=parent.FONTS["body"],
                text_color=parent.current_theme["text"],
                wraplength=620,
                justify="left",
            ).pack(padx=18, pady=(0, 10))

        box = ctk.CTkTextbox(
            card,
            fg_color=parent.current_theme["input"],
            border_width=1,
            border_color=parent.current_theme["border"],
            corner_radius=14,
            text_color=parent.current_theme["muted"],
            font=parent.FONTS["mono"],
            wrap="word",
            height=210 if user_friendly_msg else 250,
        )
        box.pack(fill="both", expand=True, padx=18, pady=(0, 10))
        box.insert("1.0", message)
        box.configure(state="disabled")

        ctk.CTkLabel(
            card,
            text="Tip: Press Enter to close",
            font=parent.FONTS["small"],
            text_color=parent.current_theme["muted2"],
        ).pack(pady=(0, 10))

        ctk.CTkButton(
            card,
            text="OK",
            height=44,
            corner_radius=14,
            fg_color=parent.current_theme["danger"],
            hover_color="#dc2626",
            font=parent.FONTS["h2"],
            command=self.destroy,
        ).pack(fill="x", padx=18, pady=(0, 18))

        self.bind("<Return>", lambda _: self.destroy())
        self.bind("<Escape>", lambda _: self.destroy())

        self.update_idletasks()
        self.deiconify()
        self.lift()

        def _safe_grab():
            try:
                self.grab_set()
            except Exception:
                self.after(50, _safe_grab)

        self.after(20, _safe_grab)


class PasswordModal(ctk.CTkToplevel):
    """Password input dialog with show/hide toggle"""

    def __init__(self, parent, title, prompt, confirm=False):
        super().__init__(parent)
        self.value = None
        self.confirm_value = None
        self._show = False
        self._confirm = confirm

        self.title("")
        self.resizable(False, False)
        self.configure(fg_color=parent.current_theme["bg"])

        try:
            self.transient(parent)
        except Exception:
            pass

        w = 500
        h = 340 if confirm else 300
        self.geometry(f"{w}x{h}")

        try:
            parent.update_idletasks()
            x = parent.winfo_x() + (parent.winfo_width() // 2) - (w // 2)
            y = parent.winfo_y() + (parent.winfo_height() // 2) - (h // 2)
            self.geometry(f"+{x}+{y}")
        except Exception:
            pass

        card = ctk.CTkFrame(
            self,
            fg_color=parent.current_theme["panel"],
            corner_radius=20,
            border_width=1,
            border_color=parent.current_theme["border"],
        )
        card.pack(fill="both", expand=True, padx=20, pady=20)

        icon_frame = ctk.CTkFrame(
            card,
            fg_color=parent.current_theme["btn_primary"],
            corner_radius=999,
            width=56,
            height=56,
        )
        icon_frame.pack(pady=(20, 16))
        icon_frame.pack_propagate(False)

        ctk.CTkLabel(
            icon_frame,
            text="KEY",
            font=("Segoe UI", 18, "bold"),
            text_color="white",
        ).pack(expand=True)

        ctk.CTkLabel(
            card,
            text=title,
            font=parent.FONTS["h1"],
            text_color=parent.current_theme["text"],
        ).pack(pady=(0, 8))

        ctk.CTkLabel(
            card,
            text=prompt,
            font=parent.FONTS["body"],
            text_color=parent.current_theme["muted"],
            justify="center",
            wraplength=440,
        ).pack(pady=(0, 16))

        self.entry = ctk.CTkEntry(
            card,
            placeholder_text="Password",
            show="•",
            height=44,
            corner_radius=12,
            fg_color=parent.current_theme["input"],
            border_color=parent.current_theme["border"],
            border_width=1,
            text_color=parent.current_theme["text"],
        )
        self.entry.pack(fill="x", padx=24, pady=(0, 10))

        self.entry2 = None
        if confirm:
            self.entry2 = ctk.CTkEntry(
                card,
                placeholder_text="Confirm password",
                show="•",
                height=44,
                corner_radius=12,
                fg_color=parent.current_theme["input"],
                border_color=parent.current_theme["border"],
                border_width=1,
                text_color=parent.current_theme["text"],
            )
            self.entry2.pack(fill="x", padx=24, pady=(0, 10))

        # explicit hint so users know Enter submits
        ctk.CTkLabel(
            card,
            text="Tip: Press Enter to confirm",
            font=parent.FONTS["small"],
            text_color=parent.current_theme["muted2"],
        ).pack(pady=(0, 10))

        self.show_btn = ctk.CTkButton(
            card,
            text="[SHOW] Show Password",
            height=36,
            corner_radius=10,
            fg_color=parent.current_theme["btn_secondary"],
            hover_color=parent.current_theme["btn_secondary_hover"],
            border_width=1,
            border_color=parent.current_theme["border"],
            font=parent.FONTS["small"],
            command=self._toggle_show,
        )
        self.show_btn.pack(padx=24, pady=(0, 16))

        btn_row = ctk.CTkFrame(card, fg_color="transparent")
        btn_row.pack(fill="x", padx=24, pady=(0, 20))

        cancel = ctk.CTkButton(
            btn_row,
            text="Cancel",
            height=44,
            corner_radius=12,
            fg_color=parent.current_theme["btn_secondary"],
            hover_color=parent.current_theme["btn_secondary_hover"],
            border_width=1,
            border_color=parent.current_theme["border"],
            font=parent.FONTS["h3"],
            command=self._cancel,
        )
        cancel.pack(side="left", fill="x", expand=True, padx=(0, 8))

        ok = ctk.CTkButton(
            btn_row,
            text="Continue",
            height=44,
            corner_radius=12,
            fg_color=parent.current_theme["btn_primary"],
            hover_color=parent.current_theme["btn_primary_hover"],
            font=parent.FONTS["h3"],
            command=self._ok,
        )
        ok.pack(side="left", fill="x", expand=True, padx=(8, 0))

        self.bind("<Return>", lambda _: self._ok())
        self.bind("<Escape>", lambda _: self._cancel())

        self.update_idletasks()
        self.deiconify()
        self.lift()
        self.entry.focus_set()

        def _safe_grab():
            try:
                self.grab_set()
            except Exception:
                self.after(50, _safe_grab)

        self.after(20, _safe_grab)

    def _toggle_show(self):
        self._show = not self._show
        char = "" if self._show else "•"
        self.entry.configure(show=char)
        if self.entry2 is not None:
            self.entry2.configure(show=char)
        self.show_btn.configure(
            text="[HIDE] Hide Password" if self._show else "[SHOW] Show Password"
        )

    def _ok(self):
        self.value = (self.entry.get() or "").strip()
        if self._confirm and self.entry2 is not None:
            self.confirm_value = (self.entry2.get() or "").strip()
        self.destroy()

    def _cancel(self):
        self.value = None
        self.confirm_value = None
        self.destroy()


class SecureVaultGUI(ctk.CTk):
    """Main application window"""

    FONTS = {
        "logo": ("Segoe UI", 20, "bold"),
        "h1": ("Segoe UI", 16, "bold"),
        "h2": ("Segoe UI", 13, "bold"),
        "h3": ("Segoe UI", 12, "bold"),
        "body": ("Segoe UI", 12),
        "small": ("Segoe UI", 10),
        "mono": ("Consolas", 10),
    }

    def __init__(self):
        super().__init__()
        self.title("SecureVault: Post-Quantum Encryption")
        self.geometry("990x800")
        self.minsize(900, 690)

        self.theme_mode = "dark"
        self.current_theme = ThemeManager.get_theme("dark")
        self.configure(fg_color=self.current_theme["bg"])

        self.last_output_path = None

        # Themed widget registries for full light mode repaint
        self._themed_entries = []
        self._themed_secondary_buttons = []
        self._themed_cards = []
        self._themed_tabviews = []
        self._themed_primary_buttons = []
        self._themed_muted_labels = []
        self._themed_tab_headers = []
        self._themed_path_labels = []
        self._themed_tab_buttons = None

        # Track key generation for thank-you on close (kept)
        self._keys_generated_this_session = False
        self._thank_on_close_shown = False

        # FIX #2: show thank-you if the user did ANY action (keys/encrypt/decrypt/info)
        self._did_any_action = False

        # Hide main window; show splash first
        self.withdraw()

        self._build_ui()
        self._set_status("Ready: choose an action.", kind="neutral")
        self._log("SYSTEM", "SecureVault GUI initialized.")
        self._show_splash_then_main()

        # Handle window close (X button)
        self.protocol("WM_DELETE_WINDOW", self._on_close)

    # ==================== SPLASH ====================
    def _show_splash_then_main(self):
        splash = ctk.CTkToplevel(self)
        splash.overrideredirect(True)
        splash.attributes("-topmost", True)
        splash.configure(fg_color=self.current_theme["bg"])

        w, h = 420, 160
        self.update_idletasks()
        x = self.winfo_screenwidth() // 2 - w // 2
        y = self.winfo_screenheight() // 2 - h // 2
        splash.geometry(f"{w}x{h}+{x}+{y}")

        card = ctk.CTkFrame(
            splash,
            fg_color=self.current_theme["panel"],
            corner_radius=18,
            border_width=1,
            border_color=self.current_theme["border"],
        )
        card.pack(fill="both", expand=True, padx=12, pady=12)

        ctk.CTkLabel(
            card,
            text="■ SecureVault",
            font=self.FONTS["logo"],
            text_color=self.current_theme["text"],
        ).pack(pady=(18, 4))

        ctk.CTkLabel(
            card,
            text="Application loaded successfully.",
            font=self.FONTS["body"],
            text_color=self.current_theme["muted"],
        ).pack()

        ctk.CTkLabel(
            card,
            text="Preparing GUI…",
            font=self.FONTS["small"],
            text_color=self.current_theme["muted2"],
        ).pack(pady=(8, 0))

        def _close():
            try:
                splash.destroy()
            except Exception:
                pass
            self.deiconify()
            self.lift()

        self.after(1500, _close)

    # ==================== ERROR AND LINK HELPERS ====================
    def _format_exception(self, e: Exception) -> str:
        name = type(e).__name__
        msg = str(e).strip()
        if not msg:
            msg = repr(e)
        tb = traceback.format_exc()
        return f"{name}: {msg}\n\nTraceback:\n{tb}"

    def _get_user_friendly_error(self, e: Exception) -> str:
        error_name = type(e).__name__

        if "InvalidToken" in error_name:
            return "Incorrect password. Please verify you're using the correct password for this private key."
        elif "FileNotFoundError" in error_name:
            return "File not found. Please check the file path and try again."
        elif "PermissionError" in error_name:
            return "Permission denied. Check if you have read/write access to the file."
        elif "JSONDecodeError" in error_name:
            return "Invalid file format. The selected file may be corrupted or not a valid vault file."
        elif "KeyError" in error_name:
            return "Missing required data in file. The file may be incomplete or corrupted."
        else:
            return f"An error occurred: {error_name}. See details below."

    def _is_wsl(self) -> bool:
        if os.environ.get("WSL_DISTRO_NAME"):
            return True
        try:
            with open("/proc/version", "r") as f:
                return "microsoft" in f.read().lower()
        except Exception:
            return False

    def _open_url(self, url: str):
            
            try:
                self.attributes("-topmost", False)
            except Exception:
                pass

            try:
                if os.name == "nt":
                    try:
                        os.startfile(url)  # type: ignore[attr-defined]
                        return
                    except Exception:
                        webbrowser.open_new_tab(url)
                        return

                if self._is_wsl():
                    try:
                        has_wslview = (
                            subprocess.call(
                                ["bash", "-lc", "command -v wslview >/dev/null 2>&1"]
                            )
                            == 0
                        )
                    except Exception:
                        has_wslview = False

                    if has_wslview:
                        subprocess.Popen(
                            ["wslview", url],
                            stdout=subprocess.DEVNULL,
                            stderr=subprocess.DEVNULL,
                        )
                        return

                    subprocess.Popen(
                        ["cmd.exe", "/c", "start", "", url],
                        stdout=subprocess.DEVNULL,
                        stderr=subprocess.DEVNULL,
                    )
                    return

                webbrowser.open_new_tab(url)
            except Exception as e:
                details = self._format_exception(e)
                self._log("SYSTEM", f"Failed to open URL:\n{details}")
                ErrorModal(self, "Could Not Open Link", details[:1500], f"Could not open:\n{url}")

    # ==================== UI CONSTRUCTION ====================
    def _build_ui(self):
        self.grid_columnconfigure(0, weight=0)
        self.grid_columnconfigure(1, weight=1)
        self.grid_rowconfigure(0, weight=0)
        self.grid_rowconfigure(1, weight=1)
        self.grid_rowconfigure(2, weight=0)

        self._build_topbar()
        self._build_sidebar()
        self._build_main_content()
        self._build_statusbar()

        self.bind("<Control-l>", lambda _: self._clear_log())
        self.bind("<Control-h>", lambda _: self._show_help())
        self.bind("<F1>", lambda _: self._show_help())

    def _build_topbar(self):
        self.topbar = ctk.CTkFrame(
            self,
            fg_color=self.current_theme["panel"],
            corner_radius=18,
            border_width=1,
            border_color=self.current_theme["border"],
        )
        self.topbar.grid(row=0, column=0, columnspan=2, sticky="ew", padx=18, pady=(18, 10))
        self.topbar.grid_columnconfigure(0, weight=1)
        self.topbar.grid_columnconfigure(1, weight=0)

        left = ctk.CTkFrame(self.topbar, fg_color="transparent")
        left.grid(row=0, column=0, sticky="w", padx=16, pady=14)

        self.logo_label = ctk.CTkLabel(
            left,
            text="◼ SecureVault",
            font=self.FONTS["logo"],
            text_color=self.current_theme["text"],
        )
        self.logo_label.pack(anchor="w")

        self.subtitle_label = ctk.CTkLabel(
            left,
            text="Hybrid X25519 + ML-KEM-768: Quantum-Resistant",
            font=self.FONTS["small"],
            text_color=self.current_theme["muted2"],
        )
        self.subtitle_label.pack(anchor="w", pady=(2, 0))

        right = ctk.CTkFrame(self.topbar, fg_color="transparent")
        right.grid(row=0, column=1, sticky="e", padx=16, pady=14)

        social_frame = ctk.CTkFrame(right, fg_color="transparent")
        social_frame.pack(side="left", padx=(0, 16))

        self.linkedin_link = self._link_label(social_frame, "⊡ LinkedIn", self._open_linkedin)
        self.linkedin_link.pack(side="left", padx=(0, 12))

        self.github_link = self._link_label(social_frame, "⬡ GitHub", self._open_github)
        self.github_link.pack(side="left")

        self.theme_switch = ctk.CTkSwitch(
            right,
            text="Light mode",
            font=self.FONTS["h3"],
            command=self._toggle_theme,
        )
        self.theme_switch.pack(side="left", padx=(0, 12))

        self.help_btn = ctk.CTkButton(
            right,
            text="◉ Help",
            height=34,
            corner_radius=12,
            fg_color=self.current_theme["btn_secondary"],
            hover_color=self.current_theme["btn_secondary_hover"],
            border_width=1,
            border_color=self.current_theme["border"],
            font=self.FONTS["h3"],
            command=self._show_help,
        )
        self.help_btn.pack(side="left")

    def _build_sidebar(self):
        self.sidebar = ctk.CTkFrame(
            self,
            fg_color=self.current_theme["panel"],
            corner_radius=18,
            border_width=1,
            border_color=self.current_theme["border"],
        )
        self.sidebar.grid(row=1, column=0, sticky="nsew", padx=(18, 10), pady=(0, 10))
        self.sidebar.grid_rowconfigure(1, weight=1)

        log_header = ctk.CTkFrame(self.sidebar, fg_color="transparent")
        log_header.pack(fill="x", padx=16, pady=(16, 10))

        self.log_title = ctk.CTkLabel(
            log_header,
            text="Activity Log",
            font=self.FONTS["h2"],
            text_color=self.current_theme["text"],
            anchor="w",
        )
        self.log_title.pack(side="left", fill="x", expand=True)

        self.log_clear_btn = ctk.CTkButton(
            log_header,
            text="Clear",
            height=32,
            width=70,
            corner_radius=10,
            fg_color=self.current_theme["btn_secondary"],
            hover_color=self.current_theme["btn_secondary_hover"],
            border_width=1,
            border_color=self.current_theme["border"],
            font=self.FONTS["small"],
            command=self._clear_log,
        )
        self.log_clear_btn.pack(side="right")

        self.log = ctk.CTkTextbox(
            self.sidebar,
            fg_color=self.current_theme["input"],
            border_width=1,
            border_color=self.current_theme["border"],
            corner_radius=14,
            text_color=self.current_theme["text"],
            font=self.FONTS["mono"],
            wrap="word",
            height=200,
        )
        self.log.pack(fill="both", expand=True, padx=16, pady=(0, 14))
        self.log.configure(state="disabled")

        self.hint = ctk.CTkFrame(
            self.sidebar,
            fg_color=self.current_theme["card"],
            corner_radius=18,
        )
        self.hint.pack(fill="x", padx=16, pady=(0, 14))

        self.hint_title = ctk.CTkLabel(
            self.hint,
            text="➤ Security Tips",
            font=self.FONTS["h2"],
            text_color=self.current_theme["text"],
        )
        self.hint_title.pack(anchor="w", padx=14, pady=(12, 6))

        self.hint_text = ctk.CTkLabel(
            self.hint,
            text="• Keep private keys offline\n• Share only public keys\n• Use strong passwords\n• Back up keys securely",
            font=self.FONTS["body"],
            text_color=self.current_theme["muted"],
            justify="left",
        )
        self.hint_text.pack(anchor="w", padx=14, pady=(0, 12))

        links_frame = ctk.CTkFrame(self.sidebar, fg_color="transparent")
        links_frame.pack(side="bottom", fill="x", padx=16, pady=(0, 10))

        self.docs_link = self._link_label(links_frame, "⬡ AboutME", self._open_docs)
        self.docs_link.pack(side="left", padx=(0, 10))

        self.pypi_link = self._link_label(links_frame, "⊟ PyPI", self._open_pypi)
        self.pypi_link.pack(side="left")

        self.shortcuts_label = ctk.CTkLabel(
            self.sidebar,
            text="Shortcuts: Ctrl+L • F1",
            font=self.FONTS["small"],
            text_color=self.current_theme["muted2"],
        )
        self.shortcuts_label.pack(side="bottom", anchor="w", padx=16, pady=(0, 10))

    def _build_main_content(self):
        self.main = ctk.CTkFrame(
            self,
            fg_color=self.current_theme["panel2"],
            corner_radius=18,
            border_width=1,
            border_color=self.current_theme["border"],
        )
        self.main.grid(row=1, column=1, sticky="nsew", padx=(10, 18), pady=(0, 10))
        self.main.grid_columnconfigure(0, weight=1)
        self.main.grid_rowconfigure(0, weight=1)

        self._build_tabs(self.main)

    def _build_tabs(self, parent):
        self.tabs = ctk.CTkTabview(
            parent,
            fg_color=self.current_theme["panel2"],
            segmented_button_fg_color=self.current_theme["card"],
            segmented_button_selected_color=self.current_theme["btn_primary"],
            segmented_button_selected_hover_color=self.current_theme["btn_primary_hover"],
            segmented_button_unselected_color=self.current_theme["card"],
            corner_radius=18,
        )
        self.tabs.grid(row=0, column=0, sticky="nsew", padx=18, pady=18)
        self._themed_tabviews.append(self.tabs)

        for name in ["Keys", "Encrypt", "Decrypt", "Info"]:
            self.tabs.add(name)

        try:
            self._themed_tab_buttons = self.tabs._segmented_button
        except Exception:
            self._themed_tab_buttons = None

        self._build_tab_keys(self.tabs.tab("Keys"))
        self._build_tab_encrypt(self.tabs.tab("Encrypt"))
        self._build_tab_decrypt(self.tabs.tab("Decrypt"))
        self._build_tab_info(self.tabs.tab("Info"))

    def _build_statusbar(self):
        self.statusbar = ctk.CTkFrame(
            self,
            fg_color=self.current_theme["panel"],
            corner_radius=18,
            border_width=1,
            border_color=self.current_theme["border"],
        )
        self.statusbar.grid(row=2, column=0, columnspan=2, sticky="ew", padx=18, pady=(0, 18))
        self.statusbar.grid_columnconfigure(0, weight=1)
        self.statusbar.grid_columnconfigure(1, weight=0)

        self.status_label = ctk.CTkLabel(
            self.statusbar,
            text="",
            font=self.FONTS["small"],
            text_color=self.current_theme["muted"],
            anchor="w",
        )
        self.status_label.grid(row=0, column=0, sticky="ew", padx=14, pady=10)

        self.progress = ctk.CTkProgressBar(self.statusbar, height=10)
        self.progress.grid(row=0, column=1, sticky="e", padx=14, pady=10)
        self.progress.set(0)
        self.progress.grid_remove()

    # ==================== TAB BUILDERS ====================
    def _build_tab_keys(self, parent):
        self._tab_header(
            parent,
            "◆ Key Management",
            "Generate a new hybrid keypair. Private key is password-protected. Public key is exported separately for sharing.",
        )

        card = self._card(parent)

        self.keys_save = self._path_row(
            card,
            "Private key output path",
            "Example: alice.key",
            browse_cmd=self._browse_save_key,
            clear_cmd=lambda: self._clear_entry(self.keys_save),
        )

        self.keys_public_hint = ctk.CTkLabel(
            card,
            text="Public key will be saved as: <name>_public.key",
            font=self.FONTS["small"],
            text_color=self.current_theme["muted2"],
        )
        self.keys_public_hint.pack(anchor="w", padx=18, pady=(0, 10))
        self._themed_muted_labels.append(self.keys_public_hint)

        self.btn_keys = self._primary(card, "⊛ Generate Keypair", self.generate_keys, kind="primary")

    def _build_tab_encrypt(self, parent):
        self._tab_header(
            parent,
            "⟳ Encrypt File",
            "Select a file and recipient public key. Output will be saved as <filename>.vault",
        )

        card = self._card(parent)

        self.enc_file = self._path_row(
            card,
            "File to encrypt",
            "Choose any file",
            browse_cmd=self._browse_enc_file,
            clear_cmd=lambda: self._clear_entry(self.enc_file),
        )

        self.enc_pub = self._path_row(
            card,
            "Recipient public key",
            "Example: bob_public.key",
            browse_cmd=self._browse_enc_key,
            clear_cmd=lambda: self._clear_entry(self.enc_pub),
        )

        self.enc_sender_priv = self._path_row(
            card,
            "Your private key (signing)",
            "Example: alice.key",
            browse_cmd=self._browse_enc_sender_priv_key,
            clear_cmd=lambda: self._clear_entry(self.enc_sender_priv),
        )

        self.enc_output_label = ctk.CTkLabel(
            card,
            text="Output: not generated yet",
            font=self.FONTS["small"],
            text_color=self.current_theme["muted2"],
        )
        self.enc_output_label.pack(anchor="w", padx=18, pady=(0, 6))
        self._themed_muted_labels.append(self.enc_output_label)

        self.btn_enc = self._primary(card, "⟳ Encrypt File", self.encrypt_file, kind="primary")

    def _build_tab_decrypt(self, parent):
        self._tab_header(
            parent,
            "⟲ Decrypt File",
            "Select a .vault file and your private key. You will be prompted for your password.",
        )

        card = self._card(parent)

        self.dec_file = self._path_row(
            card,
            "Vault file",
            "Example: secret.vault",
            browse_cmd=self._browse_dec_file,
            clear_cmd=lambda: self._clear_entry(self.dec_file),
        )

        self.dec_priv = self._path_row(
            card,
            "Your private key",
            "Example: alice.key",
            browse_cmd=self._browse_dec_key,
            clear_cmd=lambda: self._clear_entry(self.dec_priv),
        )

        self.dec_sender_pub = self._path_row(
            card,
            "Sender public key",
            "Example: sender_public.key",
            browse_cmd=self._browse_dec_sender_pub_key,
            clear_cmd=lambda: self._clear_entry(self.dec_sender_pub),
        )

        self.dec_output_label = ctk.CTkLabel(
            card,
            text="Output: not generated yet",
            font=self.FONTS["small"],
            text_color=self.current_theme["muted2"],
        )
        self.dec_output_label.pack(anchor="w", padx=18, pady=(0, 6))
        self._themed_muted_labels.append(self.dec_output_label)

        self.btn_dec = self._primary(card, "⟲ Decrypt File", self.decrypt_file, kind="primary")

    def _build_tab_info(self, parent):
        self._tab_header(
            parent,
            "◉ File Information",
            "Read metadata from a .vault file to see encryption algorithm and payload size.",
        )

        card = self._card(parent)

        self.info_file = self._path_row(
            card,
            "Vault file",
            "Example: file.vault",
            browse_cmd=self._browse_info_file,
            clear_cmd=lambda: self._clear_entry(self.info_file),
        )

        self.btn_info = self._primary(card, "◎ Read Vault Info", self.file_info, kind="border")

    # ==================== UI HELPERS ====================
    def _tab_header(self, parent, title, desc):
        top = ctk.CTkFrame(parent, fg_color="transparent")
        top.pack(fill="x", padx=18, pady=(18, 10))

        title_label = ctk.CTkLabel(
            top,
            text=title,
            font=self.FONTS["h1"],
            text_color=self.current_theme["text"],
        )
        title_label.pack(anchor="w")

        desc_label = ctk.CTkLabel(
            top,
            text=desc,
            font=self.FONTS["body"],
            text_color=self.current_theme["muted"],
            wraplength=650,
            justify="left",
        )
        desc_label.pack(anchor="w", pady=(6, 0))

        self._themed_tab_headers.append((title_label, desc_label))

    def _card(self, parent):
        card = ctk.CTkFrame(
            parent,
            fg_color=self.current_theme["panel"],
            corner_radius=18,
            border_width=1,
            border_color=self.current_theme["border"],
        )
        card.pack(fill="x", padx=18, pady=(6, 12))
        self._themed_cards.append(card)
        return card

    def _path_row(self, parent, label, placeholder, browse_cmd, clear_cmd):
        label_color = "#0f172a" if self.theme_mode == "light" else self.current_theme["text"]
        label_widget = ctk.CTkLabel(
            parent,
            text=label,
            font=self.FONTS["h2"],
            text_color=label_color,
        )
        label_widget.pack(anchor="w", padx=18, pady=(14, 6))
        self._themed_path_labels.append(label_widget)

        row = ctk.CTkFrame(parent, fg_color="transparent")
        row.pack(fill="x", padx=18, pady=(0, 10))

        entry = ctk.CTkEntry(
            row,
            placeholder_text=placeholder,
            height=42,
            corner_radius=14,
            fg_color=self.current_theme["input"],
            border_width=1,
            border_color=self.current_theme["border"],
            text_color=self.current_theme["text"],
        )
        entry.pack(side="left", fill="x", expand=True)

        browse_btn = ctk.CTkButton(
            row,
            text="Browse",
            height=42,
            width=110,
            corner_radius=14,
            fg_color=self.current_theme["btn_secondary"],
            hover_color=self.current_theme["btn_secondary_hover"],
            border_width=1,
            border_color=self.current_theme["border"],
            font=self.FONTS["h3"],
            command=browse_cmd,
        )
        browse_btn.pack(side="left", padx=(10, 0))

        clear_btn = ctk.CTkButton(
            row,
            text="Clear",
            height=42,
            width=100,
            corner_radius=14,
            fg_color=self.current_theme["btn_secondary"],
            hover_color=self.current_theme["btn_secondary_hover"],
            border_width=1,
            border_color=self.current_theme["border"],
            font=self.FONTS["h3"],
            command=clear_cmd,
        )
        clear_btn.pack(side="left", padx=(10, 0))

        self._themed_entries.append(entry)
        self._themed_secondary_buttons.append(browse_btn)
        self._themed_secondary_buttons.append(clear_btn)

        return entry

    def _primary(self, parent, text, cmd, kind="primary"):
        """
        Primary action button helper; supports:
        primary, success, warn, neutral, border
        """
        palette = {
            "primary": (self.current_theme["btn_primary"], self.current_theme["btn_primary_hover"]),
            "success": (self.current_theme["success"], "#16a34a"),
            "warn": (self.current_theme["warn"], "#d97706"),
            "neutral": (self.current_theme["neutral"], "#64748b"),
            "border": (self.current_theme["btn_secondary"], self.current_theme["btn_secondary_hover"]),
        }
        fg, hover = palette.get(kind, palette["primary"])

        btn = ctk.CTkButton(
            parent,
            text=text,
            height=46,
            corner_radius=16,
            fg_color=fg,
            hover_color=hover,
            font=self.FONTS["h2"],
            command=cmd,
        )

        if kind == "border":
            btn.configure(
                border_width=1,
                border_color=self.current_theme["border"],
                text_color=self.current_theme["text"],
            )
        else:
            btn.configure(text_color="white", border_width=0)

        btn.pack(fill="x", padx=18, pady=(6, 16))
        self._themed_primary_buttons.append((btn, kind))
        return btn

    def _link_label(self, parent, text, cmd):
        label = ctk.CTkLabel(
            parent,
            text=text,
            font=self.FONTS["small"],
            text_color=self.current_theme["btn_primary"],
            cursor="hand2",
        )
        label.bind("<Button-1>", lambda _e: cmd())
        return label

    # ==================== FILE BROWSE HELPERS ====================
    def _clear_entry(self, entry):
        entry.delete(0, "end")

    def _browse_save_key(self):
        p = filedialog.asksaveasfilename(
            defaultextension=".key",
            filetypes=[("Key files", "*.key"), ("All files", "*.*")],
            title="Save keypair as",
            initialfile="my_keys.key",
        )
        if p:
            self.keys_save.delete(0, "end")
            self.keys_save.insert(0, p)
            base_name = p[:-4] if p.endswith(".key") else p
            self.keys_public_hint.configure(
                text=f"Public key will be saved as: {os.path.basename(base_name)}_public.key"
            )

    def _browse_enc_file(self):
        p = filedialog.askopenfilename(
            title="Select file to encrypt",
            filetypes=[("All files", "*.*")],
        )
        if p:
            self.enc_file.delete(0, "end")
            self.enc_file.insert(0, p)
            self.enc_output_label.configure(text=f"Output: {p}.vault")

    def _browse_enc_key(self):
        p = filedialog.askopenfilename(
            title="Select recipient public key",
            filetypes=[
                ("Public key files", "*_public.key"),
                ("Key files", "*.key"),
                ("All files", "*.*"),
            ],
        )
        if p:
            self.enc_pub.delete(0, "end")
            self.enc_pub.insert(0, p)

    def _browse_enc_sender_priv_key(self):
        p = filedialog.askopenfilename(
            title="Select your private key for signing",
            filetypes=[("Key files", "*.key"), ("All files", "*.*")],
        )
        if p:
            self.enc_sender_priv.delete(0, "end")
            self.enc_sender_priv.insert(0, p)

    def _browse_dec_file(self):
        p = filedialog.askopenfilename(
            title="Select vault file",
            filetypes=[("Vault files", "*.vault"), ("All files", "*.*")],
        )
        if p:
            self.dec_file.delete(0, "end")
            self.dec_file.insert(0, p)
            out = p.replace(".vault", "")
            if out == p:
                out = p + ".decrypted"
            self.dec_output_label.configure(text=f"Output: {out}")

    def _browse_dec_key(self):
        p = filedialog.askopenfilename(
            title="Select your private key",
            filetypes=[("Key files", "*.key"), ("All files", "*.*")],
        )
        if p:
            self.dec_priv.delete(0, "end")
            self.dec_priv.insert(0, p)

    def _browse_dec_sender_pub_key(self):
        p = filedialog.askopenfilename(
            title="Select sender public key",
            filetypes=[
                ("Public key files", "*_public.key"),
                ("Key files", "*.key"),
                ("All files", "*.*"),
            ],
        )
        if p:
            self.dec_sender_pub.delete(0, "end")
            self.dec_sender_pub.insert(0, p)

    def _browse_info_file(self):
        p = filedialog.askopenfilename(
            title="Select vault file",
            filetypes=[("Vault files", "*.vault"), ("All files", "*.*")],
        )
        if p:
            self.info_file.delete(0, "end")
            self.info_file.insert(0, p)

    # ==================== PASSWORD HELPER ====================
    def _ask_password(self, title, prompt, confirm=False):
        dlg = PasswordModal(self, title=title, prompt=prompt, confirm=confirm)
        self.wait_window(dlg)
        return dlg.value, dlg.confirm_value

    # ==================== UI FEEDBACK HELPERS ====================
    def _toast(self, title, msg, kind="neutral"):
        try:
            Toast(self, title, msg, kind=kind)
        except Exception:
            pass

    def _stamp(self):
        return datetime.now().strftime("%H:%M:%S")

    def _log(self, tag, msg):
        line = f"[{self._stamp()}] {tag}: {msg}\n"
        self.log.configure(state="normal")
        self.log.insert("end", line)
        self.log.see("end")
        self.log.configure(state="disabled")

    def _clear_log(self):
        self.log.configure(state="normal")
        self.log.delete("1.0", "end")
        self.log.configure(state="disabled")
        self._log("SYSTEM", "Activity log cleared.")
        self._toast("Log Cleared", "Activity log has been cleared.", kind="neutral")

    def _set_status(self, text, kind="neutral"):
        color = {
            "neutral": self.current_theme["muted"],
            "success": self.current_theme["success"],
            "warn": self.current_theme["warn"],
            "danger": self.current_theme["danger"],
        }.get(kind, self.current_theme["muted"])
        self.status_label.configure(text=text, text_color=color)

    def _busy(self, on, message="Working…"):
        for btn in [
            getattr(self, "btn_keys", None),
            getattr(self, "btn_enc", None),
            getattr(self, "btn_dec", None),
            getattr(self, "btn_info", None),
        ]:
            if btn is not None:
                btn.configure(state="disabled" if on else "normal")

        if on:
            self.progress.grid()
            self.progress.configure(mode="indeterminate")
            self.progress.start()
            self._set_status(message, kind="neutral")
        else:
            try:
                self.progress.stop()
            except Exception:
                pass
            self.progress.grid_remove()

    # ==================== THEME & HELP ====================
    def _toggle_theme(self):
        if self.theme_switch.get() == 1:
            self.theme_mode = "light"
            ctk.set_appearance_mode("light")
            self.theme_switch.configure(text="Dark mode")
        else:
            self.theme_mode = "dark"
            ctk.set_appearance_mode("dark")
            self.theme_switch.configure(text="Light mode")

        self.current_theme = ThemeManager.get_theme(self.theme_mode)
        self._apply_theme()

    def _apply_theme(self):
        self.configure(fg_color=self.current_theme["bg"])

        self.topbar.configure(fg_color=self.current_theme["panel"], border_color=self.current_theme["border"])
        self.logo_label.configure(text_color=self.current_theme["text"])
        self.subtitle_label.configure(text_color=self.current_theme["muted2"])
        self.help_btn.configure(
            fg_color=self.current_theme["btn_secondary"],
            hover_color=self.current_theme["btn_secondary_hover"],
            border_color=self.current_theme["border"],
            text_color=self.current_theme["text"],
        )

        self.sidebar.configure(fg_color=self.current_theme["panel"], border_color=self.current_theme["border"])
        self.log_title.configure(text_color=self.current_theme["text"])
        self.log_clear_btn.configure(
            fg_color=self.current_theme["btn_secondary"],
            hover_color=self.current_theme["btn_secondary_hover"],
            border_color=self.current_theme["border"],
            text_color=self.current_theme["text"],
        )
        self.log.configure(
            fg_color=self.current_theme["input"],
            border_color=self.current_theme["border"],
            text_color=self.current_theme["text"],
        )
        self.hint.configure(fg_color=self.current_theme["card"])
        self.hint_title.configure(text_color=self.current_theme["text"])
        self.hint_text.configure(text_color=self.current_theme["muted"])
        self.shortcuts_label.configure(text_color=self.current_theme["muted2"])

        self.main.configure(fg_color=self.current_theme["panel2"], border_color=self.current_theme["border"])

        self.statusbar.configure(fg_color=self.current_theme["panel"], border_color=self.current_theme["border"])
        self.status_label.configure(text_color=self.current_theme["muted"])

        for link in [self.docs_link, self.pypi_link, self.linkedin_link, self.github_link]:
            link.configure(text_color=self.current_theme["btn_primary"])

        for lbl in self._themed_muted_labels:
            try:
                lbl.configure(text_color=self.current_theme["muted2"])
            except Exception:
                pass

        for title_lbl, desc_lbl in self._themed_tab_headers:
            try:
                title_lbl.configure(text_color=self.current_theme["text"])
                desc_lbl.configure(text_color=self.current_theme["muted"])
            except Exception:
                pass

        for lbl in self._themed_path_labels:
            try:
                label_color = "#0f172a" if self.theme_mode == "light" else self.current_theme["text"]
                lbl.configure(text_color=label_color)
            except Exception:
                pass

        for c in self._themed_cards:
            try:
                c.configure(fg_color=self.current_theme["panel"], border_color=self.current_theme["border"])
            except Exception:
                pass

        for e in self._themed_entries:
            try:
                e.configure(
                    fg_color=self.current_theme["input"],
                    border_color=self.current_theme["border"],
                    text_color=self.current_theme["text"],
                )
            except Exception:
                pass

        for b in self._themed_secondary_buttons:
            try:
                b.configure(
                    fg_color=self.current_theme["btn_secondary"],
                    hover_color=self.current_theme["btn_secondary_hover"],
                    border_color=self.current_theme["border"],
                    text_color=self.current_theme["text"],
                )
            except Exception:
                pass

        for tv in self._themed_tabviews:
            try:
                tv.configure(
                    fg_color=self.current_theme["panel2"],
                    segmented_button_fg_color=self.current_theme["card"],
                    segmented_button_selected_color=self.current_theme["btn_primary"],
                    segmented_button_selected_hover_color=self.current_theme["btn_primary_hover"],
                    segmented_button_unselected_color=self.current_theme["card"],
                )
            except Exception:
                pass

        try:
            if self._themed_tab_buttons:
                self._themed_tab_buttons.configure(text_color=self.current_theme["text"])
        except Exception:
            pass

        # Repaint primary buttons including border style
        for btn, kind in self._themed_primary_buttons:
            try:
                palette = {
                    "primary": (self.current_theme["btn_primary"], self.current_theme["btn_primary_hover"]),
                    "success": (self.current_theme["success"], "#16a34a"),
                    "warn": (self.current_theme["warn"], "#d97706"),
                    "neutral": (self.current_theme["neutral"], "#64748b"),
                    "border": (self.current_theme["btn_secondary"], self.current_theme["btn_secondary_hover"]),
                }
                fg, hover = palette.get(kind, palette["primary"])
                btn.configure(fg_color=fg, hover_color=hover)

                if kind == "border":
                    btn.configure(
                        border_width=1,
                        border_color=self.current_theme["border"],
                        text_color=self.current_theme["text"],
                    )
                else:
                    btn.configure(border_width=0, text_color="white")
            except Exception:
                pass

    def _show_help(self):
        HelpModal(self)

    def _on_close(self):
        """
        FIX #2: show thank-you on exit if user performed ANY action
        (Keys OR Encrypt OR Decrypt OR Info).
        """
        try:
            if self._did_any_action and not self._thank_on_close_shown:
                self._thank_on_close_shown = True

                dlg = SuccessModal(
                    self,
                    title="Thank you!",
                    message="Thank you for using SecureVault; Mégane Alexis",
                    icon="✓",
                    kind="neutral",
                    # size=(350, 380),
                    size=(380, 380),
                )
                self.wait_window(dlg)

            self.destroy()
        except Exception:
            try:
                self.destroy()
            except Exception:
                pass

    def _open_linkedin(self):
        self._open_url("https://www.linkedin.com/in/megane-alexis/")

    def _open_docs(self):
        self._open_url("https://meganealexis.com")

    def _open_github(self):
        self._open_url("https://github.com/megane18")

    def _open_pypi(self):
        self._open_url("https://pypi.org/project/securevault-pqc/")

    # ==================== CRYPTO ACTIONS ====================
    def generate_keys(self):
        save_path = (self.keys_save.get() or "").strip()
        if not save_path:
            self._toast("Keys", "Please select an output path first.", kind="warn")
            self._set_status("Cancelled: no output path selected.", kind="warn")
            self._log("KEYS", "Cancelled; no output path.")
            return

        password, confirm = self._ask_password(
            title="Generate Keys",
            prompt="Create a strong password for your private key.",
            confirm=True,
        )

        if not password:
            self._toast("Keys", "Cancelled.", kind="warn")
            self._log("KEYS", "Cancelled; no password entered.")
            return

        if password != confirm:
            self._toast("Keys", "Passwords do not match.", kind="danger")
            self._log("KEYS", "Error; password mismatch.")
            ErrorModal(
                self,
                "Password Mismatch",
                "The passwords you entered do not match.\nPlease try again.",
            )
            return

        try:
            self._busy(True, "Generating quantum-resistant keypair…")

            if not save_path.endswith(".key"):
                save_path = save_path + ".key"

            self._log("KEYS", f"Generating keypair: {save_path}")

            keypair = securevault.generate_hybrid_keypair()
            securevault.save_keypair_to_file(keypair, save_path, password)

            base_name = save_path[:-4] if save_path.endswith(".key") else save_path
            public_path = base_name + "_public.key"

            public_only = {
                "private_key": b"",
                "public_key": keypair["public_key"],
                "pqc_private_key": b"",
                "pqc_public_key": keypair["pqc_public_key"],
                "signing_private_key": b"",
                "signing_public_key": keypair["signing_public_key"],
                "ml_dsa_65_private_key": b"",
                "ml_dsa_65_public_key": keypair["ml_dsa_65_public_key"],
                "algorithm": keypair["algorithm"],
                "signing_algorithm": keypair.get("signing_algorithm", "Ed25519+ML-DSA-65"),
            }
            securevault.save_keypair_to_file(public_only, public_path, None)

            self._keys_generated_this_session = True
            self._did_any_action = True  # FIX: any action happened

            self._log("KEYS", f"✓ Private: {os.path.basename(save_path)}")
            self._log("KEYS", f"✓ Public: {os.path.basename(public_path)}")
            self._set_status("Success: keypair generated.", kind="success")
            self._toast("Keys", "Keypair generated successfully.", kind="success")

            SuccessModal(
                self,
                title="Keypair Generated",
                message=(
                    f"Private: {os.path.basename(save_path)}\n"
                    f"Public: {os.path.basename(public_path)}\n\n"
                    "Keep private key secure.\nShare public key freely."
                ),
                footer_text="Thank you for using SecureVault; Mégane Alexis",
                icon="✓",
                kind="success",
                size=(620, 380),
            )

        except Exception as e:
            details = self._format_exception(e)
            user_msg = self._get_user_friendly_error(e)
            self._log("KEYS", "✗ Error occurred (details below).")
            self._log("KEYS", details)
            self._set_status(f"Error: {type(e).__name__}", kind="danger")
            self._toast("Keys", f"Key generation failed: {type(e).__name__}", kind="danger")
            ErrorModal(self, "Key Generation Failed", details[:1800], user_msg)
        finally:
            self._busy(False)

    def encrypt_file(self):
        file_path = (self.enc_file.get() or "").strip()
        key_path = (self.enc_pub.get() or "").strip()

        if not file_path:
            self._toast("Encrypt", "Please select a file to encrypt.", kind="warn")
            self._log("ENCRYPT", "Cancelled; no file selected.")
            return

        if not key_path:
            self._toast("Encrypt", "Please select recipient public key.", kind="warn")
            self._log("ENCRYPT", "Cancelled; no recipient key.")
            return

        sender_priv_path = (self.enc_sender_priv.get() or "").strip()
        if not sender_priv_path:
            self._toast("Encrypt", "Please select your private key for signing.", kind="warn")
            self._log("ENCRYPT", "Cancelled; no sender private key for signing.")
            return

        sender_pw, _ = self._ask_password(
            title="Encrypt File",
            prompt="Enter your private key password (used to sign).",
            confirm=False,
        )
        if not sender_pw:
            self._toast("Encrypt", "Cancelled.", kind="warn")
            self._log("ENCRYPT", "Cancelled; no sender password.")
            return

        try:
            self._busy(True, "Encrypting with hybrid post-quantum crypto…")

            self._log("ENCRYPT", f"File: {os.path.basename(file_path)}")
            self._log("ENCRYPT", f"Recipient key: {os.path.basename(key_path)}")
            self._log("ENCRYPT", f"Signing key: {os.path.basename(sender_priv_path)}")

            recipient_keys = securevault.load_keypair_from_file(key_path, None)
            recipient_public_keys = {
                "classical_public_key": recipient_keys["public_key"],
                "pqc_public_key": recipient_keys["pqc_public_key"],
            }

            sender_keys = securevault.load_keypair_from_file(sender_priv_path, sender_pw)

            output_path = file_path + ".vault"

            securevault.hybrid_encrypt_file(
                file_path,
                output_path,
                recipient_public_keys,
                sender_ed25519_signing_private_key=sender_keys["signing_private_key"],
                sender_ml_dsa_65_signing_private_key=sender_keys["ml_dsa_65_private_key"],
            )

            self.enc_output_label.configure(text=f"Output: {output_path}")
            self._log("ENCRYPT", f"✓ Output: {os.path.basename(output_path)}")
            self._set_status(f"Success: encrypted to {os.path.basename(output_path)}", kind="success")
            self._toast("Encrypt", "Encryption successful.", kind="success")

            self._did_any_action = True  # FIX: any action happened

            SuccessModal(
                self,
                title="File Encrypted",
                message=(
                    f"Encrypted: {os.path.basename(output_path)}\n\n"
                    "Protected by:\n"
                    "X25519 (Classical)\n"
                    "ML-KEM-768 (Post-Quantum)\n\n"
                    "Signed by:\n"
                    "Ed25519 + ML-DSA-65"
                ),
                icon="✓",
                kind="success",
                size=(620, 420),
            )

        except Exception as e:
            details = self._format_exception(e)
            user_msg = self._get_user_friendly_error(e)
            self._log("ENCRYPT", "✗ Error occurred (details below).")
            self._log("ENCRYPT", details)
            self._set_status(f"Error: {type(e).__name__}", kind="danger")
            self._toast("Encrypt", f"Encryption failed: {type(e).__name__}", kind="danger")
            ErrorModal(self, "Encryption Failed", details[:1800], user_msg)
        finally:
            self._busy(False)

    def decrypt_file(self):
        file_path = (self.dec_file.get() or "").strip()
        key_path = (self.dec_priv.get() or "").strip()

        if not file_path:
            self._toast("Decrypt", "Please select a vault file.", kind="warn")
            self._log("DECRYPT", "Cancelled; no vault file.")
            return

        if not key_path:
            self._toast("Decrypt", "Please select your private key.", kind="warn")
            self._log("DECRYPT", "Cancelled; no private key.")
            return

        sender_pub_path = (self.dec_sender_pub.get() or "").strip()
        if not sender_pub_path:
            self._toast("Decrypt", "Please select the sender public key.", kind="warn")
            self._log("DECRYPT", "Cancelled; no sender public key.")
            return

        password, _ = self._ask_password(
            title="Decrypt File",
            prompt="Enter your private key password.",
            confirm=False,
        )

        if not password:
            self._toast("Decrypt", "Cancelled.", kind="warn")
            self._log("DECRYPT", "Cancelled; no password.")
            return

        try:
            self._busy(True, "Decrypting with hybrid verification…")

            self._log("DECRYPT", f"Vault: {os.path.basename(file_path)}")
            self._log("DECRYPT", f"Private key: {os.path.basename(key_path)}")
            self._log("DECRYPT", f"Sender pubkey: {os.path.basename(sender_pub_path)}")

            private_keys = securevault.load_keypair_from_file(key_path, password)
            sender_keys = securevault.load_keypair_from_file(sender_pub_path, password=None)

            output_path = file_path.replace(".vault", "")
            if output_path == file_path:
                output_path = file_path + ".decrypted"

            securevault.hybrid_decrypt_file(
                file_path,
                output_path,
                private_keys,
                sender_ed25519_signing_public_key=sender_keys["signing_public_key"],
                sender_ml_dsa_65_signing_public_key=sender_keys["ml_dsa_65_public_key"],
            )

            self.dec_output_label.configure(text=f"Output: {output_path}")
            self._log("DECRYPT", f"✓ Output: {os.path.basename(output_path)}")
            self._set_status(f"Success: decrypted to {os.path.basename(output_path)}", kind="success")
            self._toast("Decrypt", "Decryption successful.", kind="success")

            self._did_any_action = True  # FIX: any action happened

            SuccessModal(
                self,
                title="File Decrypted",
                message=(f"Decrypted: {os.path.basename(output_path)}\n\nVerification: OK (Ed25519 + ML-DSA-65)"),
                icon="✓",
                kind="success",
                size=(620, 360),
            )

        except Exception as e:
            details = self._format_exception(e)
            user_msg = self._get_user_friendly_error(e)
            self._log("DECRYPT", "✗ Error occurred (details below).")
            self._log("DECRYPT", details)
            self._set_status(f"Error: {type(e).__name__}", kind="danger")
            self._toast("Decrypt", f"Decryption failed: {type(e).__name__}", kind="danger")
            ErrorModal(self, "Decryption Failed", details[:1800], user_msg)
        finally:
            self._busy(False)

    def file_info(self):
        file_path = (self.info_file.get() or "").strip()

        if not file_path:
            self._toast("Info", "Please select a vault file.", kind="warn")
            self._log("INFO", "Cancelled; no file selected.")
            return

        try:
            self._busy(True, "Reading vault metadata…")

            self._log("INFO", f"Reading: {os.path.basename(file_path)}")

            with open(file_path, "r") as f:
                package = json.load(f)

            encrypted_size = len(base64.b64decode(package["encrypted_data"]))
            total_size = os.path.getsize(file_path)
            algo = package.get("algorithm", "unknown")
            signing_algo = package.get("signing_algorithm", "unknown")

            self._log("INFO", f"Algorithm: {algo}")
            self._log("INFO", f"Signing: {signing_algo}")
            self._log("INFO", f"Payload: {encrypted_size:,} bytes")
            self._set_status("Success: vault info loaded.", kind="success")
            self._toast("Info", "Vault info loaded.", kind="success")

            self._did_any_action = True  # FIX: any action happened

            info_text = (
                f"File: {os.path.basename(file_path)}\n"
                f"Size: {total_size:,} bytes ({total_size/1024:.1f} KB)\n"
                f"Encrypted data: {encrypted_size:,} bytes\n\n"
                f"Algorithm: {algo}\n"
                f"Signing: {signing_algo}\n\n"
                "Protection:\n"
                "Classical: X25519\n"
                "Post-Quantum: ML-KEM-768\n\n"
                "Quantum Resistant: YES"
            )

            SuccessModal(
                self,
                title="Vault Information",
                message=info_text,
                icon="i",
                kind="neutral",
                size=(640, 460),
            )

        except Exception as e:
            details = self._format_exception(e)
            user_msg = self._get_user_friendly_error(e)
            self._log("INFO", "✗ Error occurred (details below).")
            self._log("INFO", details)
            self._set_status(f"Error: {type(e).__name__}", kind="danger")
            self._toast("Info", f"Failed to read vault info: {type(e).__name__}", kind="danger")
            ErrorModal(self, "Failed to Read Vault", details[:1800], user_msg)
        finally:
            self._busy(False)


def main():
    app = SecureVaultGUI()
    app.mainloop()


if __name__ == "__main__":
    main()
