package controllers

import (
	"math"
	"strconv"
	"strings"

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

// scopesValidos son los grupos de /api/v2 que hoy sí respetan RequireScope.
// El resto de endpoints de /api/v2 sigue funcionando solo con la llave
// maestra (ADMIN_API_KEY) mientras se migran en una fase posterior.
var scopesValidos = map[string]bool{
	"oss":          true,
	"query_runner": true,
	"usuarios":     true,
	"pasarelas":    true,
}

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

func GetApiKeys(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.GetAllApiKeys(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 apiKeyReq struct {
	Nombre      string   `json:"nombre"`
	IPPermitida string   `json:"ip_permitida"`
	Scopes      []string `json:"scopes"`
	Activa      bool     `json:"activa"`
}

func (r apiKeyReq) validar() (scopes []string, err error) {
	if strings.TrimSpace(r.Nombre) == "" {
		return nil, fiber.NewError(fiber.StatusBadRequest, "nombre es requerido")
	}
	if strings.TrimSpace(r.IPPermitida) == "" {
		return nil, fiber.NewError(fiber.StatusBadRequest, "ip_permitida es requerida (IP exacta o CIDR)")
	}
	for _, s := range r.Scopes {
		s = strings.ToLower(strings.TrimSpace(s))
		if s == "" {
			continue
		}
		if !scopesValidos[s] {
			return nil, fiber.NewError(fiber.StatusBadRequest, "scope inválido: "+s)
		}
		scopes = append(scopes, s)
	}
	if len(scopes) == 0 {
		return nil, fiber.NewError(fiber.StatusBadRequest, "selecciona al menos un scope")
	}
	return scopes, nil
}

// CreateApiKeyHandler crea una nueva API key y devuelve el token en texto
// plano — es la única respuesta donde vendrá completo.
func CreateApiKeyHandler(c *fiber.Ctx) error {
	var req apiKeyReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	scopes, err := req.validar()
	if err != nil {
		return c.Status(err.(*fiber.Error).Code).JSON(fiber.Map{"error": err.Error()})
	}

	item := &models.ApiKey{
		Nombre:      strings.TrimSpace(req.Nombre),
		IPPermitida: strings.TrimSpace(req.IPPermitida),
		Scopes:      models.JoinModulos(scopes),
		Activa:      true,
		CreadoPorID: extraerUserID(c),
	}
	tokenPlano, err := models.CreateApiKey(item)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.Status(fiber.StatusCreated).JSON(fiber.Map{
		"ok":    true,
		"id":    item.ID,
		"token": tokenPlano,
		"aviso": "Guarda este token ahora: no se volverá a mostrar completo.",
	})
}

func UpdateApiKeyHandler(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 apiKeyReq
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	scopes, verr := req.validar()
	if verr != nil {
		return c.Status(verr.(*fiber.Error).Code).JSON(fiber.Map{"error": verr.Error()})
	}

	updates := map[string]interface{}{
		"nombre":       strings.TrimSpace(req.Nombre),
		"ip_permitida": strings.TrimSpace(req.IPPermitida),
		"scopes":       models.JoinModulos(scopes),
		"activa":       req.Activa,
	}
	if err := models.UpdateApiKey(uint(id), updates); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

func RegenerarApiKeyHandler(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"})
	}
	tokenPlano, err := models.RegenerarApiKeyToken(uint(id))
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true, "token": tokenPlano, "aviso": "El token anterior dejó de funcionar. Guarda este ahora, no se volverá a mostrar."})
}

func DeleteApiKeyHandler(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.DeleteApiKey(uint(id)); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}
