package controllers

import (
	"fmt"
	"math"
	"regexp"
	"strconv"
	"strings"

	"github.com/gofiber/fiber/v2"
	"github.com/sujit-baniya/fiber-boilerplate/app"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/services"
)

// UmindIndex renderiza el panel de administración de uMind.
func UmindIndex(c *fiber.Ctx) error {
	return c.Render("umind", fiber.Map{
		"user":    c.Locals("user"),
		"modules": c.Locals("userModules"),
	}, "layouts/main")
}

var umindColorHexRegex = regexp.MustCompile(`^#[0-9a-fA-F]{6}$`)

// umindColor valida el hex del color de marca del widget; si viene vacío o
// inválido cae al verde por defecto en vez de guardar basura (el valor se
// aplica tal cual como CSS custom property en el widget embebido).
func umindColor(color string) string {
	color = strings.TrimSpace(color)
	if umindColorHexRegex.MatchString(color) {
		return color
	}
	return "#8eb02f"
}

// ─── Tenants ─────────────────────────────────────────────────────────────────
// Un tenant es el negocio/sitio dueño de los dominios permitidos — la config
// de IA, tono, tools, etc. viven en sus UmindAgente (ver más abajo).

func GetUmindTenants(c *fiber.Ctx) error {
	page, _ := strconv.Atoi(c.Query("page", "1"))
	if page < 1 {
		page = 1
	}
	limit := 20
	offset := (page - 1) * limit
	items, total, err := models.GetAllUmindTenants(limit, offset)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{
		"items":      items,
		"total":      total,
		"totalPages": int(math.Ceil(float64(total) / float64(limit))),
		"page":       page,
	})
}

type umindTenantReq struct {
	Nombre             string   `json:"nombre"`
	DominiosPermitidos []string `json:"dominios_permitidos"`
	Activo             bool     `json:"activo"`
	ClienteID          *uint    `json:"cliente_id"`
	PlanID             *uint    `json:"plan_id"`
}

func (r umindTenantReq) dominiosLimpios() []string {
	var out []string
	for _, d := range r.DominiosPermitidos {
		d = strings.ToLower(strings.TrimSpace(d))
		if d != "" {
			out = append(out, d)
		}
	}
	return out
}

func CreateUmindTenantHandler(c *fiber.Ctx) error {
	var req umindTenantReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	if strings.TrimSpace(req.Nombre) == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "nombre es requerido"})
	}
	dominios := req.dominiosLimpios()
	if len(dominios) == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "agrega al menos un dominio permitido"})
	}

	tenant := &models.UmindTenant{
		Nombre:             strings.TrimSpace(req.Nombre),
		DominiosPermitidos: strings.Join(dominios, ","),
		Activo:             true,
		CreadoPorID:        extraerUserID(c),
		ClienteID:          req.ClienteID,
		PlanID:             req.PlanID,
	}
	if err := models.CreateUmindTenant(tenant); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(fiber.StatusCreated).JSON(fiber.Map{"ok": true, "id": tenant.ID})
}

func UpdateUmindTenantHandler(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	var req umindTenantReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	dominios := req.dominiosLimpios()
	updates := map[string]interface{}{
		"nombre":              strings.TrimSpace(req.Nombre),
		"dominios_permitidos": strings.Join(dominios, ","),
		"activo":              req.Activo,
		"cliente_id":          req.ClienteID,
		"plan_id":             req.PlanID,
	}
	if err := models.UpdateUmindTenant(uint(id), updates); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

func DeleteUmindTenantHandler(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	if err := models.DeleteUmindTenant(uint(id)); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// ─── Agentes ─────────────────────────────────────────────────────────────────
// Un tenant puede tener varios agentes independientes (ej. "Ventas",
// "Soporte"), cada uno con su propia config de IA, base de conocimiento,
// tools, canales y conexión de correo.

func GetUmindAgentesHandler(c *fiber.Ctx) error {
	tenantID, err := strconv.ParseUint(c.Query("tenant_id"), 10, 64)
	if err != nil || tenantID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tenant_id requerido"})
	}
	items, err := models.GetUmindAgentesByTenant(uint(tenantID))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"items": items})
}

type umindAgenteReq struct {
	TenantID          uint   `json:"tenant_id"`
	Nombre            string `json:"nombre"`
	AiConfigID        *uint  `json:"ai_config_id"`
	Tono              string `json:"tono"`
	MensajeBienvenida string `json:"mensaje_bienvenida"`
	Color             string `json:"color"`
	Activo            bool   `json:"activo"`
}

func CreateUmindAgenteHandler(c *fiber.Ctx) error {
	var req umindAgenteReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	if req.TenantID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tenant_id requerido"})
	}
	if strings.TrimSpace(req.Nombre) == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "nombre es requerido"})
	}
	if _, err := models.GetUmindTenantByID(req.TenantID); err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "tenant no encontrado"})
	}

	agente := &models.UmindAgente{
		TenantID:          req.TenantID,
		Nombre:            strings.TrimSpace(req.Nombre),
		AiConfigID:        req.AiConfigID,
		Tono:              req.Tono,
		MensajeBienvenida: req.MensajeBienvenida,
		Color:             umindColor(req.Color),
		Activo:            true,
	}
	if err := models.CreateUmindAgente(agente); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(fiber.StatusCreated).JSON(fiber.Map{"ok": true, "id": agente.ID, "site_key": agente.SiteKey})
}

func UpdateUmindAgenteHandler(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	var req umindAgenteReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	updates := map[string]interface{}{
		"nombre":             strings.TrimSpace(req.Nombre),
		"ai_config_id":       req.AiConfigID,
		"tono":               req.Tono,
		"mensaje_bienvenida": req.MensajeBienvenida,
		"color":              umindColor(req.Color),
		"activo":             req.Activo,
	}
	if err := models.UpdateUmindAgente(uint(id), updates); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

func DeleteUmindAgenteHandler(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	if err := models.DeleteUmindAgente(uint(id)); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// ─── Documentos / ingesta ────────────────────────────────────────────────────

func GetUmindDocumentosHandler(c *fiber.Ctx) error {
	agenteID, err := strconv.ParseUint(c.Query("agente_id"), 10, 64)
	if err != nil || agenteID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "agente_id requerido"})
	}
	items, err := models.GetUmindDocumentosByAgente(uint(agenteID))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"items": items})
}

// CreateUmindDocumentoHandler registra una nueva fuente (por ahora solo URL a
// crawlear) y dispara la ingesta en segundo plano — puede tardar varios
// segundos/minutos según cuántas páginas tenga el sitio.
func CreateUmindDocumentoHandler(c *fiber.Ctx) error {
	var req struct {
		AgenteID   uint   `json:"agente_id"`
		URL        string `json:"url"`
		MaxPaginas int    `json:"max_paginas"`
	}
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	if req.AgenteID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "agente_id requerido"})
	}
	if strings.TrimSpace(req.URL) == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "url requerida"})
	}
	if _, err := models.GetUmindAgenteByID(req.AgenteID); err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "agente no encontrado"})
	}

	doc := &models.UmindDocumento{
		AgenteID: req.AgenteID,
		Tipo:     "url",
		Origen:   strings.TrimSpace(req.URL),
		Estado:   "procesando",
	}
	if err := models.CreateUmindDocumento(doc); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	go services.IngestarAgente(req.AgenteID, doc.ID, doc.Origen, req.MaxPaginas)

	return c.Status(fiber.StatusAccepted).JSON(fiber.Map{"ok": true, "id": doc.ID, "estado": "procesando"})
}

func DeleteUmindDocumentoHandler(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	if err := models.DeleteUmindDocumento(uint(id)); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// ─── Conversaciones ───────────────────────────────────────────────────────────

func GetUmindSesionesHandler(c *fiber.Ctx) error {
	agenteID, err := strconv.ParseUint(c.Query("agente_id"), 10, 64)
	if err != nil || agenteID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "agente_id requerido"})
	}
	items, err := models.GetUmindSesiones(uint(agenteID), 50)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"items": items})
}

func GetUmindHistorialHandler(c *fiber.Ctx) error {
	agenteID, err := strconv.ParseUint(c.Query("agente_id"), 10, 64)
	if err != nil || agenteID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "agente_id requerido"})
	}
	sessionID := c.Query("session_id")
	if sessionID == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "session_id requerido"})
	}
	items, err := models.GetUmindHistorial(uint(agenteID), sessionID, 200)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"items": items})
}

// ─── Tools custom (webhooks) ───────────────────────────────────────────────

var umindNombreToolRegex = regexp.MustCompile(`^[a-z][a-z0-9_]{2,63}$`)

func GetUmindHerramientasHandler(c *fiber.Ctx) error {
	agenteID, err := strconv.ParseUint(c.Query("agente_id"), 10, 64)
	if err != nil || agenteID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "agente_id requerido"})
	}
	items, err := models.GetUmindHerramientasByAgente(uint(agenteID))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	// No se devuelve el secreto cifrado ni el descifrado — solo si hay uno configurado.
	out := make([]fiber.Map, len(items))
	for i, h := range items {
		out[i] = fiber.Map{
			"ID": h.ID, "agente_id": h.AgenteID, "nombre": h.Nombre, "descripcion": h.Descripcion,
			"parametros_json": h.ParametrosJSON, "url": h.URL, "auth_header_nombre": h.AuthHeaderNombre,
			"auth_configurado": h.AuthHeaderValorEnc != "", "activa": h.Activa,
		}
	}
	return c.JSON(fiber.Map{"items": out})
}

type umindHerramientaReq struct {
	AgenteID         uint                               `json:"agente_id"`
	Nombre           string                             `json:"nombre"`
	Descripcion      string                             `json:"descripcion"`
	Parametros       []models.UmindHerramientaParametro `json:"parametros"`
	URL              string                             `json:"url"`
	AuthHeaderNombre string                             `json:"auth_header_nombre"`
	AuthHeaderValor  *string                            `json:"auth_header_valor"` // nil = no tocar (en updates)
	Activa           bool                               `json:"activa"`
}

func CreateUmindHerramientaHandler(c *fiber.Ctx) error {
	var req umindHerramientaReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	if req.AgenteID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "agente_id requerido"})
	}
	if !umindNombreToolRegex.MatchString(req.Nombre) {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "el nombre debe ser minúsculas/números/guion_bajo, empezar con letra (3-64 caracteres)"})
	}
	if !strings.HasPrefix(strings.ToLower(strings.TrimSpace(req.URL)), "https://") {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "la URL debe ser https"})
	}

	parametrosJSON, err := models.ParametrosToJSON(req.Parametros)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "parametros inválidos"})
	}
	var authEnc string
	if req.AuthHeaderValor != nil && *req.AuthHeaderValor != "" {
		authEnc, err = services.CifrarSecretoUmind(*req.AuthHeaderValor)
		if err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
		}
	}

	h := &models.UmindHerramienta{
		AgenteID: req.AgenteID, Nombre: req.Nombre, Descripcion: strings.TrimSpace(req.Descripcion),
		ParametrosJSON: parametrosJSON, URL: strings.TrimSpace(req.URL),
		AuthHeaderNombre: strings.TrimSpace(req.AuthHeaderNombre), AuthHeaderValorEnc: authEnc, Activa: true,
	}
	if err := models.CreateUmindHerramienta(h); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(fiber.StatusCreated).JSON(fiber.Map{"ok": true, "id": h.ID})
}

func UpdateUmindHerramientaHandler(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	var req umindHerramientaReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	if !umindNombreToolRegex.MatchString(req.Nombre) {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "el nombre debe ser minúsculas/números/guion_bajo, empezar con letra (3-64 caracteres)"})
	}
	if !strings.HasPrefix(strings.ToLower(strings.TrimSpace(req.URL)), "https://") {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "la URL debe ser https"})
	}
	parametrosJSON, err := models.ParametrosToJSON(req.Parametros)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "parametros inválidos"})
	}

	updates := map[string]interface{}{
		"nombre": req.Nombre, "descripcion": strings.TrimSpace(req.Descripcion),
		"parametros_json": parametrosJSON, "url": strings.TrimSpace(req.URL),
		"auth_header_nombre": strings.TrimSpace(req.AuthHeaderNombre), "activa": req.Activa,
	}
	if req.AuthHeaderValor != nil {
		if *req.AuthHeaderValor == "" {
			updates["auth_header_valor_enc"] = ""
		} else {
			enc, err := services.CifrarSecretoUmind(*req.AuthHeaderValor)
			if err != nil {
				return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
			}
			updates["auth_header_valor_enc"] = enc
		}
	}
	if err := models.UpdateUmindHerramienta(uint(id), updates); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

func DeleteUmindHerramientaHandler(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	if err := models.DeleteUmindHerramienta(uint(id)); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// ─── Canales (Telegram / WhatsApp) ─────────────────────────────────────────

func GetUmindCanalesHandler(c *fiber.Ctx) error {
	agenteID, err := strconv.ParseUint(c.Query("agente_id"), 10, 64)
	if err != nil || agenteID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "agente_id requerido"})
	}
	items, err := models.GetUmindCanalesByAgente(uint(agenteID))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	out := make([]fiber.Map, len(items))
	for i, canal := range items {
		webhookURL := ""
		if canal.Tipo == "telegram" {
			webhookURL = fmt.Sprintf("%s/webhooks/umind-telegram/%s", app.Http.Server.Url, canal.WebhookSecret)
		} else if canal.Tipo == "whatsapp" {
			webhookURL = fmt.Sprintf("%s/webhooks/umind-whatsapp/%s", app.Http.Server.Url, canal.WebhookSecret)
		}
		out[i] = fiber.Map{
			"ID": canal.ID, "agente_id": canal.AgenteID, "tipo": canal.Tipo, "activo": canal.Activo,
			"webhook_url": webhookURL, "ultimo_error": canal.UltimoError,
			"usar_whisper_audio": canal.UsarWhisperAudio, "usar_ocr_imagenes": canal.UsarOcrImagenes,
		}
	}
	return c.JSON(fiber.Map{"items": out})
}

type umindCanalReq struct {
	AgenteID         uint              `json:"agente_id"`
	Tipo             string            `json:"tipo"`
	Credenciales     map[string]string `json:"credenciales"`
	Activo           bool              `json:"activo"`
	UsarWhisperAudio bool              `json:"usar_whisper_audio"`
	UsarOcrImagenes  bool              `json:"usar_ocr_imagenes"`
}

func CreateUmindCanalHandler(c *fiber.Ctx) error {
	var req umindCanalReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	if req.AgenteID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "agente_id requerido"})
	}
	switch req.Tipo {
	case "telegram":
		if strings.TrimSpace(req.Credenciales["bot_token"]) == "" {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "bot_token requerido"})
		}
	case "whatsapp":
		for _, k := range []string{"phone_number_id", "access_token", "app_secret", "verify_token"} {
			if strings.TrimSpace(req.Credenciales[k]) == "" {
				return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": k + " requerido"})
			}
		}
	default:
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "tipo debe ser telegram o whatsapp"})
	}

	credencialesEnc, err := services.CifrarCredencialesCanal(req.Credenciales)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	canal := &models.UmindCanal{
		AgenteID: req.AgenteID, Tipo: req.Tipo, Activo: true, CredencialesEnc: credencialesEnc,
		UsarWhisperAudio: req.UsarWhisperAudio, UsarOcrImagenes: req.UsarOcrImagenes,
	}
	if err := models.CreateUmindCanal(canal); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	if req.Tipo == "telegram" {
		webhookURL := fmt.Sprintf("%s/webhooks/umind-telegram/%s", app.Http.Server.Url, canal.WebhookSecret)
		if err := services.RegistrarWebhookTelegram(req.Credenciales["bot_token"], webhookURL); err != nil {
			models.UpdateUmindCanal(canal.ID, map[string]interface{}{"ultimo_error": err.Error()})
		}
	}
	return c.Status(fiber.StatusCreated).JSON(fiber.Map{"ok": true, "id": canal.ID, "webhook_secret": canal.WebhookSecret})
}

func UpdateUmindCanalHandler(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	var req umindCanalReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	updates := map[string]interface{}{"activo": req.Activo, "usar_whisper_audio": req.UsarWhisperAudio, "usar_ocr_imagenes": req.UsarOcrImagenes}
	if len(req.Credenciales) > 0 {
		enc, err := services.CifrarCredencialesCanal(req.Credenciales)
		if err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
		}
		updates["credenciales_enc"] = enc
		updates["ultimo_error"] = ""
	}
	if err := models.UpdateUmindCanal(uint(id), updates); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

func DeleteUmindCanalHandler(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	if err := models.DeleteUmindCanal(uint(id)); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// ─── Chat de prueba ─────────────────────────────────────────────────────────

// UmindChatPruebaHandler deja que el staff pruebe un agente puntual directo
// desde el panel, sin pasar por site_key/dominio (ya está gateado por la
// sesión con la que se llega acá).
func UmindChatPruebaHandler(c *fiber.Ctx) error {
	var req struct {
		AgenteID  uint   `json:"agente_id"`
		SessionID string `json:"session_id"`
		Mensaje   string `json:"mensaje"`
	}
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	if strings.TrimSpace(req.Mensaje) == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "mensaje requerido"})
	}
	agente, err := models.GetUmindAgenteByID(req.AgenteID)
	if err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "agente no encontrado"})
	}
	sessionID := strings.TrimSpace(req.SessionID)
	if sessionID == "" {
		sessionID = "staff-preview:" + strconv.FormatUint(uint64(extraerUserID(c)), 10)
	}
	respuesta, err := services.ProcessWidgetMessage(agente, sessionID, strings.TrimSpace(req.Mensaje))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"session_id": sessionID, "respuesta": respuesta})
}

// ─── Auditoría ──────────────────────────────────────────────────────────────

// GetUmindEventosHandler expone el log de auditoría técnica de un agente —
// errores/eventos que antes solo se podían ver pidiendo los logs del
// servidor (fallos de AI, tools, correo, canales).
func GetUmindEventosHandler(c *fiber.Ctx) error {
	agenteID, err := strconv.ParseUint(c.Query("agente_id"), 10, 64)
	if err != nil || agenteID == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "agente_id requerido"})
	}
	items, err := models.GetUmindEventosByAgente(uint(agenteID), 100)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"items": items})
}
