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/objet"
)

type objetRequest struct {
	Nom             *string           `json:"nom"`
	Description     *string           `json:"description"`
	Quantite        *int              `json:"quantite"`
	PrixAchat       *float64          `json:"prix_achat"`
	DateAchat       *string           `json:"date_achat"`
	Boutique        *string           `json:"boutique"`
	NumeroSerie     *string           `json:"numero_serie"`
	NumeroModele    *string           `json:"numero_modele"`
	Fabricant       *string           `json:"fabricant"`
	Statut          *string           `json:"statut"`
	Caracteristiques map[string]any   `json:"caracteristiques"`
}

// @Summary Lister les objets
// @Tags Objets
// @Produce json
// @Param page query int false "Page"
// @Param limit query int false "Limite"
// @Param statut query string false "Statut"
// @Param nom query string false "Recherche nom"
// @Success 200 {object} map[string]any
// @Failure 500 {object} map[string]string
// @Router /objets [get]
func (h *Handler) ListObjets(c *gin.Context) {
	ctx := c.Request.Context()
	limit, offset, page := parsePagination(c.Query("page"), c.Query("limit"))
	query := h.client.Objet.Query()

	if statutValue := c.Query("statut"); statutValue != "" {
		statut, ok := parseStatut(statutValue)
		if !ok {
			c.JSON(http.StatusBadRequest, gin.H{"erreur": "statut invalide"})
			return
		}
		query = query.Where(objet.StatutEQ(statut))
	}
	if nomValue := c.Query("nom"); nomValue != "" {
		query = query.Where(objet.NomContainsFold(nomValue))
	}
	total, err := query.Count(ctx)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"erreur": "impossible de compter les objets"})
		return
	}

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

// @Summary Creer un objet
// @Tags Objets
// @Accept json
// @Produce json
// @Param body body objetRequest true "Objet a creer"
// @Success 201 {object} ent.Objet
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /objets [post]
func (h *Handler) CreateObjet(c *gin.Context) {
	var req objetRequest
	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.Objet.Create().SetNom(*req.Nom)

	if req.Description != nil {
		create.SetNillableDescription(req.Description)
	}
	if req.Quantite != nil {
		create.SetQuantite(*req.Quantite)
	}
	if req.PrixAchat != nil {
		create.SetNillablePrixAchat(req.PrixAchat)
	}
	if req.DateAchat != nil {
		if parsed, err := parseDateTime(*req.DateAchat); err == nil {
			create.SetNillableDateAchat(&parsed)
		}
	}
	if req.Boutique != nil {
		create.SetNillableBoutique(req.Boutique)
	}
	if req.NumeroSerie != nil {
		create.SetNillableNumeroSerie(req.NumeroSerie)
	}
	if req.NumeroModele != nil {
		create.SetNillableNumeroModele(req.NumeroModele)
	}
	if req.Fabricant != nil {
		create.SetNillableFabricant(req.Fabricant)
	}
	if req.Statut != nil {
		parsed, ok := parseStatut(*req.Statut)
		if !ok {
			c.JSON(http.StatusBadRequest, gin.H{"erreur": "statut invalide"})
			return
		}
		create.SetStatut(parsed)
	}
	if req.Caracteristiques != nil {
		create.SetCaracteristiques(req.Caracteristiques)
	}

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

// @Summary Recuperer un objet
// @Tags Objets
// @Produce json
// @Param id path string true "ID objet"
// @Success 200 {object} ent.Objet
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /objets/{id} [get]
func (h *Handler) GetObjet(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.Objet.Get(c.Request.Context(), id)
	if err != nil {
		if ent.IsNotFound(err) {
			c.JSON(http.StatusNotFound, gin.H{"erreur": "objet introuvable"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"erreur": "impossible de charger l'objet"})
		return
	}
	c.JSON(http.StatusOK, item)
}

// @Summary Mettre a jour un objet
// @Tags Objets
// @Accept json
// @Produce json
// @Param id path string true "ID objet"
// @Param body body objetRequest true "Objet a mettre a jour"
// @Success 200 {object} ent.Objet
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /objets/{id} [put]
func (h *Handler) UpdateObjet(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 objetRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"erreur": "donnees invalides"})
		return
	}

	update := h.client.Objet.UpdateOneID(id)

	if req.Nom != nil {
		update.SetNom(*req.Nom)
	}
	if req.Description != nil {
		update.SetNillableDescription(req.Description)
	}
	if req.Quantite != nil {
		update.SetQuantite(*req.Quantite)
	}
	if req.PrixAchat != nil {
		update.SetNillablePrixAchat(req.PrixAchat)
	}
	if req.DateAchat != nil {
		if parsed, err := parseDateTime(*req.DateAchat); err == nil {
			update.SetNillableDateAchat(&parsed)
		}
	}
	if req.Boutique != nil {
		update.SetNillableBoutique(req.Boutique)
	}
	if req.NumeroSerie != nil {
		update.SetNillableNumeroSerie(req.NumeroSerie)
	}
	if req.NumeroModele != nil {
		update.SetNillableNumeroModele(req.NumeroModele)
	}
	if req.Fabricant != nil {
		update.SetNillableFabricant(req.Fabricant)
	}
	if req.Statut != nil {
		parsed, ok := parseStatut(*req.Statut)
		if !ok {
			c.JSON(http.StatusBadRequest, gin.H{"erreur": "statut invalide"})
			return
		}
		update.SetStatut(parsed)
	}
	if req.Caracteristiques != nil {
		update.SetCaracteristiques(req.Caracteristiques)
	}

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

// @Summary Supprimer un objet
// @Tags Objets
// @Param id path string true "ID objet"
// @Success 204 {string} string "No Content"
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /objets/{id} [delete]
func (h *Handler) DeleteObjet(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.Objet.DeleteOneID(id).Exec(c.Request.Context()); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"erreur": "impossible de supprimer l'objet"})
		return
	}
	c.Status(http.StatusNoContent)
}

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