"""
Dialogo de configuracion del ticket / factura termica.
Permite configurar impresora, sede, numeracion, encabezados y mensaje final.
Muestra una vista previa en tiempo real al estilo impresora 80mm.
El combo de muestra se elige desde un menu desplegable con los combos reales.
"""
import threading
import tkinter as tk
import customtkinter as ctk
import printing
import api
from ui.kb_shortcuts import bind_entry


_SEDES = ["Cuenca", "Quito"]

_DEFAULT_SAMPLE = {
    "minutes":        60,
    "package_name":   "Combo 1 Hora",
    "amount":         5.00,
    "payment_method": "cash",
    "customer_name":  "Juan Perez",
    "folio_number":   "F-0042",
}


class TicketConfigDialog(ctk.CTkToplevel):
    """Modal de configuracion del ticket termico."""

    def __init__(self, parent):
        super().__init__(parent)
        self.title("Configuracion de Factura / Ticket")
        self.geometry("940x640")
        self.resizable(False, False)
        self.grab_set()
        self.lift()
        self.focus_force()

        # Centrar
        self.update_idletasks()
        px = parent.winfo_rootx() + parent.winfo_width()  // 2 - 470
        py = parent.winfo_rooty() + parent.winfo_height() // 2 - 320
        if px < 0: px = 0
        if py < 0: py = 0
        self.geometry(f"+{px}+{py}")

        self._cfg = printing.load_config()

        # Datos del combo de muestra (se actualizan al seleccionar del dropdown)
        self._sample_sale = dict(_DEFAULT_SAMPLE)
        # Mapa nombre -> paquete completo
        self._packages_map: dict[str, dict] = {}
        # Nombres de combos cargados
        self._combo_names: list[str] = [_DEFAULT_SAMPLE["package_name"]]

        self._build()
        self._refresh_preview()

        # Cargar combos reales en segundo plano
        threading.Thread(target=self._load_packages, daemon=True).start()

    # ──────────────────────────────────────────────────────────
    # Layout
    # ──────────────────────────────────────────────────────────

    def _build(self):
        cfg = self._cfg

        ctk.CTkLabel(
            self,
            text="Configuracion de Factura / Ticket",
            font=ctk.CTkFont(size=17, weight="bold"),
            text_color="#4fc3f7",
        ).pack(anchor="w", padx=24, pady=(16, 2))

        ctk.CTkLabel(
            self, text="Editar variables del recibo termico 80mm",
            font=ctk.CTkFont(size=12), text_color="#556688",
        ).pack(anchor="w", padx=24, pady=(0, 10))

        body = ctk.CTkFrame(self, fg_color="transparent")
        body.pack(fill="both", expand=True, padx=16, pady=(0, 8))
        body.grid_columnconfigure(0, weight=0)
        body.grid_columnconfigure(1, weight=1)
        body.grid_rowconfigure(0, weight=1)

        left  = ctk.CTkScrollableFrame(body, width=480, fg_color=("gray88", "#0d0d1a"),
                                       corner_radius=10)
        left.grid(row=0, column=0, sticky="nsew", padx=(0, 8))

        right = ctk.CTkFrame(body, fg_color=("gray88", "#0d0d1a"), corner_radius=10)
        right.grid(row=0, column=1, sticky="nsew")

        self._build_left(left, cfg)
        self._build_right(right)

        bar = ctk.CTkFrame(self, height=54, fg_color=("gray82", "#080810"), corner_radius=0)
        bar.pack(fill="x", side="bottom")

        ctk.CTkButton(
            bar, text="Cancelar", width=120, height=36,
            fg_color="#333344", hover_color="#444455",
            command=self.destroy,
        ).pack(side="right", padx=(8, 20), pady=9)

        ctk.CTkButton(
            bar, text="Guardar cambios", width=160, height=36,
            fg_color="#1565c0", hover_color="#0d47a1",
            font=ctk.CTkFont(weight="bold"),
            command=self._save,
        ).pack(side="right", padx=4, pady=9)

    # ──────────────────────────────────────────────────────────
    # Panel izquierdo
    # ──────────────────────────────────────────────────────────

    def _build_left(self, parent, cfg: dict):

        def section(icon, text):
            ctk.CTkLabel(parent, text=f"{icon}  {text}",
                         font=ctk.CTkFont(size=11, weight="bold"),
                         text_color="#4fc3f7").pack(anchor="w", padx=20, pady=(14, 4))
            ctk.CTkFrame(parent, height=1, fg_color="#1e3055").pack(fill="x", padx=20, pady=(0, 8))

        # ── IMPRESORA ──────────────────────────────────────
        section("🖨", "Impresora")

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

        self._printer_var = ctk.StringVar(value=cfg.get("printer_name", ""))
        printers = printing.get_printers() or ["(Sin impresoras detectadas)"]
        if self._printer_var.get() and self._printer_var.get() not in printers:
            printers = [self._printer_var.get()] + printers

        self._combo_printer = ctk.CTkComboBox(
            pr, variable=self._printer_var, values=printers,
            width=330, height=34, command=lambda v: self._refresh_preview(),
        )
        self._combo_printer.pack(side="left")
        ctk.CTkButton(pr, text="↺", width=36, height=34, corner_radius=6,
                      fg_color="#1e3050", hover_color="#2a4060",
                      font=ctk.CTkFont(size=14), command=self._refresh_printers,
                      ).pack(side="left", padx=(6, 0))

        # ── SEDE ───────────────────────────────────────────
        section("🏢", "Informacion de la Sede")

        ctk.CTkLabel(parent, text="Sede:", font=ctk.CTkFont(size=11),
                     text_color="#8899aa").pack(anchor="w", padx=20, pady=(0, 2))
        self._sede_var = ctk.StringVar(value=cfg.get("sede", "Cuenca"))
        ctk.CTkComboBox(parent, variable=self._sede_var, values=_SEDES,
                        width=200, height=34,
                        command=lambda v: self._refresh_preview(),
                        ).pack(anchor="w", padx=20, pady=(0, 6))

        ctk.CTkLabel(parent,
                     text="📅  Fecha y hora: automaticas (datetime.now() al imprimir)",
                     font=ctk.CTkFont(size=10), text_color="#445566",
                     ).pack(anchor="w", padx=20, pady=(0, 10))

        # ── DATOS DE VENTA (info) ──────────────────────────
        section("🧾", "Datos incluidos por venta (automaticos)")

        info = ctk.CTkFrame(parent, fg_color=("gray82", "#081208"), corner_radius=8)
        info.pack(fill="x", padx=20, pady=(0, 10))
        for item in [
            "✓  Combo / Paquete comprado",
            "✓  Tiempo del combo",
            "✓  Monto cobrado ($)",
            "✓  Metodo de pago",
            "✓  Nombre del cliente (si fue ingresado)",
            "✓  Folio de la venta (si hay)",
        ]:
            ctk.CTkLabel(info, text=item, font=ctk.CTkFont(size=11),
                         text_color="#3da860", anchor="w").pack(anchor="w", padx=14, pady=1)
        ctk.CTkLabel(info, text="", height=4).pack()

        # ── NUMERACION ─────────────────────────────────────
        section("🔢", "Numeracion de Recibo")

        nr = ctk.CTkFrame(parent, fg_color="transparent")
        nr.pack(fill="x", padx=20, pady=(0, 6))
        ctk.CTkLabel(nr, text="Numero actual:", font=ctk.CTkFont(size=11),
                     text_color="#8899aa", width=120, anchor="w").pack(side="left")
        self._receipt_var = ctk.StringVar(value=str(cfg.get("receipt_number", 1)))
        self._entry_receipt = ctk.CTkEntry(nr, textvariable=self._receipt_var,
                                           width=100, height=32)
        self._entry_receipt.pack(side="left", padx=(0, 10))
        bind_entry(self._entry_receipt)
        self._receipt_next_lbl = ctk.CTkLabel(nr, text="", font=ctk.CTkFont(size=10),
                                              text_color="#445566")
        self._receipt_next_lbl.pack(side="left")
        self._receipt_var.trace_add("write", self._update_receipt_next)
        self._update_receipt_next()

        nb = ctk.CTkFrame(parent, fg_color="transparent")
        nb.pack(fill="x", padx=20, pady=(0, 10))
        ctk.CTkButton(nb, text="Reiniciar (→ 0)", width=150, height=30,
                      fg_color="#5a1010", hover_color="#7f0000",
                      font=ctk.CTkFont(size=11),
                      command=lambda: self._receipt_var.set("0")).pack(side="left", padx=(0, 8))
        ctk.CTkButton(nb, text="Guardar ahora", width=140, height=30,
                      fg_color="#1a3a1a", hover_color="#2e5c2e",
                      font=ctk.CTkFont(size=11),
                      command=self._save_receipt_now).pack(side="left")

        # ── MENSAJE FINAL ──────────────────────────────────
        section("💬", "Mensaje Final del Ticket")

        self._footer_text = ctk.CTkTextbox(parent, height=80, font=ctk.CTkFont(size=12),
                                           fg_color=("gray82", "#0a0d14"))
        self._footer_text.pack(fill="x", padx=20, pady=(0, 8))
        self._footer_text.insert("0.0", cfg.get(
            "footer_message", "Gracias por su visita!\n¡Vuelva pronto y siga disfrutando!"))
        self._footer_text.bind("<KeyRelease>", lambda e: self._refresh_preview())

        # ── OPCIONES ───────────────────────────────────────
        section("⚙", "Opciones")
        self._auto_print_var = ctk.BooleanVar(value=cfg.get("auto_print", True))
        ctk.CTkCheckBox(parent,
                        text="Imprimir automaticamente despues de cada venta",
                        variable=self._auto_print_var,
                        font=ctk.CTkFont(size=12)).pack(anchor="w", padx=20, pady=(0, 16))

    # ──────────────────────────────────────────────────────────
    # Panel derecho — vista previa
    # ──────────────────────────────────────────────────────────

    def _build_right(self, parent):
        ctk.CTkLabel(parent, text="Vista Previa Termica",
                     font=ctk.CTkFont(size=12, weight="bold"),
                     text_color="#4fc3f7").pack(anchor="w", padx=16, pady=(14, 2))
        ctk.CTkLabel(parent, text="80mm · datos de ejemplo",
                     font=ctk.CTkFont(size=10), text_color="#334455").pack(anchor="w", padx=16)

        # ── Selector de combo de muestra ─────────────────
        sel_row = ctk.CTkFrame(parent, fg_color="transparent")
        sel_row.pack(fill="x", padx=14, pady=(8, 4))

        ctk.CTkLabel(sel_row, text="Combo muestra:",
                     font=ctk.CTkFont(size=11), text_color="#8899aa",
                     width=110, anchor="w").pack(side="left")

        self._combo_var = ctk.StringVar(value=self._sample_sale["package_name"])
        self._combo_sel = ctk.CTkComboBox(
            sel_row,
            variable=self._combo_var,
            values=self._combo_names,
            width=190, height=28,
            command=self._on_combo_select,
        )
        self._combo_sel.pack(side="left")

        self._lbl_loading = ctk.CTkLabel(
            sel_row, text="cargando...",
            font=ctk.CTkFont(size=10), text_color="#334455",
        )
        self._lbl_loading.pack(side="left", padx=(8, 0))

        # ── Zona de papel: centra el recibo con ancho fijo ────────────────
        # El papel se centra dentro del panel. Ancho fijo = 42 chars Courier 10pt
        # que simula exactamente el ancho de impresora POS 80mm.
        paper_zone = ctk.CTkFrame(parent, fg_color="transparent")
        paper_zone.pack(fill="both", expand=True, padx=14, pady=(4, 14))

        paper = ctk.CTkFrame(paper_zone, fg_color="#f0ece4", corner_radius=4)
        # place centrado horizontalmente; relheight=1 ocupa todo el alto disponible
        paper.place(relx=0.5, rely=0.0, anchor="n", relheight=1.0)

        self._preview_text = tk.Text(
            paper,
            font=("Courier New", 10),   # 10pt — legible, simula roll 80mm en pantalla
            bg="#f0ece4", fg="#111111",
            relief="flat", bd=0,
            padx=8, pady=8,
            wrap="none",
            width=44,                   # 44 chars = ancho W=42 + margen visual
            state="disabled",
            cursor="arrow",
        )
        self._preview_text.pack(fill="y", expand=True)

    # ──────────────────────────────────────────────────────────
    # Carga de paquetes (background)
    # ──────────────────────────────────────────────────────────

    def _load_packages(self):
        """Carga los paquetes disponibles (servidor + custom) en segundo plano."""
        try:
            packages = api.get_packages()
        except Exception:
            packages = []

        if not packages:
            try:
                self.after(0, lambda: self._lbl_loading.configure(text="sin servidor"))
            except Exception:
                pass
            return

        pkg_map: dict[str, dict] = {}
        names: list[str] = []
        for p in packages:
            name = p.get("name") or p.get("package_name") or str(p.get("id", ""))
            if name and name not in pkg_map:
                pkg_map[name] = p
                names.append(name)

        if not names:
            try:
                self.after(0, lambda: self._lbl_loading.configure(text=""))
            except Exception:
                pass
            return

        def _apply():
            try:
                self._packages_map = pkg_map
                self._combo_names  = names

                # Dropdown de muestra (panel derecho)
                self._combo_sel.configure(values=names)
                if self._combo_var.get() not in names:
                    self._combo_sel.set(names[0])
                    self._on_combo_select(names[0])

                self._lbl_loading.configure(text=f"{len(names)} combos")
            except Exception:
                pass

        try:
            self.after(0, _apply)
        except Exception:
            pass

    def _on_combo_select(self, name: str):
        """Actualiza los datos de muestra con el combo seleccionado y refresca la previa."""
        pkg = self._packages_map.get(name)
        if pkg:
            self._sample_sale["package_name"] = (
                pkg.get("name") or pkg.get("package_name") or name
            )
            self._sample_sale["minutes"] = int(pkg.get("minutes", 60))
            self._sample_sale["amount"]  = float(pkg.get("price", pkg.get("amount", 5.00)))
        else:
            self._sample_sale["package_name"] = name
        self._refresh_preview()

    # ──────────────────────────────────────────────────────────
    # Logica interna
    # ──────────────────────────────────────────────────────────

    def _get_current_config(self) -> dict:
        cfg = dict(printing._DEFAULTS)
        cfg["printer_name"]       = self._printer_var.get()
        cfg["sede"]               = self._sede_var.get()
        cfg["auto_date"]          = True
        cfg["auto_time"]          = True
        try:
            cfg["receipt_number"] = int(self._receipt_var.get())
        except ValueError:
            cfg["receipt_number"] = self._cfg.get("receipt_number", 1)
        cfg["footer_message"]     = self._footer_text.get("0.0", "end").rstrip("\n")
        cfg["auto_print"]         = self._auto_print_var.get()
        return cfg

    def _refresh_preview(self, *_):
        cfg  = self._get_current_config()
        text = printing.format_preview(self._sample_sale, cfg)
        self._preview_text.configure(state="normal")
        self._preview_text.delete("0.0", "end")
        self._preview_text.insert("0.0", text)
        self._preview_text.configure(state="disabled")

    def _refresh_printers(self):
        printers = printing.get_printers() or ["(Sin impresoras detectadas)"]
        self._combo_printer.configure(values=printers)
        if self._printer_var.get() not in printers:
            self._combo_printer.set(printers[0])

    def _update_receipt_next(self, *_):
        try:
            n = int(self._receipt_var.get())
            self._receipt_next_lbl.configure(text=f"→ siguiente: {n + 1}")
        except ValueError:
            self._receipt_next_lbl.configure(text="")

    def _save_receipt_now(self):
        try:
            n = int(self._receipt_var.get())
        except ValueError:
            return
        cfg = printing.load_config()
        cfg["receipt_number"] = n
        printing.save_config(cfg)

    def _save(self):
        printing.save_config(self._get_current_config())
        self.destroy()
