package controllers

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"math"
	"net/http"
	"strconv"
	"strings"

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

// TelegramIndex renderiza la vista de configuración de Telegram.
func TelegramIndex(c *fiber.Ctx) error {
	return c.Render("telegram", fiber.Map{
		"user":    c.Locals("user"),
		"modules": c.Locals("userModules"),
	}, "layouts/main")
}

// GetTelegramConfigs devuelve todas las configs en JSON.
func GetTelegramConfigs(c *fiber.Ctx) error {
	items, err := models.GetAllTelegramConfigs()
	if err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(items)
}

// CreateTelegramConfig crea una nueva configuración.
func CreateTelegramConfig(c *fiber.Ctx) error {
	var m models.TelegramConfig
	if err := c.BodyParser(&m); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	if strings.TrimSpace(m.Nombre) == "" || strings.TrimSpace(m.BotToken) == "" || strings.TrimSpace(m.ChatID) == "" {
		return c.Status(400).JSON(fiber.Map{"error": "nombre, bot_token y chat_id son obligatorios"})
	}
	m.Activo = true
	if err := models.CreateTelegramConfig(&m); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(201).JSON(m)
}

// UpdateTelegramConfig actualiza una configuración.
func UpdateTelegramConfig(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"})
	}
	var m models.TelegramConfig
	if err := c.BodyParser(&m); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	m.ID = uint(id)
	// Si no se envía bot_token, mantener el existente
	if strings.TrimSpace(m.BotToken) == "" {
		cfg, err := models.GetTelegramConfigByID(uint(id))
		if err != nil {
			return c.Status(404).JSON(fiber.Map{"error": "no encontrado"})
		}
		m.BotToken = cfg.BotToken
	}
	if err := models.UpdateTelegramConfig(&m); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// DeleteTelegramConfig elimina una configuración.
func DeleteTelegramConfig(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.DeleteTelegramConfig(uint(id)); err != nil {
		return c.Status(500).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// TestTelegramConfig envía un mensaje de prueba al bot configurado.
func TestTelegramConfig(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"})
	}
	cfg, err := models.GetTelegramConfigByID(uint(id))
	if err != nil {
		return c.Status(404).JSON(fiber.Map{"error": "Configuración no encontrada"})
	}
	msg := "✅ <b>Prueba de conexión</b>\nEsta es una notificación de prueba desde <b>u-site admin</b>."
	sendErr := sendTelegramMessage(cfg.BotToken, cfg.ChatID, msg)
	logEntry := &models.TelegramLog{
		TelegramConfigID: cfg.ID,
		Titulo:           "Prueba manual",
		Mensaje:          msg,
		Estado:           "ok",
	}
	if sendErr != nil {
		logEntry.Estado = "failed"
		logEntry.ErrorMsg = sendErr.Error()
		_ = models.CreateTelegramLog(logEntry)
		return c.Status(422).JSON(fiber.Map{"ok": false, "error": sendErr.Error()})
	}
	_ = models.CreateTelegramLog(logEntry)
	return c.JSON(fiber.Map{"ok": true, "message": "Mensaje enviado"})
}

// SendTelegramNotification envía un mensaje personalizado a una o varias configs.
// Body: { "config_ids": [1,2], "titulo": "...", "mensaje": "..." }
func SendTelegramNotification(c *fiber.Ctx) error {
	type Req struct {
		ConfigIDs []uint `json:"config_ids"`
		Titulo    string `json:"titulo"`
		Mensaje   string `json:"mensaje"`
	}
	var req Req
	if err := c.BodyParser(&req); err != nil {
		return c.Status(400).JSON(fiber.Map{"error": err.Error()})
	}
	if strings.TrimSpace(req.Mensaje) == "" {
		return c.Status(400).JSON(fiber.Map{"error": "El mensaje no puede estar vacío"})
	}
	if len(req.ConfigIDs) == 0 {
		return c.Status(400).JSON(fiber.Map{"error": "Selecciona al menos un destino"})
	}

	text := req.Mensaje
	if req.Titulo != "" {
		text = fmt.Sprintf("<b>%s</b>\n\n%s", req.Titulo, req.Mensaje)
	}

	var enviados, fallidos int
	for _, cid := range req.ConfigIDs {
		cfg, err := models.GetTelegramConfigByID(cid)
		if err != nil || !cfg.Activo {
			fallidos++
			continue
		}
		sendErr := sendTelegramMessage(cfg.BotToken, cfg.ChatID, text)
		logEntry := &models.TelegramLog{
			TelegramConfigID: cfg.ID,
			Titulo:           req.Titulo,
			Mensaje:          req.Mensaje,
			Estado:           "ok",
		}
		if sendErr != nil {
			logEntry.Estado = "failed"
			logEntry.ErrorMsg = sendErr.Error()
			fallidos++
		} else {
			enviados++
		}
		_ = models.CreateTelegramLog(logEntry)
	}
	return c.JSON(fiber.Map{"ok": fallidos == 0, "enviados": enviados, "fallidos": fallidos})
}

// GetTelegramLogs devuelve el historial paginado de mensajes enviados.
func GetTelegramLogs(c *fiber.Ctx) error {
	page, _ := strconv.Atoi(c.Query("page", "1"))
	if page < 1 {
		page = 1
	}
	limit := 30
	offset := (page - 1) * limit
	items, total, err := models.GetTelegramLogs(limit, offset)
	if err != nil {
		return c.Status(500).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,
	})
}

// ─── helper interno ───────────────────────────────────────────────────────────

func sendTelegramMessage(botToken, chatID, text string) error {
	if botToken == "" {
		return fmt.Errorf("bot_token vacío")
	}
	apiURL := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", botToken)
	payload := map[string]interface{}{
		"chat_id":    chatID,
		"text":       text,
		"parse_mode": "HTML",
	}
	body, _ := json.Marshal(payload)
	resp, err := http.Post(apiURL, "application/json", bytes.NewReader(body)) //nolint:noctx
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("Telegram respondió %d", resp.StatusCode)
	}
	return nil
}

// ─── Webhook portal (set + info) ─────────────────────────────────────────────

// SetPortalWebhook registra el webhook del portal en la API de Telegram para el bot indicado.
// POST /app/telegram/:id/set-portal-webhook
func SetPortalWebhook(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"})
	}
	cfg, err := models.GetTelegramConfigByID(uint(id))
	if err != nil {
		return c.Status(404).JSON(fiber.Map{"error": "Configuración no encontrada"})
	}

	webhookURL := services.GetPublicURL() + "/webhooks/telegram-portal"

	apiURL := fmt.Sprintf("https://api.telegram.org/bot%s/setWebhook", cfg.BotToken)
	payload, _ := json.Marshal(map[string]string{"url": webhookURL})
	resp, err := http.Post(apiURL, "application/json", bytes.NewReader(payload)) //nolint:noctx
	if err != nil {
		return c.Status(502).JSON(fiber.Map{"ok": false, "error": err.Error()})
	}
	defer resp.Body.Close()
	raw, _ := io.ReadAll(resp.Body)

	var tgResp map[string]interface{}
	_ = json.Unmarshal(raw, &tgResp)
	log.Printf("[SetPortalWebhook] bot=%d url=%s resp=%s", id, webhookURL, string(raw))
	return c.JSON(fiber.Map{"ok": tgResp["ok"], "webhook_url": webhookURL, "telegram": tgResp})
}

// GetPortalWebhookInfo consulta el estado actual del webhook en Telegram.
// GET /app/telegram/:id/webhook-info
func GetPortalWebhookInfo(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"})
	}
	cfg, err := models.GetTelegramConfigByID(uint(id))
	if err != nil {
		return c.Status(404).JSON(fiber.Map{"error": "Configuración no encontrada"})
	}
	apiURL := fmt.Sprintf("https://api.telegram.org/bot%s/getWebhookInfo", cfg.BotToken)
	resp, err := http.Get(apiURL) //nolint:noctx
	if err != nil {
		return c.Status(502).JSON(fiber.Map{"ok": false, "error": err.Error()})
	}
	defer resp.Body.Close()
	var result map[string]interface{}
	_ = json.NewDecoder(resp.Body).Decode(&result)
	return c.JSON(result)
}
