package controllers import ( "net/http" "clapclap/internal/apperr" "clapclap/internal/dtos" "clapclap/internal/ginctx" "clapclap/internal/models" "clapclap/internal/utils" "clapclap/internal/uuid" "github.com/gin-gonic/gin" ) // --- HANDLERS --- // CreateComment godoc // @Summary Create a comment // @Description Creates a new comment on a track, or replies to an existing comment if reply_to_id is provided. // @Tags comments // @Accept json // @Produce json // @Param comment body dtos.CreateCommentRequest true "Comment data" // @Success 201 {object} dtos.IDMessageResponse "Returns the new comment ID and a success message" // @Failure 400 {object} swaggError400 // @Failure 401 {object} swaggError401 // @Failure 422 {object} swaggError422 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /comments [post] func CreateComment(c *gin.Context) { req, _ := ginctx.GetDto[dtos.CreateCommentRequest](c) repo, _ := ginctx.GetRepo(c, "repo") // TODO (pipo) : check errors ffs trackUUID, _ := uuid.Parse(req.TrackID) authorUUID, _ := uuid.Parse(req.AuthorID) var replyToUUID *uuid.UUID if req.ReplyToID != nil { parsed, err := uuid.Parse(*req.ReplyToID) if err == nil { replyToUUID = &parsed } } commentModel := models.Comment{ Content: req.Content, TrackID: trackUUID, AuthorID: authorUUID, ReplyToID: replyToUUID, } if err := repo.CreateComment(&commentModel); err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to create comment")) return } c.JSON(http.StatusCreated, dtos.IDMessageResponse{ ID: commentModel.ID.String(), Message: "Comment created", }) } // GetCommentByTrack godoc // @Summary Get comments for a track // @Description Retrieves all root-level comments (and their nested replies) for a specific track UUID // @Tags comments // @Produce json // @Param id path string true "Track UUID" // @Param preload query string false "Relation to preload" Enums(Author, Track, Replies) // @Success 200 {array} models.Comment // @Failure 500 {object} swaggError500 // @Router /comments/track/{id} [get] func GetCommentByTrack(c *gin.Context) { // TODO : what if id == "" ???? id := c.Param("id") preloads := utils.GetReqPreloads(c.Request, utils.GetCommentPreloads()) repo, _ := ginctx.GetRepo(c, "repo") comments, err := repo.GetCommentsByTrack(id, preloads) if err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to fetch comments")) return } c.JSON(http.StatusOK, comments) } // GetCommentByID godoc // @Summary Get a comment by ID // @Description Retrieves a specific comment and its nested replies using its UUID // @Tags comments // @Produce json // @Param id path string true "Comment UUID" // @Param preload query string false "Relation to preload" Enums(Author, Track, Replies) // @Success 200 {object} models.Comment // @Failure 404 {object} swaggError404 // @Router /comments/{id} [get] func GetCommentByID(c *gin.Context) { id := c.Param("id") preloads := utils.GetReqPreloads(c.Request, utils.GetCommentPreloads()) repo, _ := ginctx.GetRepo(c, "repo") comment, err := repo.GetCommentByID(id, preloads) if err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Comment not found")) return } c.JSON(http.StatusOK, comment) } // UpdateComment godoc // @Summary Update a comment // @Description Updates the text content of an existing comment // @Tags comments // @Accept json // @Produce json // @Param id path string true "Comment UUID" // @Param comment body dtos.UpdateCommentRequest true "Updated content" // @Success 200 {object} swaggMessageResponse // @Failure 400 {object} swaggError400 // @Failure 401 {object} swaggError401 // @Failure 403 {object} swaggError403 // @Failure 404 {object} swaggError404 // @Failure 422 {object} swaggError422 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /comments/{id} [put] func UpdateComment(c *gin.Context) { id := c.Param("id") uid, err := uuid.Parse(id) if err != nil { apperr.AbortWithError(c, apperr.NewBadRequestError(err, "Invalid comment ID format")) return } userID, _ := ginctx.GetUUID(c, "userID") repo, _ := ginctx.GetRepo(c, "repo") existingComment, err := repo.GetCommentByID(id, nil) if err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Comment not found")) return } if existingComment.AuthorID != userID { apperr.AbortWithError(c, apperr.NewForbiddenError(nil, "You can only edit your own comments")) return } req, _ := ginctx.GetDto[dtos.UpdateCommentRequest](c) commentUpdate := models.Comment{ Base: models.Base{ID: uid}, Content: req.Content, } if err := repo.UpdateComment(&commentUpdate); err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to update comment")) return } c.JSON(http.StatusOK, gin.H{"message": "Comment updated successfully"}) } // DeleteCommentByID godoc // @Summary Delete a comment // @Description Deletes a comment (and automatically its replies, depending on DB cascade settings) by its UUID // @Tags comments // @Produce json // @Param id path string true "Comment UUID" // @Success 200 {object} swaggMessageResponse // @Failure 401 {object} swaggError401 // @Failure 403 {object} swaggError403 // @Failure 404 {object} swaggError404 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /comments/{id} [delete] func DeleteCommentByID(c *gin.Context) { id := c.Param("id") userID, _ := ginctx.GetUUID(c, "userID") repo, _ := ginctx.GetRepo(c, "repo") existingComment, err := repo.GetCommentByID(id, nil) if err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Comment not found")) return } if existingComment.AuthorID != userID { apperr.AbortWithError(c, apperr.NewForbiddenError(nil, "You can only delete your own comments")) return } if err := repo.DeleteComment(id); err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to delete comment")) return } c.JSON(http.StatusOK, gin.H{"message": "Comment successfully deleted"}) }