"""
Dialogo para reprogramar una pulsera activa.

Flujo:
  1. Muestra info de la pulsera actual.
  2. Ingresar nuevos tiempos LED (verde/azul/rojo) + PIN de supervisor.
  3. Clic REPROGRAMAR -> valida PIN contra servidor, luego programa via agente.
  4. Exito: actualiza tracker con nuevos tiempos, muestra confirmacion.
  5. Error: muestra mensaje, permite reintentar.
"""
import threading
import customtkinter as ctk
from typing import Optional, Callable
import api
import wristband_tracker
from ui.kb_shortcuts import bind_entry

_STATE_IDLE    = "idle"
_STATE_WAITING = "waiting"
_STATE_OK      = "ok"
_STATE_TIMEOUT = "timeout"
_STATE_ERROR   = "error"


class ReprogramarDialog(ctk.CTkToplevel):
    """
    Modal para reprogramar una pulsera activa.
    entry:   dict de wristband_tracker (con id, package_name, name, code).
    cashier: cajero actual.
    on_done: llamado con result dict si la reprogramacion es exitosa.
    """

    def __init__(
        self,
        parent,
        entry: dict,
        cashier: str = "caja1",
        on_done: Optional[Callable] = None,
    ):
        super().__init__(parent)
        self._entry   = entry
        self._cashier = cashier
        self._on_done = on_done
        self._result  = None
        self._state   = _STATE_IDLE

        # Valores para retry
        self._new_g = 0
        self._new_b = 0
        self._new_r = 0

        self.title("Reprogramar Pulsera")
        self.geometry("440x540")
        self.resizable(False, False)
        self.grab_set()
        self.lift()
        self.focus_force()

        # Centrar sobre el padre
        self.update_idletasks()
        px = parent.winfo_rootx() + parent.winfo_width()  // 2
        py = parent.winfo_rooty() + parent.winfo_height() // 2
        self.geometry(f"440x540+{px - 220}+{py - 270}")

        self._build()
        self.bind("<Escape>", lambda e: self._close())
        self.bind("<Return>",  lambda e: self._on_submit())

    # ------------------------------------------------------------------
    # UI
    # ------------------------------------------------------------------

    def _build(self):
        # Boton X
        ctk.CTkButton(
            self, text="✕", width=30, height=30, corner_radius=6,
            fg_color="transparent", hover_color="#444444",
            text_color="#888888", font=ctk.CTkFont(size=14),
            command=self._close,
        ).place(x=400, y=8)

        # Titulo
        ctk.CTkLabel(
            self, text="Reprogramar Pulsera",
            font=ctk.CTkFont(size=18, weight="bold"),
            text_color="#e6a800",
        ).pack(pady=(18, 2))
        ctk.CTkLabel(
            self,
            text="Cambiar tiempos LED — requiere autorizacion de supervisor",
            font=ctk.CTkFont(size=11), text_color="#888888",
        ).pack(pady=(0, 14))

        # Info de la pulsera
        info = ctk.CTkFrame(
            self, fg_color=("#1e1e2e", "#100d1a"),
            corner_radius=10, border_width=1, border_color="#3a3a1a",
        )
        info.pack(fill="x", padx=20, pady=(4, 14))

        name = self._entry.get("name", "").strip() or "(sin nombre)"
        pkg  = self._entry.get("package_name", "")
        code = self._entry.get("code", "")

        ctk.CTkLabel(
            info, text=name,
            font=ctk.CTkFont(size=14, weight="bold"), text_color="#e0e0e0",
        ).pack(anchor="w", padx=14, pady=(10, 2))
        ctk.CTkLabel(
            info, text=f"{pkg}  ·  {code}",
            font=ctk.CTkFont(size=11), text_color="#888888",
        ).pack(anchor="w", padx=14, pady=(0, 10))

        # Nuevos tiempos LED
        ctk.CTkLabel(self, text="Nuevos tiempos LED (minutos):",
                     font=ctk.CTkFont(size=12),
                     text_color="#aaaaaa").pack(anchor="w", padx=20, pady=(0, 6))

        led_row = ctk.CTkFrame(self, fg_color="transparent")
        led_row.pack(fill="x", padx=20, pady=(0, 10))

        for label, color_hex, attr, key in [
            ("Verde",  "#4caf50", "_entry_green", "grace_minutes"),
            ("Azul",   "#2196f3", "_entry_blue",  "play_minutes"),
            ("Rojo",   "#f44336", "_entry_red",   "red_minutes"),
        ]:
            col = ctk.CTkFrame(led_row, fg_color="transparent")
            col.pack(side="left", expand=True)
            ctk.CTkLabel(col, text=label, font=ctk.CTkFont(size=11),
                         text_color=color_hex).pack()
            e = ctk.CTkEntry(col, placeholder_text="0", width=80,
                             font=ctk.CTkFont(size=13), height=34)
            e.pack(pady=(2, 0))
            # Pre-fill con tiempos actuales
            val = self._entry.get(key, 0)
            if val:
                e.insert(0, str(val))
            bind_entry(e)
            setattr(self, attr, e)

        # PIN de supervisor
        ctk.CTkLabel(self, text="PIN del supervisor:",
                     font=ctk.CTkFont(size=12),
                     text_color="#aaaaaa").pack(anchor="w", padx=20, pady=(4, 2))
        self._entry_pin = ctk.CTkEntry(
            self, placeholder_text="4-8 digitos",
            font=ctk.CTkFont(size=13), height=36,
            show="●",
        )
        self._entry_pin.pack(fill="x", padx=20, pady=(0, 14))

        # Boton principal
        self._btn_submit = ctk.CTkButton(
            self, text="REPROGRAMAR  →  ACERCAR PULSERA",
            font=ctk.CTkFont(size=14, weight="bold"), height=50,
            corner_radius=10, fg_color="#6d4c00", hover_color="#8b6a1f",
            command=self._on_submit,
        )
        self._btn_submit.pack(fill="x", padx=20, pady=(0, 8))

        # Estado / resultado
        self._lbl_status = ctk.CTkLabel(
            self, text="",
            font=ctk.CTkFont(size=13), wraplength=400, justify="center",
        )
        self._lbl_status.pack(pady=(0, 6))

        # Reintentar (oculto hasta timeout)
        self._btn_retry = ctk.CTkButton(
            self, text="Reintentar (acercar pulsera nuevamente)",
            height=36, corner_radius=8,
            fg_color="#e65100", hover_color="#bf360c",
            command=self._on_retry,
        )

        self._entry_green.focus_set()

    # ------------------------------------------------------------------
    # Helpers
    # ------------------------------------------------------------------

    def _parse_int(self, entry: ctk.CTkEntry, default: int = 0) -> Optional[int]:
        val = entry.get().strip()
        if not val:
            return default
        try:
            n = int(val)
            return n if n >= 0 else None
        except ValueError:
            return None

    # ------------------------------------------------------------------
    # Flujo
    # ------------------------------------------------------------------

    def _on_submit(self):
        if self._state == _STATE_WAITING:
            return

        g = self._parse_int(self._entry_green)
        b = self._parse_int(self._entry_blue)
        r = self._parse_int(self._entry_red)

        if g is None or b is None or r is None:
            self._lbl_status.configure(
                text="Los minutos deben ser enteros >= 0.", text_color="#f44336"
            )
            return

        if g == 0 and b == 0 and r == 0:
            self._lbl_status.configure(
                text="Al menos un color debe tener minutos > 0.", text_color="#f44336"
            )
            return

        pin = self._entry_pin.get().strip()
        if len(pin) < 4:
            self._lbl_status.configure(
                text="El PIN debe tener al menos 4 digitos.", text_color="#f44336"
            )
            return

        self._new_g = g
        self._new_b = b
        self._new_r = r
        self._pin   = pin

        self._set_state(_STATE_WAITING)
        self._lbl_status.configure(text="Verificando autorizacion...", text_color="#4fc3f7")
        threading.Thread(target=self._validate_and_program, daemon=True).start()

    def _on_retry(self):
        self._set_state(_STATE_WAITING)
        self._lbl_status.configure(
            text="Acerque la pulsera al programador...", text_color="#4fc3f7"
        )
        threading.Thread(target=self._do_program, daemon=True).start()
        self.after(200, self._poll)

    def _validate_and_program(self):
        import session as _session
        user_id = _session.get_user().get("id", "")
        pin_result = api.validar_pin_supervisor(user_id, self._pin)

        # Aceptar "status":"ok" o "valid":true
        if pin_result.get("status") == "ok" or pin_result.get("valid"):
            self.after(0, self._start_programming)
        else:
            self.after(0, lambda: self._on_pin_fail(pin_result))

    def _on_pin_fail(self, result: dict):
        self._set_state(_STATE_ERROR)
        msg = (result.get("message")
               or result.get("error", "PIN incorrecto o sin autorizacion."))
        self._lbl_status.configure(text=msg, text_color="#f44336")

    def _start_programming(self):
        self._result = None
        self._btn_submit.configure(text="Esperando pulsera...")
        self._lbl_status.configure(
            text="Acerque la pulsera al programador...", text_color="#4fc3f7"
        )
        threading.Thread(target=self._do_program, daemon=True).start()
        self.after(200, self._poll)

    def _do_program(self):
        self._result = api.program_via_agent(
            green_minutes=self._new_g,
            blue_minutes=self._new_b,
            red_minutes=self._new_r,
        )

    def _poll(self):
        if self._result is None:
            dots = (self._lbl_status.cget("text").count(".") % 3) + 1
            self._lbl_status.configure(
                text=f"Acerque la pulsera al programador{'.' * dots}",
                text_color="#4fc3f7",
            )
            self.after(200, self._poll)
            return

        r = self._result
        self._result = None

        if r.get("error"):
            self._set_state(_STATE_ERROR)
            self._lbl_status.configure(
                text=f"Error: {r['error']}", text_color="#f44336"
            )

        elif r.get("status") == "ok":
            # Actualizar tracker con nuevos tiempos
            wristband_tracker.update_reprogram(
                self._entry["id"], self._new_g, self._new_b, self._new_r,
            )
            total = self._new_g + self._new_b + self._new_r
            self._set_state(_STATE_OK)
            self._lbl_status.configure(
                text=f"Pulsera reprogramada exitosamente.\n"
                     f"Nuevos tiempos: {self._new_g}V / {self._new_b}A / {self._new_r}R = {total} min",
                text_color="#4caf50",
            )
            if self._on_done:
                self._on_done(r)
            self.after(3000, self._close)

        elif r.get("status") == "timeout":
            self._set_state(_STATE_TIMEOUT)
            self._lbl_status.configure(
                text="Pulsera no detectada.\nAcerca la pulsera y presiona Reintentar.",
                text_color="#ff9800",
            )

        else:
            self._set_state(_STATE_ERROR)
            self._lbl_status.configure(
                text="Respuesta inesperada del servidor.", text_color="#f44336"
            )

    # ------------------------------------------------------------------
    # Estado visual
    # ------------------------------------------------------------------

    def _set_state(self, state: str):
        self._state = state
        if state == _STATE_IDLE:
            self._btn_submit.configure(
                state="normal",
                text="REPROGRAMAR  →  ACERCAR PULSERA",
                fg_color="#6d4c00",
            )
            self._btn_retry.pack_forget()
        elif state == _STATE_WAITING:
            self._btn_submit.configure(
                state="disabled", text="Procesando...", fg_color="#424242"
            )
            self._btn_retry.pack_forget()
        elif state == _STATE_OK:
            self._btn_submit.configure(
                state="disabled", text="Reprogramada!", fg_color="#1b5e20"
            )
            self._btn_retry.pack_forget()
        elif state == _STATE_TIMEOUT:
            self._btn_submit.configure(
                state="normal",
                text="REPROGRAMAR  →  ACERCAR PULSERA",
                fg_color="#6d4c00",
            )
            self._btn_retry.pack(fill="x", padx=20, pady=(0, 8))
        elif state == _STATE_ERROR:
            self._btn_submit.configure(
                state="normal",
                text="REPROGRAMAR  →  ACERCAR PULSERA",
                fg_color="#6d4c00",
            )
            self._btn_retry.pack_forget()

    # ------------------------------------------------------------------

    def _close(self):
        if self._state == _STATE_WAITING:
            return
        self.grab_release()
        self.destroy()
