"""Composants UI stylés de la GUI V6 (G4), alignés sur la maquette.

Chaque helper reçoit une ``palette`` (cf. theme.py) et applique les couleurs
correspondantes à des widgets customtkinter. Aucune logique métier ici.
Les widgets ne sont créés qu'à l'appel (import sûr pour ``--self-test``).
"""

from __future__ import annotations

import tkinter as tk
from typing import Optional

import customtkinter as ctk

CARD_RADIUS = 8


def font(size: int = 13, weight: str = "normal") -> "ctk.CTkFont":
    return ctk.CTkFont(size=size, weight=weight)


class Card(ctk.CTkFrame):
    """Carte maquette : fond `card`, bordure `card_border`, titre uppercase optionnel."""

    def __init__(
        self,
        master,
        palette: dict,
        title: Optional[str] = None,
        help_text: Optional[str] = None,
        help_title: Optional[str] = None,
        **kwargs,
    ):
        super().__init__(
            master,
            fg_color=palette["card"],
            border_color=palette["card_border"],
            border_width=1,
            corner_radius=CARD_RADIUS,
            **kwargs,
        )
        self._palette = palette
        self.body = self  # alias pour clarté
        if title:
            header = ctk.CTkFrame(self, fg_color="transparent")
            header.pack(fill="x", padx=16, pady=(14, 8))
            ctk.CTkLabel(
                header,
                text=title.upper(),
                text_color=palette["text_dim"],
                font=font(11, "bold"),
                anchor="w",
            ).pack(side="left", fill="x", expand=True)
            if help_text:
                HelpButton(
                    header,
                    palette,
                    help_text,
                    title=help_title or title.lstrip("⚙️👤🏷️⬛🧠📝🛡️🔍✅📤📥 ").strip() or "Aide",
                ).pack(side="right", padx=(8, 0))


def primary_button(master, palette: dict, text: str, command=None, large: bool = False):
    return ctk.CTkButton(
        master,
        text=text,
        command=command,
        fg_color=palette["primary"],
        hover_color=palette["primary_dim"],
        text_color="#ffffff",
        corner_radius=CARD_RADIUS,
        height=38 if large else 32,
        font=font(14 if large else 13, "bold"),
    )


def secondary_button(master, palette: dict, text: str, command=None):
    return ctk.CTkButton(
        master,
        text=text,
        command=command,
        fg_color=palette["btn_sec_bg"],
        hover_color=palette["card_border"],
        text_color=palette["text"],
        border_color=palette["btn_sec_border"],
        border_width=1,
        corner_radius=CARD_RADIUS,
        height=32,
        font=font(13),
    )


def success_button(master, palette: dict, text: str, command=None):
    return ctk.CTkButton(
        master,
        text=text,
        command=command,
        fg_color=palette["success"],
        hover_color=palette["success"],
        text_color="#ffffff",
        corner_radius=CARD_RADIUS,
        height=32,
        font=font(13, "bold"),
    )


def pill_button(master, palette: dict, text: str, command=None, active: bool = False):
    """Bouton pilule (sélecteur de thème / sous-onglet)."""
    return ctk.CTkButton(
        master,
        text=text,
        command=command,
        fg_color=palette["primary"] if active else "transparent",
        hover_color=palette["primary_dim"],
        text_color="#ffffff" if active else palette["text_dim"],
        border_color=palette["card_border"] if not active else palette["primary"],
        border_width=2,
        corner_radius=99,
        height=30,
        font=font(12, "bold" if active else "normal"),
    )


class StatCard(ctk.CTkFrame):
    """Carte statistique (rgrid/sc) : grande valeur + label."""

    def __init__(self, master, palette: dict, value: str, label: str, value_color: Optional[str] = None, **kwargs):
        super().__init__(
            master,
            fg_color=palette["btn_sec_bg"],
            border_color=palette["btn_sec_border"],
            border_width=1,
            corner_radius=CARD_RADIUS,
            **kwargs,
        )
        ctk.CTkLabel(
            self, text=value, text_color=value_color or palette["primary"], font=font(22, "bold")
        ).pack(pady=(10, 0))
        ctk.CTkLabel(self, text=label, text_color=palette["text_muted"], font=font(11)).pack(
            pady=(0, 10)
        )


class ToggleRow(ctk.CTkFrame):
    """Ligne de réglage (srow) : libellé + indice + interrupteur."""

    def __init__(self, master, palette: dict, label: str, hint: str = "", value: bool = True, command=None, **kwargs):
        super().__init__(master, fg_color="transparent", **kwargs)
        left = ctk.CTkFrame(self, fg_color="transparent")
        left.pack(side="left", fill="x", expand=True)
        ctk.CTkLabel(left, text=label, text_color=palette["text"], font=font(13), anchor="w").pack(anchor="w")
        if hint:
            ctk.CTkLabel(left, text=hint, text_color=palette["text_muted"], font=font(11), anchor="w").pack(anchor="w")
        self.var = ctk.BooleanVar(value=value)
        self.switch = ctk.CTkSwitch(
            self,
            text="",
            variable=self.var,
            command=command,
            progress_color=palette["primary"],
            width=44,
        )
        self.switch.pack(side="right", padx=(8, 0))

    def get(self) -> bool:
        return bool(self.var.get())


class HelpButton(ctk.CTkButton):
    """Petit bouton « ? » ouvrant une fenêtre d'aide en français simple.

    Restaure l'affordance d'aide de la V5 (``ToolTip`` / « Comment ça marche ? »)
    pour les utilisateurs non informaticiens.
    """

    def __init__(self, master, palette: dict, text: str, *, title: str = "Aide", **kwargs):
        self._palette = palette
        self._help_text = text
        self._help_title = title
        self._window = None
        super().__init__(
            master,
            text="?",
            command=self.open_help,
            width=26,
            height=26,
            corner_radius=13,
            fg_color=palette["btn_sec_bg"],
            hover_color=palette["card_border"],
            text_color=palette["text_dim"],
            border_color=palette["btn_sec_border"],
            border_width=1,
            font=font(13, "bold"),
            **kwargs,
        )

    def open_help(self):
        if self._window is not None:
            try:
                if self._window.winfo_exists():
                    self._window.lift()
                    self._window.focus_force()
                    return self._window
            except Exception:
                pass
        p = self._palette
        win = ctk.CTkToplevel(self)
        win.title(self._help_title)
        win.geometry("480x380")
        win.minsize(360, 240)
        try:
            win.configure(fg_color=p["bg"])
        except Exception:
            pass
        ctk.CTkLabel(
            win, text=self._help_title, text_color=p["text"], font=font(15, "bold"), anchor="w"
        ).pack(fill="x", padx=16, pady=(14, 4))
        box = ctk.CTkScrollableFrame(win, fg_color=p["card"])
        box.pack(fill="both", expand=True, padx=12, pady=(0, 8))
        ctk.CTkLabel(
            box,
            text=self._help_text,
            text_color=p["text_dim"],
            font=font(12),
            justify="left",
            wraplength=420,
            anchor="w",
        ).pack(fill="x", padx=10, pady=10)
        ctk.CTkButton(
            win,
            text="Fermer",
            command=win.destroy,
            fg_color=p["btn_sec_bg"],
            hover_color=p["card_border"],
            text_color=p["text"],
            border_color=p["btn_sec_border"],
            border_width=1,
            corner_radius=CARD_RADIUS,
            height=30,
        ).pack(padx=12, pady=(0, 12))
        try:
            win.transient(self.winfo_toplevel())
            win.after(120, lambda: (win.lift(), win.focus_force()))
        except Exception:
            pass
        self._window = win
        return win


def help_button(master, palette: dict, text: str, title: str = "Aide") -> "HelpButton":
    return HelpButton(master, palette, text, title=title)


class Tooltip:
    """Infobulle au survol (façon V5 ``ToolTip``), pour les éléments ambigus."""

    def __init__(self, widget, text: str, delay: int = 450):
        self.widget = widget
        self.text = text
        self.delay = delay
        self._tip = None
        self._after = None
        widget.bind("<Enter>", self._schedule, add="+")
        widget.bind("<Leave>", self.hide, add="+")
        widget.bind("<ButtonPress>", self.hide, add="+")

    def _schedule(self, *_):
        self._cancel()
        try:
            self._after = self.widget.after(self.delay, self.show)
        except Exception:
            pass

    def _cancel(self):
        if self._after is not None:
            try:
                self.widget.after_cancel(self._after)
            except Exception:
                pass
            self._after = None

    def show(self, *_):
        if self._tip is not None or not self.text:
            return self._tip
        try:
            x = self.widget.winfo_rootx() + 16
            y = self.widget.winfo_rooty() + self.widget.winfo_height() + 4
        except Exception:
            return None
        self._tip = tw = tk.Toplevel(self.widget)
        tw.wm_overrideredirect(True)
        tw.wm_geometry(f"+{x}+{y}")
        tk.Label(
            tw,
            text=self.text,
            justify="left",
            background="#1f2937",
            foreground="#f9fafb",
            relief="solid",
            borderwidth=1,
            wraplength=320,
            padx=8,
            pady=5,
        ).pack()
        return tw

    def hide(self, *_):
        self._cancel()
        if self._tip is not None:
            try:
                self._tip.destroy()
            except Exception:
                pass
            self._tip = None


def attach_tooltip(widget, text: str, delay: int = 450) -> "Tooltip":
    return Tooltip(widget, text, delay)
