package controllers

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"

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

// ─── Tipos del update de Telegram ────────────────────────────────────────────

type tgUser struct {
	ID        int64  `json:"id"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	Username  string `json:"username"`
}

type tgChat struct {
	ID   int64  `json:"id"`
	Type string `json:"type"`
}

type tgDocument struct {
	FileID   string `json:"file_id"`
	FileName string `json:"file_name"`
	MimeType string `json:"mime_type"`
}

type tgPhotoSize struct {
	FileID string `json:"file_id"`
	Width  int    `json:"width"`
	Height int    `json:"height"`
}

type tgVoice struct {
	FileID   string `json:"file_id"`
	Duration int    `json:"duration"`
	MimeType string `json:"mime_type"`
}

type tgMessage struct {
	MessageID int           `json:"message_id"`
	From      tgUser        `json:"from"`
	Chat      tgChat        `json:"chat"`
	Text      string        `json:"text"`
	Caption   string        `json:"caption"`
	Document  *tgDocument   `json:"document"`
	Photo     []tgPhotoSize `json:"photo"`
	Voice     *tgVoice      `json:"voice"`
	Audio     *tgVoice      `json:"audio"`
}

type tgUpdate struct {
	UpdateID int        `json:"update_id"`
	Message  *tgMessage `json:"message"`
}

// ─── Webhook handler ──────────────────────────────────────────────────────────

// TelegramAgentWebhook recibe updates del bot administrador.
// Telegram llama aquí cuando alguien escribe al bot.
// Ruta: POST /webhooks/telegram-agent/:bot_token
func TelegramAgentWebhook(c *fiber.Ctx) error {
	botToken := c.Params("bot_token")
	if botToken == "" {
		return c.SendStatus(400)
	}

	// Parsear el update
	var update tgUpdate
	if err := c.BodyParser(&update); err != nil {
		return c.SendStatus(200) // siempre 200 a Telegram
	}
	if update.Message == nil {
		return c.SendStatus(200)
	}

	msg := update.Message
	chatID := msg.Chat.ID
	text := strings.TrimSpace(msg.Text)
	caption := strings.TrimSpace(msg.Caption)
	hasAttachment := msg.Document != nil || len(msg.Photo) > 0
	hasAudio := msg.Voice != nil || msg.Audio != nil
	if text == "" && caption == "" && !hasAttachment && !hasAudio {
		return c.SendStatus(200)
	}

	// Buscar el config del agente que tenga este bot token
	ai, tgCfg, err := models.GetAgenteBotConfig()
	if err != nil || tgCfg == nil || tgCfg.BotToken != botToken {
		log.Printf("[AGENT_WEBHOOK] Token no corresponde a ningún agente activo")
		return c.SendStatus(200)
	}

	// Verificar autorización del chat (si hay whitelist configurada)
	if !models.IsAgentAuthChat(chatID) {
		// Si no hay ningún auth configurado, solo responder al chat_id del config
		tgChatIDStr := strings.TrimSpace(tgCfg.ChatID)
		tgChatID, _ := strconv.ParseInt(tgChatIDStr, 10, 64)
		if tgChatID != 0 && chatID != tgChatID {
			sendAgentReply(tgCfg.BotToken, chatID, "No tienes autorización para usar este agente.")
			return c.SendStatus(200)
		}
	}

	// Comandos de control (sincrónicos, respuesta inmediata)
	if text == "/reset" || text == "/limpiar" || text == "/start" {
		_ = models.ClearAgentHistory(chatID)
		sendAgentReply(tgCfg.BotToken, chatID, "Historial borrado. Empezamos de cero.")
		return c.SendStatus(200)
	}

	// Documento o foto adjunto: se descarga y queda "pendiente" para que el
	// agente lo asocie a una factura (tool adjuntar_factura) según lo que diga
	// el caption o lo que responda el usuario a continuación.
	if hasAttachment {
		fileID, fileName, mimeType := "", "documento", ""
		switch {
		case msg.Document != nil:
			fileID = msg.Document.FileID
			if msg.Document.FileName != "" {
				fileName = msg.Document.FileName
			}
			mimeType = msg.Document.MimeType
		case len(msg.Photo) > 0:
			// Telegram manda varias resoluciones de la misma foto; la última es la de mayor calidad.
			best := msg.Photo[len(msg.Photo)-1]
			fileID = best.FileID
			fileName = "foto.jpg"
			mimeType = "image/jpeg"
		}
		if fileID != "" {
			path, dlErr := services.DescargarDocumentoTelegram(tgCfg.BotToken, fileID, fileName, mimeType)
			if dlErr != nil {
				log.Printf("[AGENT_WEBHOOK] Error descargando adjunto: %v", dlErr)
				sendAgentReply(tgCfg.BotToken, chatID, "No pude descargar el archivo que enviaste, intenta de nuevo.")
				return c.SendStatus(200)
			}
			services.StageTelegramAttachment(chatID, path, fileName, mimeType)
			nota := fmt.Sprintf("[Documento adjunto recibido: %s]", fileName)
			switch {
			case caption != "":
				text = nota + " " + caption
			case text == "":
				text = nota + " ¿A qué cliente corresponde y cuál es el monto de la factura?"
			default:
				text = nota + " " + text
			}
		}
	}

	// Nota de voz o audio: se descarga, se transcribe con Whisper (config del
	// módulo "whisper" en /app/ai-config) y el texto resultante se procesa como
	// si el usuario lo hubiera escrito.
	if hasAudio {
		voice := msg.Voice
		if voice == nil {
			voice = msg.Audio
		}
		fileName := "audio.ogg"
		mimeType := voice.MimeType
		path, dlErr := services.DescargarDocumentoTelegram(tgCfg.BotToken, voice.FileID, fileName, mimeType)
		if dlErr != nil {
			log.Printf("[AGENT_WEBHOOK] Error descargando audio: %v", dlErr)
			sendAgentReply(tgCfg.BotToken, chatID, "No pude descargar el audio que enviaste, intenta de nuevo.")
			return c.SendStatus(200)
		}
		defer os.Remove(path)

		whisperCfg, wErr := models.GetWhisperConfig()
		if wErr != nil {
			log.Printf("[AGENT_WEBHOOK] Whisper no configurado: %v", wErr)
			sendAgentReply(tgCfg.BotToken, chatID, "No pude transcribir el audio: no hay ninguna configuración de Whisper activa. Ve a /app/ai-config, crea o edita una config con módulo 'Transcripción de audio (Whisper)' y credenciales válidas de OpenAI o Groq.")
			return c.SendStatus(200)
		}

		transcripcion, tErr := services.TranscribirAudio(whisperCfg, path)
		if tErr != nil {
			log.Printf("[AGENT_WEBHOOK] Error transcribiendo audio: %v", tErr)
			sendAgentReply(tgCfg.BotToken, chatID, fmt.Sprintf("No pude transcribir el audio: %s", tErr.Error()))
			return c.SendStatus(200)
		}
		log.Printf("[AGENT_WEBHOOK] Audio transcrito (chat %d): %s", chatID, transcripcion)

		if caption != "" {
			text = transcripcion + " " + caption
		} else {
			text = transcripcion
		}
	}

	// Procesar en goroutine para responder 200 inmediatamente a Telegram
	go func() {
		response, err := services.ProcessAgentMessage(chatID, text, ai)
		if err != nil {
			log.Printf("[AGENT] Error procesando mensaje: %v", err)
			response = fmt.Sprintf("Error interno: %s", err.Error())
		}
		if response == "" {
			return
		}
		if sendErr := sendAgentReply(tgCfg.BotToken, chatID, response); sendErr != nil {
			log.Printf("[AGENT] Error enviando respuesta: %v", sendErr)
		}
	}()

	return c.SendStatus(200)
}

func sendAgentReply(botToken string, chatID int64, text string) error {
	svc := &services.TelegramService{BotToken: botToken}
	return svc.SendMessage(chatID, services.FormatearParaTelegram(text))
}

// ─── CRUD de chats autorizados ────────────────────────────────────────────────

// AgentAuthView renderiza la pantalla de administración de chats autorizados.
func AgentAuthView(c *fiber.Ctx) error {
	return c.Render("automatizacion/chats_autorizados", fiber.Map{
		"user":    c.Locals("user"),
		"modules": c.Locals("userModules"),
	}, "layouts/main")
}

// AgentAuthRecientes consulta los últimos mensajes recibidos por el bot del
// agente (getUpdates) y devuelve los remitentes distintos que todavía no están
// autorizados, para que el admin pueda autorizarlos con un clic sin tener que
// buscar el chat_id a mano.
func AgentAuthRecientes(c *fiber.Ctx) error {
	tgCfg, err := models.GetAgentTelegramConfig()
	if err != nil || tgCfg.BotToken == "" {
		return c.Status(400).JSON(fiber.Map{"error": "No hay un bot de Telegram configurado para el agente"})
	}
	autorizados, _ := models.GetAllAgentAuth()
	yaAutorizado := map[int64]bool{}
	for _, a := range autorizados {
		yaAutorizado[a.ChatID] = true
	}

	remitentes, err := services.UpdatesRecientesDelBot(tgCfg.BotToken)
	if err != nil {
		return c.Status(502).JSON(fiber.Map{"error": err.Error()})
	}

	type item struct {
		ChatID  int64  `json:"chat_id"`
		Nombre  string `json:"nombre"`
		Mensaje string `json:"mensaje"`
	}
	seen := map[int64]bool{}
	out := make([]item, 0, len(remitentes))
	for _, r := range remitentes {
		if seen[r.ChatID] || yaAutorizado[r.ChatID] {
			continue
		}
		seen[r.ChatID] = true
		out = append(out, item{ChatID: r.ChatID, Nombre: r.Nombre, Mensaje: r.Mensaje})
	}
	return c.JSON(fiber.Map{"items": out})
}

func AgentAuthList(c *fiber.Ctx) error {
	items, err := models.GetAllAgentAuth()
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"items": items})
}

func AgentAuthCreate(c *fiber.Ctx) error {
	type Req struct {
		ChatID int64  `json:"chat_id"`
		Nombre string `json:"nombre"`
	}
	var req Req
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "body inválido"})
	}
	if req.ChatID == 0 {
		return c.Status(400).JSON(fiber.Map{"error": "chat_id requerido"})
	}
	if err := models.CreateAgentAuth(req.ChatID, req.Nombre); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(201).JSON(fiber.Map{"ok": true})
}

func AgentAuthDelete(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 32)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "id inválido"})
	}
	if err := models.DeleteAgentAuth(uint(id)); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// AgentHistoryClear borra el historial de conversación de un chat.
func AgentHistoryClear(c *fiber.Ctx) error {
	chatID, err := strconv.ParseInt(c.Params("chat_id"), 10, 64)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "chat_id inválido"})
	}
	if err := models.ClearAgentHistory(chatID); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}
