"""
Dialogo de autorizacion mediante PIN de supervisor.
Se usa para operaciones restringidas: cortesia, anulacion, reprogramacion.

Flujo:
  Paso 1 (si requiere motivo):
    - Cajero escribe el motivo/justificacion.
    - Teclado fisico solo va al campo de texto.
    - Enter o boton "Continuar" pasa al Paso 2.

  Paso 2:
    - Supervisor ingresa PIN (teclado numerico visual o fisico).
    - No hay campo de texto — numeros van solo al PIN.
    - Se llama POST /api/supervisor/validar-pin.
    - Si valido  -> animacion de exito -> on_result(True).
    - Si invalido -> muestra error, permite reintentar.
    - Si cancela  -> on_result(False).

Diseño: popup flotante always-on-top, sin bordes del OS.
"""
import threading
import customtkinter as ctk
from typing import Callable, Optional
import api
import session
from ui.kb_shortcuts import bind_entry

# ---------------------------------------------------------------------------
# Colores ICEBERG
# ---------------------------------------------------------------------------
_BG_CARD     = "#0f1228"
_ACCENT      = "#00d4ff"
_ACCENT_DIM  = "#0a4a6a"
_BTN_NUM     = "#1a2440"
_BTN_NUM_HV  = "#243050"
_BTN_DEL     = "#3a2020"
_BTN_DEL_HV  = "#4a2828"
_BTN_OK      = "#0a5a7a"
_BTN_OK_HV   = "#00d4ff"
_TEXT_DIM    = "#666677"
_TEXT_MID    = "#9999aa"
_ERR_RED     = "#f44336"
_OK_GREEN    = "#4caf50"


# ---------------------------------------------------------------------------
# Dialogo
# ---------------------------------------------------------------------------

class SupervisorPinDialog(ctk.CTkToplevel):
    """
    Popup flotante always-on-top de autorizacion de supervisor (2 pasos).
    on_result(True)  si el PIN es valido.
    on_result(False) si se cancela o falla.
    """

    def __init__(
        self,
        parent,
        reason: str = "Operacion restringida",
        accion: str = "cortesia",
        on_result: Optional[Callable[[bool], None]] = None,
        required_permiso: str | None = None,
    ):
        super().__init__(parent)
        self._on_result        = on_result
        self._accion           = accion
        self._reason           = reason
        self._pin: list        = []
        self._result_sent      = False
        self._waiting          = False
        self._motivo_text      = ""
        self._required_permiso = required_permiso
        self._motivo_required  = required_permiso is None
        self._atribucion       = "error_cajero"
        self._cajero           = ""
        self._supervisor_name  = ""
        self._supervisor_id    = 0

        # Ventana sin bordes del OS, siempre encima
        self.overrideredirect(True)
        self.attributes("-topmost", True)
        self.configure(fg_color=_BG_CARD)

        # Tamaño y posicion centrada sobre el padre
        self._win_w = 440
        self._win_h = 520 if self._motivo_required else 480
        self.geometry(f"{self._win_w}x{self._win_h}")
        self._center_on_parent(parent)

        # Grab modal
        self.grab_set()
        self.focus_force()

        # Card principal (es la propia ventana, pero agregamos borde visual)
        self._card = ctk.CTkFrame(
            self,
            fg_color=_BG_CARD,
            corner_radius=0,
            border_width=2,
            border_color=_ACCENT_DIM,
        )
        self._card.pack(fill="both", expand=True)

        # Bind Escape para cancelar
        self.bind("<Escape>", lambda e: self._cancel())

        # Permitir mover la ventana arrastrando
        self._drag_data = {"x": 0, "y": 0}
        self._card.bind("<Button-1>", self._start_drag)
        self._card.bind("<B1-Motion>", self._do_drag)

        # Iniciar en el paso correcto
        if self._motivo_required:
            self._build_step_motivo()
        else:
            self._build_step_pin()

    def _center_on_parent(self, parent):
        """Centra la ventana sobre el padre."""
        self.update_idletasks()
        px = parent.winfo_rootx() + parent.winfo_width() // 2
        py = parent.winfo_rooty() + parent.winfo_height() // 2
        x = px - self._win_w // 2
        y = py - self._win_h // 2
        self.geometry(f"{self._win_w}x{self._win_h}+{x}+{y}")

    def _start_drag(self, event):
        self._drag_data["x"] = event.x
        self._drag_data["y"] = event.y

    def _do_drag(self, event):
        x = self.winfo_x() + (event.x - self._drag_data["x"])
        y = self.winfo_y() + (event.y - self._drag_data["y"])
        self.geometry(f"+{x}+{y}")

    # ------------------------------------------------------------------
    # PASO 1: Motivo
    # ------------------------------------------------------------------

    def _build_step_motivo(self):
        for w in self._card.winfo_children():
            w.destroy()

        self._win_h = 600
        self.geometry(f"{self._win_w}x{self._win_h}")

        # Barra de acento superior
        ctk.CTkFrame(
            self._card, height=4, fg_color=_ACCENT, corner_radius=2,
        ).pack(fill="x", padx=40, pady=(20, 0))

        # Icono candado
        ctk.CTkLabel(
            self._card, text="\U0001F512",
            font=ctk.CTkFont(size=36),
        ).pack(pady=(16, 4))

        # Titulo
        ctk.CTkLabel(
            self._card,
            text="Autorizacion Requerida",
            font=ctk.CTkFont(size=18, weight="bold"),
            text_color=_ACCENT,
        ).pack(pady=(0, 2))

        # Razon
        ctk.CTkLabel(
            self._card,
            text=self._reason,
            font=ctk.CTkFont(size=11),
            text_color=_TEXT_MID,
            wraplength=360,
            justify="center",
        ).pack(pady=(0, 12))

        # Separador
        ctk.CTkFrame(
            self._card, height=1, fg_color="#1a2a3a",
        ).pack(fill="x", padx=50, pady=(0, 16))

        # Label motivo
        ctk.CTkLabel(
            self._card,
            text="Motivo / Justificacion:",
            font=ctk.CTkFont(size=12),
            text_color=_TEXT_MID,
            anchor="w",
        ).pack(anchor="w", padx=50, pady=(0, 4))

        # Entry motivo
        self._entry_motivo = ctk.CTkEntry(
            self._card,
            width=340, height=40,
            font=ctk.CTkFont(size=13),
            placeholder_text="Ej: Cliente premiado por evento...",
            corner_radius=10,
            border_color=_ACCENT_DIM,
            fg_color="#0a1020",
        )
        self._entry_motivo.pack(padx=50, pady=(0, 6))
        bind_entry(self._entry_motivo)

        # Restaurar texto si vuelve del paso 2
        if self._motivo_text:
            self._entry_motivo.insert(0, self._motivo_text)

        # Separador antes de atribucion
        ctk.CTkFrame(
            self._card, height=1, fg_color="#1a2a3a",
        ).pack(fill="x", padx=50, pady=(8, 8))

        # Label atribucion
        ctk.CTkLabel(
            self._card,
            text="Responsabilidad:",
            font=ctk.CTkFont(size=12),
            text_color=_TEXT_MID,
            anchor="w",
        ).pack(anchor="w", padx=50, pady=(0, 4))

        # Radio buttons de atribucion
        self._attr_var = ctk.StringVar(value=self._atribucion)
        attr_frame = ctk.CTkFrame(self._card, fg_color="transparent")
        attr_frame.pack(anchor="w", padx=50, pady=(0, 4))

        ctk.CTkRadioButton(
            attr_frame,
            text="Error del cajero",
            variable=self._attr_var,
            value="error_cajero",
            font=ctk.CTkFont(size=12),
            text_color="#c0d0e0",
            fg_color=_ACCENT,
            hover_color=_ACCENT_DIM,
            border_color=_ACCENT_DIM,
        ).pack(anchor="w", pady=2)

        ctk.CTkRadioButton(
            attr_frame,
            text="Decision del supervisor",
            variable=self._attr_var,
            value="decision_supervisor",
            font=ctk.CTkFont(size=12),
            text_color="#c0d0e0",
            fg_color=_ACCENT,
            hover_color=_ACCENT_DIM,
            border_color=_ACCENT_DIM,
        ).pack(anchor="w", pady=2)

        # Status (errores)
        self._lbl_status = ctk.CTkLabel(
            self._card, text="",
            font=ctk.CTkFont(size=11),
            text_color=_ERR_RED,
            wraplength=340,
            justify="center",
        )
        self._lbl_status.pack(pady=(4, 4))

        # Boton Continuar
        ctk.CTkButton(
            self._card,
            text="CONTINUAR",
            width=340, height=46,
            corner_radius=10,
            fg_color=_ACCENT,
            hover_color="#009ac4",
            text_color="#000000",
            font=ctk.CTkFont(size=14, weight="bold"),
            command=self._on_motivo_continue,
        ).pack(padx=50, pady=(4, 4))

        # Boton Cancelar
        ctk.CTkButton(
            self._card,
            text="Cancelar",
            width=120, height=34,
            corner_radius=8,
            fg_color="transparent",
            hover_color="#1a1a2e",
            text_color=_TEXT_DIM,
            font=ctk.CTkFont(size=12),
            command=self._cancel,
        ).pack(pady=(2, 16))

        # Bindings
        self._entry_motivo.bind("<Return>", lambda e: self._on_motivo_continue())
        self.after(100, self._entry_motivo.focus_set)

    def _on_motivo_continue(self):
        motivo = self._entry_motivo.get().strip()
        if not motivo:
            self._lbl_status.configure(text="El motivo es obligatorio.")
            self._entry_motivo.focus_set()
            return
        self._motivo_text = motivo
        self._atribucion = self._attr_var.get()
        # Flash de transicion
        self._card.configure(border_color=_ACCENT)
        self.after(120, lambda: self._card.configure(border_color=_ACCENT_DIM))
        self.after(80, self._build_step_pin)

    # ------------------------------------------------------------------
    # PASO 2: PIN
    # ------------------------------------------------------------------

    def _build_step_pin(self):
        for w in self._card.winfo_children():
            w.destroy()

        self._win_h = 540
        self.geometry(f"{self._win_w}x{self._win_h}")

        # Barra de acento superior
        ctk.CTkFrame(
            self._card, height=4, fg_color=_ACCENT, corner_radius=2,
        ).pack(fill="x", padx=40, pady=(20, 0))

        # Icono candado
        ctk.CTkLabel(
            self._card, text="\U0001F512",
            font=ctk.CTkFont(size=36),
        ).pack(pady=(16, 4))

        # Titulo
        ctk.CTkLabel(
            self._card,
            text="Ingrese PIN del Supervisor",
            font=ctk.CTkFont(size=18, weight="bold"),
            text_color=_ACCENT,
        ).pack(pady=(0, 2))

        # Razon
        ctk.CTkLabel(
            self._card,
            text=self._reason,
            font=ctk.CTkFont(size=11),
            text_color=_TEXT_MID,
            wraplength=360,
            justify="center",
        ).pack(pady=(0, 14))

        # Display del PIN (puntos)
        pin_frame = ctk.CTkFrame(
            self._card,
            fg_color="#0a1020",
            corner_radius=10,
            border_width=1,
            border_color=_ACCENT_DIM,
            height=52,
            width=220,
        )
        pin_frame.pack(pady=(0, 12))
        pin_frame.pack_propagate(False)

        self._display = ctk.CTkLabel(
            pin_frame,
            text="",
            font=ctk.CTkFont(size=28, family="Consolas"),
            text_color=_ACCENT,
            anchor="center",
        )
        self._display.pack(expand=True)
        self._update_display()

        # Teclado numerico
        keypad = ctk.CTkFrame(self._card, fg_color="transparent")
        keypad.pack(pady=(0, 6))

        layout = [
            ["7", "8", "9"],
            ["4", "5", "6"],
            ["1", "2", "3"],
            ["\u232b", "0", "\u2713"],
        ]
        for row_keys in layout:
            row_frame = ctk.CTkFrame(keypad, fg_color="transparent")
            row_frame.pack()
            for k in row_keys:
                if k == "\u2713":  # checkmark
                    btn = ctk.CTkButton(
                        row_frame, text=k,
                        width=82, height=56, corner_radius=10,
                        fg_color=_BTN_OK, hover_color=_BTN_OK_HV,
                        font=ctk.CTkFont(size=22, weight="bold"),
                        text_color="#ffffff",
                        command=self._confirm,
                    )
                elif k == "\u232b":  # backspace
                    btn = ctk.CTkButton(
                        row_frame, text=k,
                        width=82, height=56, corner_radius=10,
                        fg_color=_BTN_DEL, hover_color=_BTN_DEL_HV,
                        font=ctk.CTkFont(size=20),
                        text_color="#ff6b6b",
                        command=self._backspace,
                    )
                else:
                    btn = ctk.CTkButton(
                        row_frame, text=k,
                        width=82, height=56, corner_radius=10,
                        fg_color=_BTN_NUM, hover_color=_BTN_NUM_HV,
                        font=ctk.CTkFont(size=20, weight="bold"),
                        text_color="#c0d0e0",
                        command=lambda d=k: self._press(d),
                    )
                btn.pack(side="left", padx=4, pady=4)

        # Estado / error
        self._lbl_status = ctk.CTkLabel(
            self._card, text="",
            font=ctk.CTkFont(size=12),
            text_color=_ERR_RED,
            wraplength=340,
            justify="center",
            height=20,
        )
        self._lbl_status.pack(pady=(2, 2))

        # Botones inferiores
        bottom = ctk.CTkFrame(self._card, fg_color="transparent")
        bottom.pack(pady=(0, 16))

        if self._motivo_required:
            ctk.CTkButton(
                bottom,
                text="\u2190 Volver",
                width=100, height=32,
                corner_radius=8,
                fg_color="transparent",
                hover_color="#1a1a2e",
                text_color=_TEXT_MID,
                font=ctk.CTkFont(size=12),
                command=self._go_back_motivo,
            ).pack(side="left", padx=(0, 16))

        ctk.CTkButton(
            bottom,
            text="Cancelar",
            width=100, height=32,
            corner_radius=8,
            fg_color="transparent",
            hover_color="#1a1a2e",
            text_color=_TEXT_DIM,
            font=ctk.CTkFont(size=12),
            command=self._cancel,
        ).pack(side="left")

        # Keyboard bindings
        self.bind("<Key>", self._key_press_pin)
        self.focus_force()

    def _go_back_motivo(self):
        """Volver al paso 1 preservando el texto del motivo."""
        self.unbind("<Key>")
        self._card.configure(border_color=_ACCENT)
        self.after(120, lambda: self._card.configure(border_color=_ACCENT_DIM))
        self.after(80, self._build_step_motivo)

    # ------------------------------------------------------------------
    # Entrada de PIN
    # ------------------------------------------------------------------

    def _press(self, digit: str):
        if self._waiting:
            return
        if len(self._pin) < 8:
            self._pin.append(digit)
            self._update_display()
            self._lbl_status.configure(text="")
        self.after(10, self.focus_force)

    def _backspace(self):
        if self._waiting:
            return
        if self._pin:
            self._pin.pop()
            self._update_display()
        self.after(10, self.focus_force)

    def _update_display(self):
        n = len(self._pin)
        self._display.configure(text="\u25cf " * n if n > 0 else "")

    def _key_press_pin(self, event):
        if self._waiting:
            return
        if event.char and event.char.isdigit():
            self._press(event.char)
        elif event.keysym == "BackSpace":
            self._backspace()
        elif event.keysym in ("Return", "KP_Enter"):
            self._confirm()

    # ------------------------------------------------------------------
    # Validacion
    # ------------------------------------------------------------------

    def _confirm(self):
        if self._waiting:
            return

        motivo = self._motivo_text if self._motivo_required else self._accion

        if len(self._pin) < 4:
            self._lbl_status.configure(
                text="El PIN debe tener al menos 4 digitos.",
                text_color=_ERR_RED,
            )
            return

        self._waiting = True
        self._lbl_status.configure(text="Verificando...", text_color=_ACCENT)
        threading.Thread(target=self._validate, args=(motivo,), daemon=True).start()

    def _validate(self, motivo: str):
        user = session.get_user()
        solicitante = user.get("name", user.get("username", "cajero"))
        self._cajero = solicitante
        pin = "".join(self._pin)
        result = api.validar_pin_supervisor(
            pin=pin,
            accion=self._accion,
            solicitante=solicitante,
            motivo=motivo,
        )
        self.after(0, lambda: self._on_validate_result(result))

    def _on_validate_result(self, result: dict):
        self._waiting = False
        if result.get("status") == "ok":
            # Verificar permiso si se requiere uno especifico
            if self._required_permiso:
                permiso = result.get("permiso", "completo")
                if permiso != "completo" and permiso != self._required_permiso:
                    self._pin.clear()
                    self._update_display()
                    self._lbl_status.configure(
                        text="PIN valido pero sin permiso para esta accion.",
                        text_color=_ERR_RED,
                    )
                    return
            # Guardar info del supervisor para el voucher
            self._supervisor_name = result.get("supervisor", "")
            self._supervisor_id = result.get("supervisor_id", 0)
            self._show_success()
        else:
            self._pin.clear()
            self._update_display()
            msg = result.get("error", "PIN incorrecto o sin autorizacion.")
            self._lbl_status.configure(text=msg, text_color=_ERR_RED)

    # ------------------------------------------------------------------
    # Pantalla de exito
    # ------------------------------------------------------------------

    def _show_success(self):
        for w in self._card.winfo_children():
            w.destroy()

        self._win_h = 200
        self.geometry(f"{self._win_w}x{self._win_h}")

        # Barra verde
        ctk.CTkFrame(
            self._card, height=4, fg_color=_OK_GREEN, corner_radius=2,
        ).pack(fill="x", padx=40, pady=(24, 0))

        # Checkmark grande
        ctk.CTkLabel(
            self._card,
            text="\u2713",
            font=ctk.CTkFont(size=52, weight="bold"),
            text_color=_OK_GREEN,
        ).pack(pady=(20, 4))

        # Texto
        ctk.CTkLabel(
            self._card,
            text="Autorizado",
            font=ctk.CTkFont(size=20, weight="bold"),
            text_color=_OK_GREEN,
        ).pack(pady=(0, 20))

        self._card.configure(border_color=_OK_GREEN)

        # Auto-cerrar despues de 800ms
        self.after(800, lambda: self._send_result(True))

    # ------------------------------------------------------------------
    # Resultado y cierre
    # ------------------------------------------------------------------

    def _send_result(self, value):
        if self._result_sent:
            return
        self._result_sent = True
        try:
            self.grab_release()
        except Exception:
            pass
        self.destroy()
        if self._on_result:
            if value is True and self._motivo_required:
                # Construir datos del voucher
                voucher_data = {
                    "authorized": True,
                    "accion": self._accion,
                    "motivo": self._motivo_text,
                    "atribucion": self._atribucion,
                    "cajero": self._cajero,
                    "supervisor": self._supervisor_name,
                    "supervisor_id": self._supervisor_id,
                }
                # Imprimir voucher en hilo aparte
                from printing import print_voucher
                threading.Thread(
                    target=print_voucher, args=(voucher_data,), daemon=True,
                ).start()
                self._on_result(voucher_data)
            else:
                self._on_result(value)

    def _cancel(self):
        self._send_result(False)
