"""
Popup overlay para agregar o editar un combo personalizado.

Diseño:
  - Overlay oscuro sobre la ventana principal
  - Card centrada con formulario y vista previa
  - Color pickers con swatch (OS color dialog)
  - Vista previa con fondo abstracto reactivo

Guarda en app/data/custom_combos.json.
"""
import json
import math
import os
import random
import threading
import tkinter as tk
from tkinter.colorchooser import askcolor
from typing import Callable, Optional
import uuid

import customtkinter as ctk
from ui.kb_shortcuts import bind_entry

_DATA_FILE = os.path.join(
    os.path.dirname(os.path.dirname(__file__)), "data", "custom_combos.json"
)


# ── Helpers ──────────────────────────────────────────────────────────────────

def _darken(col: str, f: float) -> str:
    try:
        h = col.lstrip("#")
        r, g, b = int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)
        return f"#{int(r*f):02x}{int(g*f):02x}{int(b*f):02x}"
    except Exception:
        return col


def _blob_pts(cx, cy, rx, ry, n=12, roughness=0.36, rng=None):
    rng = rng or random
    pts = []
    for i in range(n):
        a = 2 * math.pi * i / n
        pts += [
            cx + rx * (1 + rng.uniform(-roughness, roughness)) * math.cos(a),
            cy + ry * (1 + rng.uniform(-roughness, roughness)) * math.sin(a),
        ]
    return pts


# ── Persistencia ─────────────────────────────────────────────────────────────

def load_custom_combos() -> list:
    try:
        with open(_DATA_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    except Exception:
        return []


def save_custom_combos(combos: list):
    os.makedirs(os.path.dirname(_DATA_FILE), exist_ok=True)
    with open(_DATA_FILE, "w", encoding="utf-8") as f:
        json.dump(combos, f, indent=2, ensure_ascii=False)


def _dominant_color(g: int, b: int, r: int) -> str:
    if g >= b and g >= r and g > 0:
        return "green"
    if b >= r and b > 0:
        return "blue"
    if r > 0:
        return "red"
    return "blue"


# ── Dialog ───────────────────────────────────────────────────────────────────

class AddComboDialog(ctk.CTkFrame):
    """
    Overlay popup para crear o editar un combo personalizado.

    Parametros:
      on_saved(pkg)  — callback al guardar exitosamente
      on_deleted()   — callback al eliminar (solo modo edicion)
      edit_combo     — dict del combo a editar (None = modo creacion)
    """

    def __init__(
        self,
        parent,
        on_saved: Optional[Callable] = None,
        on_deleted: Optional[Callable] = None,
        edit_combo: Optional[dict] = None,
    ):
        super().__init__(parent, fg_color="#08081a")
        self.on_saved   = on_saved
        self.on_deleted = on_deleted

        data = edit_combo or {}
        self._edit_mode = edit_combo is not None
        self._is_custom = data.get("custom", False)
        self._combo_id  = data.get("id")

        self._color1     = data.get("color1",     "#1565c0")
        self._color2     = data.get("color2",     "#0d47a1")
        self._text_color = data.get("text_color", "#ffffff")

        self._new_id = self._combo_id or self._generate_id()

        self._bold_var  = tk.BooleanVar(value=data.get("bold", False))
        self._font_size = tk.StringVar(value=data.get("font_size", "M"))

        # Overlay cubre toda la ventana
        self.place(relx=0, rely=0, relwidth=1, relheight=1)
        self.lift()

        # Card centrada
        self._card = ctk.CTkFrame(
            self, width=720, height=620,
            fg_color="#12121e",
            corner_radius=18, border_width=2,
            border_color="#2a2a44",
        )
        self._card.place(relx=0.5, rely=0.5, anchor="center")
        self._card.pack_propagate(False)

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

    @staticmethod
    def _generate_id() -> str:
        """Genera ID basado en agent_id: C-CUE001-01, C-CUE001-02, etc."""
        import api
        agent_id = api.load_config().get("agent_id", "LOCAL")
        existing = load_custom_combos()
        prefix = f"C-{agent_id}-"
        max_n = 0
        for c in existing:
            cid = str(c.get("id", ""))
            if cid.startswith(prefix):
                try:
                    max_n = max(max_n, int(cid[len(prefix):]))
                except ValueError:
                    pass
        return f"{prefix}{max_n + 1:02d}"

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

    def _build(self, data: dict):
        card = self._card

        # Barra acento superior
        ctk.CTkFrame(
            card, height=4, fg_color=self._color1, corner_radius=2,
        ).pack(fill="x", padx=50, pady=(14, 0))

        # Header
        hdr = ctk.CTkFrame(card, fg_color="transparent")
        hdr.pack(fill="x", padx=20, pady=(12, 4))

        title_text = "EDITAR COMBO" if self._edit_mode else "NUEVO COMBO"
        ctk.CTkLabel(
            hdr, text=title_text,
            font=ctk.CTkFont(size=17, weight="bold"),
            text_color="#4fc3f7",
        ).pack(side="left")

        # Boton cerrar
        ctk.CTkButton(
            hdr, text="X", width=28, height=28, corner_radius=6,
            fg_color="transparent", hover_color="#2a2a44",
            text_color="#777777", font=ctk.CTkFont(size=13),
            command=self._close,
        ).pack(side="right")

        ctk.CTkFrame(card, height=1, fg_color="#2a2a44").pack(fill="x", padx=16, pady=(4, 0))

        # Footer con botones (pack bottom primero)
        self._build_footer(card)
        ctk.CTkFrame(card, height=1, fg_color="#2a2a44").pack(fill="x", padx=16, side="bottom")

        # Body 2 columnas
        body = ctk.CTkFrame(card, fg_color="transparent")
        body.pack(fill="both", expand=True)
        body.grid_columnconfigure(0, weight=1)
        body.grid_columnconfigure(1, weight=0, minsize=240)
        body.grid_rowconfigure(0, weight=1)

        self._build_left(body, data)
        self._build_right(body)

    def _section(self, parent, text: str):
        ctk.CTkLabel(
            parent, text=text.upper(),
            font=ctk.CTkFont(size=9, weight="bold"),
            text_color="#3a8aaa",
        ).pack(anchor="w", pady=(10, 1))
        ctk.CTkFrame(parent, height=1, fg_color="#1a2a3a").pack(fill="x", pady=(0, 6))

    def _build_left(self, body, data: dict):
        left = ctk.CTkFrame(body, fg_color="transparent")
        left.grid(row=0, column=0, sticky="nsew", padx=(16, 6), pady=8)

        # ── IDENTIFICADOR ─────────────────────────────────────────────────
        self._section(left, "Identificador")

        id_frame = ctk.CTkFrame(left, fg_color="#0d1525", corner_radius=6, height=36)
        id_frame.pack(fill="x", pady=(0, 2), padx=2)
        id_frame.pack_propagate(False)
        ctk.CTkLabel(
            id_frame, text=self._new_id,
            font=ctk.CTkFont(family="Courier New", size=14, weight="bold"),
            text_color="#4fc3f7", anchor="w",
        ).pack(fill="both", padx=10, pady=4)

        id_hint = ("Ajusta los valores LED de este paquete."
                   if self._edit_mode and not self._is_custom
                   else "Crea en el servidor un paquete con este ID para asignarle nombre y precio.")
        ctk.CTkLabel(
            left,
            text=id_hint,
            font=ctk.CTkFont(size=10), text_color="#3a4455", wraplength=360,
        ).pack(anchor="w", pady=(0, 4))

        # ── VISUAL ────────────────────────────────────────────────────────
        self._section(left, "Colores")

        clr_row = ctk.CTkFrame(left, fg_color="transparent")
        clr_row.pack(fill="x", pady=(0, 2))

        self._btn_c1 = self._color_swatch(clr_row, "Base",    self._color1,
                                           lambda: self._pick("_color1", self._btn_c1))
        self._btn_c1.pack(side="left", padx=(0, 16))

        self._btn_c2 = self._color_swatch(clr_row, "Gradiente", self._color2,
                                           lambda: self._pick("_color2", self._btn_c2))
        self._btn_c2.pack(side="left", padx=(0, 16))

        self._btn_ct = self._color_swatch(clr_row, "Texto",     self._text_color,
                                           lambda: self._pick("_text_color", self._btn_ct))
        self._btn_ct.pack(side="left")

        # ── TEXTO ─────────────────────────────────────────────────────────
        self._section(left, "Texto")

        tx_row = ctk.CTkFrame(left, fg_color="transparent")
        tx_row.pack(fill="x", pady=(0, 2))

        ctk.CTkSwitch(
            tx_row, text="Negrita",
            variable=self._bold_var, onvalue=True, offvalue=False,
            font=ctk.CTkFont(size=12), text_color="#aaaaaa",
            progress_color="#1565c0",
            command=self._update_preview,
        ).pack(side="left", padx=(0, 20))

        ctk.CTkLabel(tx_row, text="Tam:",
                     font=ctk.CTkFont(size=11), text_color="#778899").pack(side="left", padx=(0, 4))
        ctk.CTkSegmentedButton(
            tx_row, values=["S", "M", "L"],
            variable=self._font_size,
            width=90, height=26,
            font=ctk.CTkFont(size=11, weight="bold"),
            selected_color="#1565c0", selected_hover_color="#0d47a1",
            unselected_color="#1c2030", unselected_hover_color="#252840",
            command=lambda _: self._update_preview(),
        ).pack(side="left")

        # ── TEMPORIZADOR LED ──────────────────────────────────────────────
        self._section(left, "Temporizador LED")

        led_row = ctk.CTkFrame(left, fg_color="transparent")
        led_row.pack(fill="x")

        for label, color_hex, attr in [
            ("Verde", "#4caf50", "_entry_green"),
            ("Azul",  "#2196f3", "_entry_blue"),
            ("Rojo",  "#f44336", "_entry_red"),
        ]:
            col_f = ctk.CTkFrame(led_row, fg_color="transparent")
            col_f.pack(side="left", padx=(0, 14))
            ctk.CTkLabel(col_f, text=f"  {label}",
                         font=ctk.CTkFont(size=11), text_color=color_hex).pack(anchor="w")
            e = ctk.CTkEntry(col_f, placeholder_text="0", width=68,
                             font=ctk.CTkFont(size=12), height=30)
            e.pack()
            setattr(self, attr, e)

        for field, key in [("_entry_green", "minutes_green"),
                            ("_entry_blue",  "minutes_blue"),
                            ("_entry_red",   "minutes_red")]:
            val = data.get(key, 0)
            if val:
                getattr(self, field).insert(0, str(val))
            bind_entry(getattr(self, field))

        ctk.CTkLabel(
            left,
            text="Deja en 0 los colores LED que no uses.",
            font=ctk.CTkFont(size=10), text_color="#3a4455", wraplength=360,
        ).pack(anchor="w", pady=(4, 0))

    def _color_swatch(self, parent, label: str, color: str, command: Callable):
        frame = ctk.CTkFrame(parent, fg_color="transparent")
        ctk.CTkLabel(frame, text=label,
                     font=ctk.CTkFont(size=10), text_color="#667788").pack(anchor="w")
        btn = ctk.CTkButton(
            frame, text="", width=44, height=24, corner_radius=6,
            fg_color=color, hover_color=color,
            border_width=1, border_color="#404060",
            command=command,
        )
        btn.pack()
        return frame

    def _build_right(self, body):
        right = ctk.CTkFrame(body, fg_color="#080c18", corner_radius=0)
        right.grid(row=0, column=1, sticky="nsew")

        ctk.CTkLabel(
            right, text="Vista Previa",
            font=ctk.CTkFont(size=12, weight="bold"),
            text_color="#3a8aaa",
        ).pack(anchor="w", padx=14, pady=(14, 6))

        self._preview_frame = ctk.CTkFrame(
            right, corner_radius=12, border_width=2,
            border_color=self._color1, fg_color=_darken(self._color1, 0.17),
        )
        self._preview_frame.pack(padx=14, pady=(0, 10), fill="x")

        self._preview = tk.Canvas(
            self._preview_frame, bg=_darken(self._color1, 0.17),
            highlightthickness=0, bd=0, height=110,
        )
        self._preview.pack(padx=3, pady=3, fill="x")
        self._preview.bind("<Configure>", lambda e: self._update_preview())

        ctk.CTkLabel(
            right, text="Se actualiza en tiempo real",
            font=ctk.CTkFont(size=10), text_color="#334455",
        ).pack(padx=14, pady=(0, 6))

    def _build_footer(self, card):
        footer = ctk.CTkFrame(card, fg_color="transparent")
        footer.pack(fill="x", padx=18, pady=(6, 12), side="bottom")

        self._lbl_error = ctk.CTkLabel(
            footer, text="",
            font=ctk.CTkFont(size=11), text_color="#f44336",
        )
        self._lbl_error.pack(anchor="w", pady=(0, 4))

        btn_row = ctk.CTkFrame(footer, fg_color="transparent")
        btn_row.pack(anchor="e")

        if self._edit_mode:
            ctk.CTkButton(
                btn_row, text="Eliminar",
                width=100, height=34, corner_radius=8,
                fg_color="#6a0f0f", hover_color="#b71c1c",
                font=ctk.CTkFont(size=13),
                command=self._confirm_delete,
            ).pack(side="left", padx=(0, 8))

        ctk.CTkButton(
            btn_row, text="Cancelar",
            width=100, height=34, corner_radius=8,
            fg_color="#1e2232", hover_color="#2a2e44",
            font=ctk.CTkFont(size=13),
            command=self._close,
        ).pack(side="left", padx=(0, 8))

        ctk.CTkButton(
            btn_row, text="Guardar",
            width=110, height=34, corner_radius=8,
            fg_color="#1565c0", hover_color="#0d47a1",
            font=ctk.CTkFont(size=13, weight="bold"),
            command=self._save,
        ).pack(side="left")

    # ── Color picker ─────────────────────────────────────────────────────────

    def _pick(self, attr: str, swatch_frame: ctk.CTkFrame):
        current = getattr(self, attr)
        result  = askcolor(color=current, title="Seleccionar color",
                           parent=self.winfo_toplevel())
        self.lift()
        if result[1]:
            setattr(self, attr, result[1])
            btn = swatch_frame.winfo_children()[-1]
            btn.configure(fg_color=result[1], hover_color=result[1])
            if attr in ("_color1", "_color2"):
                self._preview_frame.configure(border_color=self._color1)
                self._preview.configure(bg=_darken(self._color1, 0.17))
                self._preview_frame.configure(fg_color=_darken(self._color1, 0.17))
            self._update_preview()

    # ── Vista previa ─────────────────────────────────────────────────────────

    def _update_preview(self):
        cv = self._preview
        w  = cv.winfo_width()
        h  = cv.winfo_height() or 110
        if w <= 1:
            return

        c1  = self._color1
        c2  = self._color2
        bg  = _darken(c1, 0.17)
        blobs = [c1, c2, _darken(c1, 0.52), _darken(c2, 0.52), _darken(c1, 0.33)]

        cv.delete("all")
        cv.create_rectangle(0, 0, w, h, fill=bg, outline="")

        rng = random.Random(99999)
        for col in blobs:
            cx = rng.uniform(-0.15, 1.15) * w
            cy = rng.uniform(-0.15, 1.15) * h
            rx = rng.uniform(0.18, 0.54) * w
            ry = rng.uniform(0.18, 0.54) * h
            pts = _blob_pts(cx, cy, rx, ry, rng=rng)
            cv.create_polygon(pts, smooth=True, fill=col, outline="")

        name = self._new_id
        ts   = "--- min"
        ps   = "$---"

        tcol   = self._text_color
        sz_map = {"S": 10, "M": 13, "L": 16}
        sz     = sz_map.get(self._font_size.get(), 13)
        bold   = "bold" if self._bold_var.get() else ""

        cy_n = h // 2 - 12
        cy_p = h // 2 + 12

        cv.create_text(w//2+1, cy_n+1, text=name, fill="#000",
                       font=("Segoe UI", sz, "bold"), anchor="center")
        cv.create_text(w//2+1, cy_p+1, text=f"{ts}  |  {ps}", fill="#000",
                       font=("Segoe UI", 9), anchor="center")
        cv.create_text(w//2, cy_n, text=name, fill=tcol,
                       font=("Segoe UI", sz, "bold" if not bold else bold), anchor="center")
        cv.create_text(w//2, cy_p, text=f"{ts}  |  {ps}", fill=c1,
                       font=("Segoe UI", 9), anchor="center")

    # ── Validacion y guardado ────────────────────────────────────────────────

    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

    def _save(self):
        self._lbl_error.configure(text="")

        code = self._new_id

        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_error.configure(text="Los minutos LED deben ser enteros >= 0.")
            return

        new_combo = {
            "id":            code,
            "custom":        True,
            "color1":        self._color1,
            "color2":        self._color2,
            "text_color":    self._text_color,
            "bold":          self._bold_var.get(),
            "font_size":     self._font_size.get(),
            "minutes_green": g,
            "minutes_blue":  b,
            "minutes_red":   r,
        }

        combos = load_custom_combos()
        if self._edit_mode and self._combo_id:
            combos = [c for c in combos if c["id"] != self._combo_id]
        combos.append(new_combo)
        save_custom_combos(combos)

        # Registrar combo en el servidor con config visual (fire-and-forget, idempotente)
        import api
        threading.Thread(
            target=api.register_combo, args=(code, new_combo), daemon=True,
        ).start()

        if self.on_saved:
            self.on_saved(new_combo)
        self._close()

    # ── Eliminacion ──────────────────────────────────────────────────────────

    def _confirm_delete(self):
        # Overlay de confirmacion dentro de este mismo overlay
        self._del_overlay = ctk.CTkFrame(self, fg_color="#08081a")
        self._del_overlay.place(relx=0, rely=0, relwidth=1, relheight=1)
        self._del_overlay.lift()

        box = ctk.CTkFrame(
            self._del_overlay, width=320, fg_color="#12121e",
            corner_radius=16, border_width=2, border_color="#442a2a",
        )
        box.place(relx=0.5, rely=0.45, anchor="center")

        ctk.CTkLabel(
            box, text="Eliminar combo?",
            font=ctk.CTkFont(size=15, weight="bold"),
            text_color="#f44336",
        ).pack(pady=(24, 4))

        ctk.CTkLabel(
            box, text="Esta accion no se puede deshacer.",
            font=ctk.CTkFont(size=12), text_color="#888888",
        ).pack(pady=(0, 18))

        row = ctk.CTkFrame(box, fg_color="transparent")
        row.pack(pady=(0, 22))

        ctk.CTkButton(
            row, text="Cancelar", width=100, height=32,
            corner_radius=8, fg_color="#252535", hover_color="#353545",
            command=lambda: self._del_overlay.destroy(),
        ).pack(side="left", padx=6)

        ctk.CTkButton(
            row, text="Eliminar", width=100, height=32,
            corner_radius=8, fg_color="#b71c1c", hover_color="#7f0f0f",
            command=lambda: [self._del_overlay.destroy(), self._delete()],
        ).pack(side="left", padx=6)

    def _delete(self):
        combos = [c for c in load_custom_combos() if c["id"] != self._combo_id]
        save_custom_combos(combos)
        # Notificar al servidor para que elimine del registro + sync al cloud
        try:
            import api
            api.unregister_combo(self._combo_id)
        except Exception as e:
            print(f"[AddCombo] Error desregistrando combo del servidor: {e}", flush=True)
        if self.on_deleted:
            self.on_deleted()
        self._close()

    # ── Cerrar ───────────────────────────────────────────────────────────────

    def _close(self):
        self.place_forget()
        self.destroy()
