package controllers import ( "net/http" "clapclap/internal/apperr" "clapclap/internal/dtos" "clapclap/internal/ginctx" "clapclap/internal/models" "clapclap/internal/uuid" "github.com/gin-gonic/gin" ) // IsFriend godoc // @Summary Check friendship status // @Description Returns whether the authenticated user is currently friends with the specified user ID // @Tags friends // @Produce json // @Param id path string true "Target User UUID" // @Success 200 {object} swaggIsFriendResponse // @Failure 400 {object} swaggError400 // @Failure 401 {object} swaggError401 // @Security BearerAuth // @Router /friends/{id} [get] func IsFriend(c *gin.Context) { userID, _ := ginctx.GetUUID(c, "userID") repo, _ := ginctx.GetRepo(c, "repo") id := c.Param("id") friendID, err := uuid.Parse(id) if err != nil { c.JSON(400, gin.H{"error": "invalid uuid"}) return } isFriend, err := repo.IsFriend(userID, friendID) if err != nil { c.JSON(200, gin.H{"is_friend": false}) return } c.JSON(200, gin.H{"is_friend": isFriend}) } // GetFriends godoc // @Summary Get all friends // @Description Retrieves the complete list of friends for the currently authenticated user // @Tags friends // @Produce json // @Success 200 {object} swaggFriendsResponse // @Failure 401 {object} swaggError401 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /friends [get] func GetFriends(c *gin.Context) { userID, _ := ginctx.GetUUID(c, "userID") repo, _ := ginctx.GetRepo(c, "repo") var friends []*models.User friends, err := repo.GetFriends(userID) if err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to retrieve friends")) return } c.JSON(http.StatusOK, gin.H{ "friends": friends, }) } // SendFriendRequest godoc // @Summary Send friend request // @Description Dispatches a new friend request to a target user // @Tags friends // @Accept json // @Produce json // @Param request body dtos.SendFriendRequest true "Target User Data" // @Success 201 {string} string "Created" // @Failure 400 {object} swaggError400 // @Failure 401 {object} swaggError401 // @Security BearerAuth // @Router /friends [post] func SendFriendRequest(c *gin.Context) { senderID, _ := ginctx.GetUUID(c, "userID") req, _ := ginctx.GetDto[dtos.SendFriendRequest](c) repo, _ := ginctx.GetRepo(c, "repo") err := repo.SendFriendRequest(senderID, req.To) if err != nil { apperr.AbortWithError(c, apperr.NewBadRequestError(err, err.Error())) return } c.Status(http.StatusCreated) } // GetPendingRequests godoc // @Summary Get pending friend requests // @Description Retrieves all pending friend requests where the authenticated user is the receiver // @Tags friends // @Produce json // @Success 200 {array} models.FriendRequest // @Failure 401 {object} swaggError401 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /friends/pending [get] func GetPendingRequests(c *gin.Context) { userID, _ := ginctx.GetUUID(c, "userID") repo, _ := ginctx.GetRepo(c, "repo") requests, err := repo.GetPendingRequests(userID) if err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to retrieve pending requests")) return } c.JSON(http.StatusOK, requests) } // GetPendingRequestsWith godoc // @Summary Get pending request with a specific user // @Description Retrieves a pending friend request between the authenticated user and the target user // @Tags friends // @Produce json // @Param id path string true "Target User UUID" // @Success 200 {object} models.FriendRequest // @Failure 400 {object} swaggError400 // @Failure 401 {object} swaggError401 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /friends/pending/{id} [get] func GetPendingRequestsWith(c *gin.Context) { userID, _ := ginctx.GetUUID(c, "userID") repo, _ := ginctx.GetRepo(c, "repo") id := c.Param("id") friendID, err := uuid.Parse(id) if err != nil { c.JSON(400, gin.H{"error": "invalid uuid"}) return } request, err := repo.GetPendingRequestWith(userID, friendID) if err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to retrieve pending request with given friend")) return } c.JSON(http.StatusOK, request) } // WithdrawFriendRequest godoc // @Summary Withdraw sent friend request // @Description Cancels a friend request previously sent by the authenticated user // @Tags friends // @Accept json // @Produce json // @Param request body dtos.WithdrawFriendRequest true "Receiver Data" // @Success 204 {string} string "No Content" // @Failure 401 {object} swaggError401 // @Failure 403 {object} swaggError403 // @Failure 404 {object} swaggError404 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /friends/withdraw [post] func WithdrawFriendRequest(c *gin.Context) { senderID, _ := ginctx.GetUUID(c, "userID") req, _ := ginctx.GetDto[dtos.WithdrawFriendRequest](c) repo, _ := ginctx.GetRepo(c, "repo") fr, err := repo.GetPendingRequestWith(senderID, req.ReceiverID) if err != nil { apperr.AbortWithError(c, apperr.NewNotFoundError(err, "Friendship request not found")) return } if fr.Status != "pending" { apperr.AbortWithError(c, apperr.NewForbiddenError(nil, "Friend request already resolved")) return } if fr.SenderID != senderID { apperr.AbortWithError(c, apperr.NewForbiddenError(nil, "Unauthorized to withdraw this request")) return } if err := repo.CancelFriendRequest(fr.ID); err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to cancel friend request")) return } c.Status(http.StatusNoContent) } // AcceptFriendRequest godoc // @Summary Accept a friend request // @Description Accepts a pending friend request received by the authenticated user // @Tags friends // @Accept json // @Produce json // @Param request body dtos.AcceptFriendRequest true "Friend Request ID Data" // @Success 204 {string} string "No Content" // @Failure 401 {object} swaggError401 // @Failure 403 {object} swaggError403 // @Failure 404 {object} swaggError404 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /friends/accept [post] func AcceptFriendRequest(c *gin.Context) { receiverID, _ := ginctx.GetUUID(c, "userID") req, _ := ginctx.GetDto[dtos.AcceptFriendRequest](c) repo, _ := ginctx.GetRepo(c, "repo") fr, err := repo.GetFriendRequestByID(req.RequestID) if err != nil { apperr.AbortWithError(c, apperr.NewNotFoundError(err, "Friendship request not found")) return } if fr.Status != "pending" { apperr.AbortWithError(c, apperr.NewForbiddenError(nil, "Friend request already resolved")) return } if fr.ReceiverID != receiverID { apperr.AbortWithError(c, apperr.NewForbiddenError(nil, "Unauthorized to accept this request")) return } if err := repo.AcceptFriendRequest(fr.ID); err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to accept friend request")) return } c.Status(http.StatusNoContent) } // RefuseFriendRequest godoc // @Summary Refuse a friend request // @Description Rejects a pending friend request received by the authenticated user // @Tags friends // @Accept json // @Produce json // @Param request body dtos.RefuseFriendRequest true "Friend Request ID Data" // @Success 200 {string} string "OK" // @Failure 401 {object} swaggError401 // @Failure 403 {object} swaggError403 // @Failure 404 {object} swaggError404 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /friends/refuse [post] func RefuseFriendRequest(c *gin.Context) { receiverID, _ := ginctx.GetUUID(c, "userID") req, _ := ginctx.GetDto[dtos.RefuseFriendRequest](c) repo, _ := ginctx.GetRepo(c, "repo") fr, err := repo.GetFriendRequestByID(req.RequestID) if err != nil { apperr.AbortWithError(c, apperr.NewNotFoundError(err, "Friendship request not found")) return } if fr.Status != "pending" { apperr.AbortWithError(c, apperr.NewForbiddenError(nil, "Friend request already resolved")) return } if fr.ReceiverID != receiverID { apperr.AbortWithError(c, apperr.NewForbiddenError(nil, "Unauthorized to refuse this request")) return } if err := repo.RejectFriendRequest(fr.ID); err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to refuse friend request")) return } c.Status(200) } // RemoveFriend godoc // @Summary Remove a friend // @Description Removes an existing friend from the authenticated user's friend list // @Tags friends // @Accept json // @Produce json // @Param request body dtos.RemoveFriendRequest true "Remove Friend Data" // @Success 204 {string} string "No Content" // @Failure 401 {object} swaggError401 // @Failure 500 {object} swaggError500 // @Security BearerAuth // @Router /friends [delete] func RemoveFriend(c *gin.Context) { userID, _ := ginctx.GetUUID(c, "userID") req, _ := ginctx.GetDto[dtos.RemoveFriendRequest](c) repo, _ := ginctx.GetRepo(c, "repo") if err := repo.RemoveFriend(userID, req.FriendID); err != nil { apperr.AbortWithError(c, apperr.NewDatabaseError(err, "Failed to remove friend")) return } c.Status(http.StatusNoContent) }