"""Ventana sencilla (tkinter) del agente del equipo de VIXORA.

Es el plan B de `vixora_agente_app.py`: cuando no hay pywebview o WebView2, se
abre esta. Enseña lo mismo —las mismas cuatro secciones, leídas del mismo
`Control`— con los controles que trae Python de serie.
"""

from __future__ import annotations

import threading
import tkinter as tk
import webbrowser
from tkinter import filedialog, messagebox, ttk
from typing import Any

import vixora_agente_bandeja as bandeja

FONDO, LADO, PANEL, CAMPO = "#0B0D14", "#0E111B", "#131724", "#0D101A"
TEXTO, TENUE, VIOLETA, AZUL = "#E6EAF2", "#8B95AD", "#7C3AED", "#3B82F6"
COLOR = {"en_linea": "#34D399", "trabajando": "#A78BFA", "conectando": "#FBBF24",
         "sin_conexion": "#F87171", "detenido": "#8B95AD"}
ESTADOS = {"en_curso": "En curso", "listo": "Listo", "error": "Error"}
FUENTE = "Segoe UI"
MODELO_AUTO = "El que pida VIXORA"
NOMBRES = (("inicio", "Inicio"), ("configuracion", "Configuración"), ("registro", "Registro"), ("ayuda", "Ayuda"))


def _dura(s: float | None) -> str:
    if s is None:
        return "—"
    return f"{s:.1f} s" if s < 60 else f"{int(s // 60)} min {int(s % 60)} s"


def _hace(s: int | None) -> str:
    if s is None:
        return ""
    if s < 60:
        return f"señal hace {s} s"
    return f"señal hace {round(s / 60)} min" if s < 3600 else f"señal hace {round(s / 3600)} h"


class Ventana(tk.Tk):
    def __init__(self, control: Any, minimizado: bool = False, seccion: str = "", auto: bool = False,
                 segundo: Any = None) -> None:
        """`segundo` es la fábrica del segundo plano (`SegundoPlano` de
        vixora_agente_app): la bandeja y la escucha de una segunda instancia.
        Sin ella la ventana se minimiza a la barra de tareas, como antes."""
        super().__init__()
        self.control = control
        self.seq = 0
        self.form_cargado = False
        self.title("Agente del equipo · VIXORA")
        self.geometry("1040x720")
        self.minsize(820, 560)
        self.configure(bg=FONDO)
        self._estilos()
        self._armar()
        self.ir(seccion or "inicio")
        self.protocol("WM_DELETE_WINDOW", self.cerrar)
        self._marco_oscuro()
        self._icono()
        # Lo que pide la bandeja llega desde otros hilos: se pasa a este con `after`.
        self.segundo = segundo(control, lambda: self.after(0, self.mostrar), lambda: self.after(0, self.salir)) \
            if segundo else None
        self.en_bandeja = bool(self.segundo and self.segundo.arrancar())
        if minimizado:
            if self.en_bandeja:
                self.withdraw()
            else:
                self.iconify()
        self.after(100, self.refrescar)
        if auto:
            self._hilo(self.control.iniciar)

    # ------------------------------------------------------------ armado
    def _icono(self) -> None:
        """Nuestro icono en la barra de título y en la de tareas. Fuera de
        Windows no hace nada (queda el de siempre).

        `iconbitmap(default=…)` lo deja como icono de todas las ventanas de
        tkinter; además se pone en esta con la API de Windows (pequeño y
        grande, a su tamaño), igual que en la ventana de HTML."""
        if not bandeja.ES_WINDOWS or not bandeja.ICONO.is_file():
            return
        try:
            self.iconbitmap(default=str(bandeja.ICONO))
        except tk.TclError:
            pass
        try:
            import ctypes

            self.update_idletasks()
            bandeja.icono_ventana(ctypes.windll.user32.GetParent(self.winfo_id()))
        except Exception:  # noqa: BLE001
            pass

    def _marco_oscuro(self) -> None:
        """Barra de título oscura en Windows 10/11; en lo demás, no hace nada."""
        try:
            import ctypes

            self.update_idletasks()
            mango = ctypes.windll.user32.GetParent(self.winfo_id())
            for atributo, valor in ((20, 1), (35, 0x00140D0B), (34, 0x00140D0B)):
                ctypes.windll.dwmapi.DwmSetWindowAttribute(
                    mango, atributo, ctypes.byref(ctypes.c_int(valor)), ctypes.sizeof(ctypes.c_int))
        except Exception:  # noqa: BLE001
            pass

    def _estilos(self) -> None:
        e = ttk.Style(self)
        e.theme_use("clam")
        e.configure("Treeview", background=PANEL, fieldbackground=PANEL, foreground=TEXTO, borderwidth=0,
                    bordercolor=PANEL, lightcolor=PANEL, darkcolor=PANEL, rowheight=28, font=(FUENTE, 10))
        e.configure("Treeview.Heading", background=PANEL, foreground=TENUE, borderwidth=0, font=(FUENTE, 9))
        e.map("Treeview", background=[("selected", "#232A42")], foreground=[("selected", TEXTO)])

    def _boton(self, padre: tk.Widget, texto: str, orden: Any, principal: bool = False) -> tk.Button:
        return tk.Button(
            padre, text=texto, command=orden, relief="flat", bd=0, padx=14, pady=6, cursor="hand2",
            bg=VIOLETA if principal else "#1B2135", fg="#FFFFFF" if principal else TEXTO,
            activebackground=AZUL if principal else "#232A42", activeforeground="#FFFFFF",
            disabledforeground="#5C6680", font=(FUENTE, 10, "bold" if principal else "normal"),
        )

    def _tarjeta(self, padre: tk.Widget, titulo: str = "") -> tk.Frame:
        t = tk.Frame(padre, bg=PANEL, padx=16, pady=14, highlightbackground="#20263A", highlightthickness=1)
        t.pack(fill="x", pady=(0, 12))
        if titulo:
            tk.Label(t, text=titulo.upper(), bg=PANEL, fg=TENUE, font=(FUENTE, 9, "bold")).pack(anchor="w", pady=(0, 8))
        return t

    def _texto(self, padre: tk.Widget, texto: str = "", color: str = TEXTO, **kw: Any) -> tk.Label:
        return tk.Label(padre, text=texto, bg=padre["bg"], fg=color, font=(FUENTE, 10), anchor="w", justify="left", **kw)

    def _campo(self, padre: tk.Widget, etiqueta: str, var: tk.Variable, oculto: bool = False) -> tk.Entry:
        self._texto(padre, etiqueta, TENUE).pack(anchor="w")
        c = tk.Entry(padre, textvariable=var, bg=CAMPO, fg=TEXTO, insertbackground=TEXTO, relief="flat",
                     highlightbackground="#2A3150", highlightcolor=VIOLETA, highlightthickness=1,
                     font=(FUENTE, 10), show="•" if oculto else "")
        c.pack(fill="x", ipady=5, pady=(3, 10))
        return c

    def _armar(self) -> None:
        lado = tk.Frame(self, bg=LADO, width=210)
        lado.pack(side="left", fill="y")
        lado.pack_propagate(False)
        marca = tk.Frame(lado, bg=LADO)
        marca.pack(fill="x", padx=16, pady=(18, 16))
        tk.Label(marca, text=" V ", bg=VIOLETA, fg="#FFFFFF", font=(FUENTE, 14, "bold")).pack(side="left")
        nombres = tk.Frame(marca, bg=LADO)
        nombres.pack(side="left", padx=10)
        tk.Label(nombres, text="VIXORA", bg=LADO, fg=TEXTO, font=(FUENTE, 11, "bold")).pack(anchor="w")
        tk.Label(nombres, text="Agente del equipo", bg=LADO, fg=TENUE, font=(FUENTE, 9)).pack(anchor="w")
        self.nav: dict[str, tk.Button] = {}
        for clave, nombre in NOMBRES:
            b = tk.Button(lado, text="  " + nombre, anchor="w", relief="flat", bd=0, bg=LADO, fg=TENUE, padx=12, pady=8,
                          activebackground="#1A1F33", activeforeground=TEXTO, font=(FUENTE, 10), cursor="hand2",
                          command=lambda c=clave: self.ir(c))
            b.pack(fill="x", padx=10, pady=1)
            self.nav[clave] = b
        self.version = tk.Label(lado, text="", bg=LADO, fg="#5C6680", font=(FUENTE, 9))
        self.version.pack(side="bottom", anchor="w", padx=18, pady=12)

        principal = tk.Frame(self, bg=FONDO)
        principal.pack(side="left", fill="both", expand=True)
        cab = tk.Frame(principal, bg=FONDO)
        cab.pack(fill="x", padx=22, pady=(16, 12))
        quien = tk.Frame(cab, bg=FONDO)
        quien.pack(side="left", fill="x", expand=True)
        self.nombre = tk.Label(quien, text="Agente del equipo", bg=FONDO, fg=TEXTO, font=(FUENTE, 14, "bold"), anchor="w")
        self.nombre.pack(anchor="w")
        linea = tk.Frame(quien, bg=FONDO)
        linea.pack(anchor="w", pady=(3, 0))
        self.pildora = tk.Label(linea, text="● Detenido", bg=FONDO, fg=TENUE, font=(FUENTE, 10, "bold"))
        self.pildora.pack(side="left")
        self.senal = tk.Label(linea, text="", bg=FONDO, fg=TENUE, font=(FUENTE, 10))
        self.senal.pack(side="left", padx=12)
        self.b_detener = self._boton(cab, "■  Detener", lambda: self._hilo(self.control.detener))
        self.b_detener.pack(side="right")
        self.b_iniciar = self._boton(cab, "▶  Iniciar", lambda: self._hilo(self.control.iniciar), principal=True)
        self.b_iniciar.pack(side="right", padx=8)
        tk.Frame(principal, bg="#20263A", height=1).pack(fill="x")

        self.paginas: dict[str, tk.Frame] = {}
        cuerpo = tk.Frame(principal, bg=FONDO)
        cuerpo.pack(fill="both", expand=True, padx=22, pady=16)
        for clave, _ in NOMBRES:
            self.paginas[clave] = tk.Frame(cuerpo, bg=FONDO)
        self._inicio(self.paginas["inicio"])
        self._configuracion(self.paginas["configuracion"])
        self._registro(self.paginas["registro"])
        self._ayuda(self.paginas["ayuda"])

    def _inicio(self, p: tk.Frame) -> None:
        self.motivo = tk.Label(p, text="", bg="#2A1518", fg="#FCA5A5", font=(FUENTE, 10), anchor="w", padx=12, pady=8,
                               wraplength=740, justify="left")
        fila = tk.Frame(p, bg=FONDO)
        fila.pack(fill="x", pady=(0, 12))
        self.cifras: dict[str, tk.Label] = {}
        for i, (clave, nombre) in enumerate((("atendidos", "Trabajos atendidos hoy"), ("fallidos", "Fallidos hoy"),
                                              ("medio", "Tiempo medio"))):
            t = tk.Frame(fila, bg=PANEL, padx=16, pady=12, highlightbackground="#20263A", highlightthickness=1)
            t.grid(row=0, column=i, sticky="nsew", padx=(0 if i == 0 else 12, 0))
            fila.columnconfigure(i, weight=1, uniform="c")
            self.cifras[clave] = tk.Label(t, text="0", bg=PANEL, fg="#A78BFA" if clave == "atendidos" else TEXTO,
                                          font=(FUENTE, 20, "bold"), anchor="w")
            self.cifras[clave].pack(anchor="w")
            tk.Label(t, text=nombre, bg=PANEL, fg=TENUE, font=(FUENTE, 9)).pack(anchor="w")
        t = self._tarjeta(p, "Este equipo")
        self.datos: dict[str, tk.Label] = {}
        rejilla = tk.Frame(t, bg=PANEL)
        rejilla.pack(fill="x")
        for i, (clave, nombre) in enumerate((("nombre", "Nombre en VIXORA"), ("claude", "Claude Code"), ("version", "Versión"),
                                              ("conc", "Trabajos a la vez"), ("api", "Servidor"))):
            tk.Label(rejilla, text=nombre, bg=PANEL, fg=TENUE, font=(FUENTE, 10), anchor="w", width=20).grid(
                row=i, column=0, sticky="w", pady=2)
            self.datos[clave] = tk.Label(rejilla, text="—", bg=PANEL, fg=TEXTO, font=(FUENTE, 10), anchor="w",
                                         wraplength=560, justify="left")
            self.datos[clave].grid(row=i, column=1, sticky="w", pady=2)
        t = tk.Frame(p, bg=PANEL, padx=16, pady=14, highlightbackground="#20263A", highlightthickness=1)
        t.pack(fill="both", expand=True)
        tk.Label(t, text="TRABAJOS", bg=PANEL, fg=TENUE, font=(FUENTE, 9, "bold")).pack(anchor="w", pady=(0, 8))
        self._texto(t, "Solo el tipo de trabajo, su estado y cuánto tardó. Nunca el contenido.", TENUE).pack(
            side="bottom", anchor="w", pady=(8, 0))
        self.tabla = ttk.Treeview(t, columns=("hora", "funcion", "estado", "dur"), show="headings", height=6)
        for col, nombre, ancho in (("hora", "Hora", 90), ("funcion", "Función", 300), ("estado", "Estado", 110),
                                   ("dur", "Duración", 110)):
            self.tabla.heading(col, text=nombre, anchor="w")
            self.tabla.column(col, width=ancho, anchor="w")
        self.tabla.tag_configure("en_curso", foreground="#A78BFA")
        self.tabla.tag_configure("listo", foreground="#34D399")
        self.tabla.tag_configure("error", foreground="#F87171")
        self.tabla.pack(fill="both", expand=True)

    def _configuracion(self, p: tk.Frame) -> None:
        self.v_api, self.v_token, self.v_conc = tk.StringVar(), tk.StringVar(), tk.StringVar()
        self.v_modelo, self.v_bin, self.v_arranque = tk.StringVar(), tk.StringVar(), tk.BooleanVar()
        t = self._tarjeta(p, "Conexión con VIXORA")
        self._campo(t, "Dirección del API", self.v_api)
        self._campo(t, "Token del equipo (pega uno nuevo para reemplazarlo)", self.v_token, oculto=True)
        self.nota_token = self._texto(t, "", TENUE)
        self.nota_token.pack(anchor="w", pady=(0, 8))
        fila = tk.Frame(t, bg=PANEL)
        fila.pack(anchor="w")
        self._boton(fila, "Pegar config.json", self.pegar).pack(side="left")
        self._boton(fila, "Cargar archivo…", self.cargar).pack(side="left", padx=8)
        t = self._tarjeta(p, "Claude Code")
        fila = tk.Frame(t, bg=PANEL)
        fila.pack(fill="x", pady=(0, 10))
        self._texto(fila, "Trabajos a la vez", TENUE).pack(side="left")
        tk.Spinbox(fila, from_=1, to=8, textvariable=self.v_conc, width=4, bg=CAMPO, fg=TEXTO, relief="flat",
                   buttonbackground=PANEL, insertbackground=TEXTO, font=(FUENTE, 10)).pack(side="left", padx=(8, 24))
        self._texto(fila, "Modelo", TENUE).pack(side="left")
        menu = tk.OptionMenu(fila, self.v_modelo, MODELO_AUTO, "haiku", "sonnet", "opus")
        menu.configure(bg=CAMPO, fg=TEXTO, activebackground="#232A42", activeforeground=TEXTO, relief="flat", bd=0,
                       highlightbackground="#2A3150", highlightthickness=1, font=(FUENTE, 10), width=22, anchor="w")
        menu["menu"].configure(bg=CAMPO, fg=TEXTO, activebackground=VIOLETA, activeforeground="#FFFFFF", bd=0)
        menu.pack(side="left", padx=8)
        self._campo(t, "Programa de Claude Code (vacío = buscarlo automáticamente)", self.v_bin)
        fila = tk.Frame(t, bg=PANEL)
        fila.pack(anchor="w")
        self._boton(fila, "Detectar", self.detectar).pack(side="left")
        self.b_probar = self._boton(fila, "Probar Claude Code", self.probar)
        self.b_probar.pack(side="left", padx=8)
        self.r_probar = self._texto(t, "", TENUE, wraplength=720)
        self.r_probar.pack(anchor="w", pady=(8, 0))
        t = self._tarjeta(p)
        self.c_arranque = tk.Checkbutton(
            t, text="Arrancar con Windows (guardado junto al reloj)", variable=self.v_arranque, command=self.arranque,
            bg=PANEL, fg=TEXTO, selectcolor=CAMPO, activebackground=PANEL, activeforeground=TEXTO, font=(FUENTE, 10))
        self.c_arranque.pack(side="left")
        self._boton(t, "Guardar", self.guardar, principal=True).pack(side="right")
        self.archivo = self._texto(p, "", "#5C6680")
        self.archivo.pack(anchor="w")

    def _registro(self, p: tk.Frame) -> None:
        cab = tk.Frame(p, bg=FONDO)
        cab.pack(fill="x", pady=(0, 8))
        tk.Label(cab, text="REGISTRO", bg=FONDO, fg=TENUE, font=(FUENTE, 9, "bold")).pack(side="left")
        self.b_copiar = self._boton(cab, "Copiar", self.copiar)
        self.b_copiar.pack(side="right")
        self.log = tk.Text(p, bg="#080A11", fg="#B9C2D8", relief="flat", font=("Consolas", 10), wrap="word",
                           state="disabled", padx=12, pady=10, highlightbackground="#20263A", highlightthickness=1)
        self.log.pack(fill="both", expand=True)

    def _ayuda(self, p: tk.Frame) -> None:
        t = self._tarjeta(p, "Cómo se usa")
        for n, paso in enumerate((
            "Instala Claude Code en este equipo e inicia sesión: en una terminal, ejecuta «claude» y entra con la cuenta del equipo.",
            "Crea el token en el panel de VIXORA: Sistema → Agente del equipo → Crear token. Se muestra una sola vez.",
            "Pégalo aquí: Configuración → «Pegar config.json» (o pega solo el token) y Guardar.",
            "Prueba con «Probar Claude Code». Debe responder OK.",
            "Pulsa Iniciar. Cuando diga «En línea, esperando trabajo», VIXORA ya puede mandarle encargos.",
            "Para que atienda siempre, activa «Arrancar con Windows» y evita que el equipo se suspenda.",
        ), 1):
            self._texto(t, f"{n}.  {paso}", wraplength=730).pack(anchor="w", pady=2)
        fila = tk.Frame(t, bg=PANEL)
        fila.pack(anchor="w", pady=(10, 0))
        self._boton(fila, "Abrir el panel", self.abrir_panel, principal=True).pack(side="left")
        self.enlace = self._texto(fila, "", "#A78BFA")
        self.enlace.pack(side="left", padx=12)
        t = self._tarjeta(p, "Privacidad")
        self._texto(t, "Cada encargo corre en una carpeta temporal vacía que se borra al terminar. Esta ventana no "
                       "muestra ni guarda el contenido: solo el tipo de trabajo, la duración y si salió bien.",
                    TENUE, wraplength=730).pack(anchor="w")

    # ------------------------------------------------------------ acciones
    def ir(self, seccion: str) -> None:
        for clave, pagina in self.paginas.items():
            pagina.pack_forget()
            self.nav[clave].configure(bg=LADO, fg=TENUE)
        self.paginas[seccion].pack(fill="both", expand=True)
        self.nav[seccion].configure(bg="#231A45", fg="#FFFFFF")

    def _hilo(self, hacer: Any, luego: Any = None) -> None:
        """Lo lento, fuera del hilo que dibuja; el resultado vuelve con `after`."""
        def correr() -> None:
            try:
                r = hacer()
            except Exception as exc:  # noqa: BLE001
                r = {"ok": False, "mensaje": f"Algo no salió: {exc}"}
            self.after(0, lambda: (luego or self._decir)(r))
        threading.Thread(target=correr, daemon=True).start()

    def _decir(self, r: dict[str, Any] | None) -> None:
        if r and r.get("mensaje") and not r.get("ok"):
            messagebox.showwarning(self.title(), r["mensaje"], parent=self)

    def _aplicado(self, r: dict[str, Any] | None) -> None:
        if not r or not r.get("mensaje"):
            return
        self.form_cargado = not r.get("ok")
        (messagebox.showinfo if r.get("ok") else messagebox.showwarning)(self.title(), r["mensaje"], parent=self)

    def guardar(self) -> None:
        cambios = {"api": self.v_api.get(), "token": self.v_token.get(), "concurrencia": self.v_conc.get(),
                   "modelo": "" if self.v_modelo.get() == MODELO_AUTO else self.v_modelo.get(),
                   "claude_bin": self.v_bin.get()}
        self._hilo(lambda: self.control.guardar_config(cambios), self._aplicado)

    def pegar(self) -> None:
        try:
            texto = self.clipboard_get()
        except tk.TclError:
            texto = ""
        if not texto.strip():
            messagebox.showinfo(self.title(), "Copia primero el contenido de config.json (o el token) y vuelve a pulsar.",
                                parent=self)
            return
        self._hilo(lambda: self.control.importar_config(texto), self._aplicado)

    def cargar(self) -> None:
        ruta = filedialog.askopenfilename(parent=self, title="Elegir config.json",
                                          filetypes=[("config.json", "*.json"), ("Todos", "*.*")])
        if ruta:
            self._hilo(lambda: self.control.importar_archivo(ruta), self._aplicado)

    def detectar(self) -> None:
        r = self.control.detectar_claude()
        if r["ok"]:
            self.v_bin.set(r["ruta"])
        self.r_probar.configure(text=r["mensaje"], fg="#34D399" if r["ok"] else "#F87171")

    def probar(self) -> None:
        self.b_probar.configure(state="disabled", text="Probando…")
        self.r_probar.configure(text="Puede tardar medio minuto.", fg=TENUE)

        def listo(r: dict[str, Any]) -> None:
            self.b_probar.configure(state="normal", text="Probar Claude Code")
            self.r_probar.configure(text=r.get("mensaje", ""), fg="#34D399" if r.get("ok") else "#F87171")
        self._hilo(self.control.probar, listo)

    def arranque(self) -> None:
        quiere = self.v_arranque.get()

        def listo(r: dict[str, Any]) -> None:
            self.v_arranque.set(bool(r.get("arranque")))
            self._decir(r)
        self._hilo(lambda: self.control.cambiar_arranque(quiere), listo)

    def copiar(self) -> None:
        r = self.control.copiar_registro()
        if not r.get("ok"):
            # El portapapeles del sistema no se dejó: el de tkinter, que vale
            # mientras la ventana siga abierta.
            texto = self.control.texto_registro()
            if texto:
                try:
                    self.clipboard_clear()
                    self.clipboard_append(texto)
                    self.update()
                    r = {"ok": True}
                except tk.TclError:
                    pass
        self.b_copiar.configure(text="Copiado ✓" if r.get("ok") else (r.get("mensaje") or "No se pudo copiar"))
        self.after(1800, lambda: self.b_copiar.configure(text="Copiar"))

    def abrir_panel(self) -> None:
        webbrowser.open(self.enlace.cget("text"))

    def guardar_en_bandeja(self) -> None:
        """A la bandeja, junto al reloj; sin bandeja, minimizada como siempre."""
        if self.segundo is not None and self.segundo.hay_bandeja:
            self.withdraw()
            self.segundo.aviso_oculto()
        else:
            self.iconify()

    def mostrar(self) -> None:
        """Vuelve a enseñar la ventana (clic en la bandeja, o la abrieron otra vez)."""
        self.deiconify()
        self._icono()
        self.lift()
        try:
            self.focus_force()
        except tk.TclError:
            pass

    def salir(self) -> None:
        """«Salir» en el menú de la bandeja: detiene el agente y cierra."""
        self.control.detener()
        self.destroy()

    def cerrar(self) -> None:
        e = self.control.instantanea(self.seq)
        if e["corriendo"]:
            donde = "guardar la ventana junto al reloj y seguir" if self.segundo is not None and self.segundo.hay_bandeja \
                else "minimizar y seguir"
            r = messagebox.askyesnocancel(
                self.title(),
                "El agente está atendiendo a VIXORA.\n\n¿Seguir trabajando en segundo plano?\n\n"
                f"Sí: {donde}.\nNo: detener y salir.", parent=self)
            if r is None:
                return
            if r:
                self.guardar_en_bandeja()
                return
            self.control.detener()
        self.destroy()

    def destroy(self) -> None:
        if self.segundo is not None:
            self.segundo.parar()  # fuera el icono de la bandeja
        super().destroy()

    # ------------------------------------------------------------ pintar
    def refrescar(self) -> None:
        try:
            self.pintar(self.control.instantanea(self.seq))
        finally:
            self.after(1000, self.refrescar)

    def pintar(self, e: dict[str, Any]) -> None:
        self.version.configure(text=f"Agente {e['version']} · ventana sencilla")
        self.nombre.configure(text=e["nombre"] or "Agente del equipo")
        self.pildora.configure(text="● " + e["texto"], fg=COLOR.get(e["estado"], TENUE))
        self.senal.configure(text=_hace(e["senal_s"]))
        puede = not (e["corriendo"] or e["ocupado"])
        self.b_iniciar.configure(state="normal" if puede else "disabled", bg=VIOLETA if puede else "#2A2350")
        self.b_detener.configure(state="normal" if e["corriendo"] and not e["ocupado"] else "disabled")
        if e["motivo"]:
            self.motivo.configure(text=e["motivo"])
            self.motivo.pack(fill="x", pady=(0, 12), before=self.paginas["inicio"].winfo_children()[1])
        else:
            self.motivo.pack_forget()
        hoy = e["hoy"]
        self.cifras["atendidos"].configure(text=str(hoy["atendidos"]))
        self.cifras["fallidos"].configure(text=str(hoy["fallidos"]), fg="#F87171" if hoy["fallidos"] else TEXTO)
        self.cifras["medio"].configure(text=_dura(hoy["medio_s"]))
        cfg, claude = e["config"], e["claude"]
        self.datos["nombre"].configure(
            text=e["nombre"] or ("Se sabrá al conectar" if cfg["hay_token"] else "Falta el token (Configuración)"))
        self.datos["claude"].configure(text=claude["ruta"] or "—")
        if claude["encontrado"] is None:
            self.datos["version"].configure(text="Buscando…", fg=TENUE)
        elif claude["encontrado"]:
            self.datos["version"].configure(text=claude["version"] or "¿?", fg="#34D399")
        else:
            self.datos["version"].configure(text="No se encontró Claude Code", fg="#F87171")
        self.datos["conc"].configure(text=str(e["concurrencia"]) + (f" · modelo fijo: {e['modelo']}" if e["modelo"] else ""))
        self.datos["api"].configure(text=e["api"])
        self.tabla.delete(*self.tabla.get_children())
        for t in e["trabajos"]:
            self.tabla.insert("", "end", values=(t["hora"], t["funcion"], ESTADOS.get(t["estado"], t["estado"]),
                                                 _dura(t["segundos"])), tags=(t["estado"],))
        if e["registro_nuevo"]:
            self.log.configure(state="normal")
            self.log.insert("end", "\n".join(e["registro_nuevo"]) + "\n")
            self.log.configure(state="disabled")
            self.log.see("end")
        self.seq = e["seq"]
        self.nota_token.configure(text=f"Guardado: {cfg['token']}. Déjalo vacío para conservarlo." if cfg["hay_token"]
                                  else "Todavía no hay token. Créalo en el panel: Sistema → Agente del equipo → Crear token.")
        self.archivo.configure(text=cfg["archivo"])
        self.enlace.configure(text=e["panel"])
        self.c_arranque.configure(state="normal" if e["windows"] else "disabled")
        if not self.form_cargado:
            self.v_api.set(cfg["api"])
            self.v_token.set("")
            self.v_conc.set(str(cfg["concurrencia"]))
            self.v_modelo.set(cfg["modelo"] or MODELO_AUTO)
            self.v_bin.set(cfg["claude_bin"])
            self.v_arranque.set(bool(e["arranque"]))
            self.form_cargado = True


def abrir_tk(control: Any, minimizado: bool = False, seccion: str = "", auto: bool = False, segundo: Any = None) -> None:
    ventana = Ventana(control, minimizado=minimizado, seccion=seccion, auto=auto, segundo=segundo)
    try:
        ventana.mainloop()
    finally:
        if ventana.segundo is not None:
            ventana.segundo.parar()
