package handlers import ( "errors" "net/http" "github.com/gin-gonic/gin" "github.com/google/uuid" "go.uber.org/zap" apperrors "veza-backend-api/internal/errors" "veza-backend-api/internal/services" ) // QueueSessionHandler handles collaborative queue session HTTP requests (v0.203 Lot D1) type QueueSessionHandler struct { svc *services.QueueSessionService logger *zap.Logger } // NewQueueSessionHandler creates a new QueueSessionHandler func NewQueueSessionHandler(svc *services.QueueSessionService, logger *zap.Logger) *QueueSessionHandler { return &QueueSessionHandler{svc: svc, logger: logger} } // CreateSession creates a new shared queue session func (h *QueueSessionHandler) CreateSession(c *gin.Context) { userID, ok := GetUserIDUUID(c) if !ok { return } session, err := h.svc.CreateSession(c.Request.Context(), userID) if err != nil { RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to create session", err)) return } RespondSuccess(c, http.StatusCreated, gin.H{ "session": session, "share_token": session.ShareToken, "share_url": "/queue?session=" + session.ShareToken, }) } // GetSession returns a session's queue (auth optional for joining via link) func (h *QueueSessionHandler) GetSession(c *gin.Context) { token := c.Param("token") if token == "" { RespondWithAppError(c, apperrors.NewValidationError("token is required")) return } session, items, err := h.svc.GetSessionByToken(c.Request.Context(), token) if err != nil { if errors.Is(err, services.ErrQueueSessionNotFound) { RespondWithAppError(c, apperrors.NewNotFoundError("session not found")) return } RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get session", err)) return } // Map items to response with tracks type itemResp struct { ID string `json:"id"` Position int `json:"position"` AddedAt string `json:"added_at"` Track interface{} `json:"track,omitempty"` } respItems := make([]itemResp, 0, len(items)) for _, it := range items { r := itemResp{ ID: it.ID.String(), Position: it.Position, AddedAt: it.AddedAt.Format("2006-01-02T15:04:05Z07:00"), } if it.Track.ID != uuid.Nil { r.Track = it.Track } respItems = append(respItems, r) } RespondSuccess(c, http.StatusOK, gin.H{ "session": session, "items": respItems, }) } // DeleteSession deletes a session (creator only) func (h *QueueSessionHandler) DeleteSession(c *gin.Context) { userID, ok := GetUserIDUUID(c) if !ok { return } token := c.Param("token") if token == "" { RespondWithAppError(c, apperrors.NewValidationError("token is required")) return } if err := h.svc.DeleteSession(c.Request.Context(), token, userID); err != nil { if errors.Is(err, services.ErrQueueSessionNotFound) { RespondWithAppError(c, apperrors.NewNotFoundError("session not found")) return } if errors.Is(err, services.ErrQueueSessionForbidden) { RespondWithAppError(c, apperrors.NewForbiddenError("only the creator can delete this session")) return } RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to delete session", err)) return } RespondSuccess(c, http.StatusOK, gin.H{"message": "session deleted"}) } // AddToSessionRequest represents the request body type AddToSessionRequest struct { TrackID uuid.UUID `json:"track_id" binding:"required"` } // AddToSession adds a track to a session's queue func (h *QueueSessionHandler) AddToSession(c *gin.Context) { token := c.Param("token") if token == "" { RespondWithAppError(c, apperrors.NewValidationError("token is required")) return } var req AddToSessionRequest if err := c.ShouldBindJSON(&req); err != nil { RespondWithAppError(c, apperrors.NewValidationError("track_id is required")) return } if err := h.svc.AddToSession(c.Request.Context(), token, req.TrackID); err != nil { if errors.Is(err, services.ErrQueueSessionNotFound) { RespondWithAppError(c, apperrors.NewNotFoundError("session not found")) return } RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to add to session", err)) return } // Return updated session session, items, _ := h.svc.GetSessionByToken(c.Request.Context(), token) RespondSuccess(c, http.StatusCreated, gin.H{"session": session, "items": items}) } // RemoveFromSession removes an item from a session's queue func (h *QueueSessionHandler) RemoveFromSession(c *gin.Context) { token := c.Param("token") if token == "" { RespondWithAppError(c, apperrors.NewValidationError("token is required")) return } itemID, err := uuid.Parse(c.Param("id")) if err != nil { RespondWithAppError(c, apperrors.NewValidationError("invalid item ID")) return } if err := h.svc.RemoveFromSession(c.Request.Context(), token, itemID); err != nil { if errors.Is(err, services.ErrQueueSessionNotFound) { RespondWithAppError(c, apperrors.NewNotFoundError("session or item not found")) return } RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to remove from session", err)) return } RespondSuccess(c, http.StatusOK, gin.H{"message": "item removed"}) }