package handlers

import (
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"gitea.maison43.duckdns.org/gilles/matosbox/internal/data/ent"
	"gitea.maison43.duckdns.org/gilles/matosbox/internal/data/ent/categorie"
)

type categorieRequest struct {
	Nom     *string `json:"nom"`
	ParentID *string `json:"parent_id"`
	Slug    *string `json:"slug"`
	Icone   *string `json:"icone"`
}

// @Summary Lister les categories
// @Tags Categories
// @Produce json
// @Param page query int false "Page"
// @Param limit query int false "Limite"
// @Success 200 {object} map[string]any
// @Failure 500 {object} map[string]string
// @Router /categories [get]
func (h *Handler) ListCategories(c *gin.Context) {
	limit, offset, page := parsePagination(c.Query("page"), c.Query("limit"))
	query := h.client.Categorie.Query()
	if nom := c.Query("nom"); nom != "" {
		query = query.Where(categorie.NomContainsFold(nom))
	}
	total, err := query.Count(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"erreur": "impossible de compter les categories"})
		return
	}

	items, err := query.
		Order(ent.Desc(categorie.FieldCreatedAt)).
		Limit(limit).
		Offset(offset).
		All(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"erreur": "impossible de lister les categories"})
		return
	}
	respondPaginated(c, items, total, page, limit)
}

// @Summary Creer une categorie
// @Tags Categories
// @Accept json
// @Produce json
// @Param body body categorieRequest true "Categorie a creer"
// @Success 201 {object} ent.Categorie
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /categories [post]
func (h *Handler) CreateCategorie(c *gin.Context) {
	var req categorieRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"erreur": "donnees invalides"})
		return
	}
	if req.Nom == nil || *req.Nom == "" {
		c.JSON(http.StatusBadRequest, gin.H{"erreur": "le champ nom est obligatoire"})
		return
	}

	create := h.client.Categorie.Create().SetNom(*req.Nom)
	if req.ParentID != nil && *req.ParentID != "" {
		if parsed, err := uuid.Parse(*req.ParentID); err == nil {
			create.SetParentID(parsed)
		}
	}
	if req.Slug != nil {
		create.SetNillableSlug(req.Slug)
	}
	if req.Icone != nil {
		create.SetNillableIcone(req.Icone)
	}

	created, err := create.Save(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"erreur": "impossible de creer la categorie"})
		return
	}
	c.JSON(http.StatusCreated, created)
}

// @Summary Recuperer une categorie
// @Tags Categories
// @Produce json
// @Param id path string true "ID categorie"
// @Success 200 {object} ent.Categorie
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /categories/{id} [get]
func (h *Handler) GetCategorie(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"erreur": "identifiant invalide"})
		return
	}

	item, err := h.client.Categorie.Get(c.Request.Context(), id)
	if err != nil {
		if ent.IsNotFound(err) {
			c.JSON(http.StatusNotFound, gin.H{"erreur": "categorie introuvable"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"erreur": "impossible de charger la categorie"})
		return
	}
	c.JSON(http.StatusOK, item)
}

// @Summary Mettre a jour une categorie
// @Tags Categories
// @Accept json
// @Produce json
// @Param id path string true "ID categorie"
// @Param body body categorieRequest true "Categorie a mettre a jour"
// @Success 200 {object} ent.Categorie
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /categories/{id} [put]
func (h *Handler) UpdateCategorie(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"erreur": "identifiant invalide"})
		return
	}

	var req categorieRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"erreur": "donnees invalides"})
		return
	}

	update := h.client.Categorie.UpdateOneID(id)
	if req.Nom != nil {
		update.SetNom(*req.Nom)
	}
	if req.ParentID != nil {
		if *req.ParentID == "" {
			update.ClearParentID()
		} else if parsed, err := uuid.Parse(*req.ParentID); err == nil {
			update.SetParentID(parsed)
		}
	}
	if req.Slug != nil {
		update.SetNillableSlug(req.Slug)
	}
	if req.Icone != nil {
		update.SetNillableIcone(req.Icone)
	}

	updated, err := update.Save(c.Request.Context())
	if err != nil {
		if ent.IsNotFound(err) {
			c.JSON(http.StatusNotFound, gin.H{"erreur": "categorie introuvable"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"erreur": "impossible de mettre a jour la categorie"})
		return
	}
	c.JSON(http.StatusOK, updated)
}

// @Summary Supprimer une categorie
// @Tags Categories
// @Param id path string true "ID categorie"
// @Success 204 {string} string "No Content"
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /categories/{id} [delete]
func (h *Handler) DeleteCategorie(c *gin.Context) {
	id, err := uuid.Parse(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"erreur": "identifiant invalide"})
		return
	}

	if err := h.client.Categorie.DeleteOneID(id).Exec(c.Request.Context()); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"erreur": "impossible de supprimer la categorie"})
		return
	}
	c.Status(http.StatusNoContent)
}

// helper for compile reference
var _ = ent.IsNotFound
