package handlers import ( "net/http" "strconv" "time" "attune-heart-therapy/internal/middleware" "attune-heart-therapy/internal/services" "github.com/gin-gonic/gin" ) type BookingHandler struct { bookingService services.BookingService } func NewBookingHandler(bookingService services.BookingService) *BookingHandler { return &BookingHandler{ bookingService: bookingService, } } // GetAvailableSlots handles GET /api/schedules for available slots func (h *BookingHandler) GetAvailableSlots(c *gin.Context) { // Get date parameter from query string dateStr := c.Query("date") if dateStr == "" { c.JSON(http.StatusBadRequest, gin.H{ "error": "Date parameter is required (format: YYYY-MM-DD)", }) return } // Parse the date date, err := time.Parse("2006-01-02", dateStr) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid date format. Expected: YYYY-MM-DD", "details": err.Error(), }) return } // Get available slots slots, err := h.bookingService.GetAvailableSlots(date) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to get available slots", "details": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "date": dateStr, "slots": slots, }) } // CreateBooking handles POST /api/bookings for booking creation func (h *BookingHandler) CreateBooking(c *gin.Context) { // Get user ID from JWT token (set by auth middleware) userID, exists := middleware.GetUserIDFromContext(c) if !exists { c.JSON(http.StatusUnauthorized, gin.H{ "error": "User not authenticated", }) return } var req services.BookingRequest // Bind JSON request to struct if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid request format", "details": err.Error(), }) return } // Create the booking booking, err := h.bookingService.CreateBooking(userID, req) if err != nil { // Handle specific error cases if err.Error() == "schedule slot is not available" { c.JSON(http.StatusConflict, gin.H{ "error": "The selected time slot is no longer available", }) return } if err.Error() == "invalid schedule" { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid schedule ID provided", }) return } c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to create booking", "details": err.Error(), }) return } c.JSON(http.StatusCreated, gin.H{ "message": "Booking created successfully", "booking": booking, }) } // GetUserBookings handles GET /api/bookings for user's booking history func (h *BookingHandler) GetUserBookings(c *gin.Context) { // Get user ID from JWT token (set by auth middleware) userID, exists := middleware.GetUserIDFromContext(c) if !exists { c.JSON(http.StatusUnauthorized, gin.H{ "error": "User not authenticated", }) return } // Get user's bookings bookings, err := h.bookingService.GetUserBookings(userID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to get user bookings", "details": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "bookings": bookings, }) } // CancelBooking handles PUT /api/bookings/:id/cancel for booking cancellation func (h *BookingHandler) CancelBooking(c *gin.Context) { // Get user ID from JWT token (set by auth middleware) userID, exists := middleware.GetUserIDFromContext(c) if !exists { c.JSON(http.StatusUnauthorized, gin.H{ "error": "User not authenticated", }) return } // Get booking ID from URL parameter bookingIDStr := c.Param("id") bookingID, err := strconv.ParseUint(bookingIDStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid booking ID", }) return } // Cancel the booking if err := h.bookingService.CancelBooking(userID, uint(bookingID)); err != nil { // Handle specific error cases if err.Error() == "unauthorized: booking does not belong to user" { c.JSON(http.StatusForbidden, gin.H{ "error": "You can only cancel your own bookings", }) return } if err.Error() == "booking cannot be cancelled" { c.JSON(http.StatusBadRequest, gin.H{ "error": "This booking cannot be cancelled (must be at least 24 hours before scheduled time)", }) return } if err.Error() == "booking not found" { c.JSON(http.StatusNotFound, gin.H{ "error": "Booking not found", }) return } c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to cancel booking", "details": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "message": "Booking cancelled successfully", }) } // RescheduleBooking handles PUT /api/bookings/:id/reschedule for booking rescheduling func (h *BookingHandler) RescheduleBooking(c *gin.Context) { // Get user ID from JWT token (set by auth middleware) userID, exists := middleware.GetUserIDFromContext(c) if !exists { c.JSON(http.StatusUnauthorized, gin.H{ "error": "User not authenticated", }) return } // Get booking ID from URL parameter bookingIDStr := c.Param("id") bookingID, err := strconv.ParseUint(bookingIDStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid booking ID", }) return } var req struct { NewScheduleID uint `json:"new_schedule_id" binding:"required"` } // Bind JSON request to struct if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Invalid request format", "details": err.Error(), }) return } // Reschedule the booking if err := h.bookingService.RescheduleBooking(userID, uint(bookingID), req.NewScheduleID); err != nil { // Handle specific error cases if err.Error() == "unauthorized: booking does not belong to user" { c.JSON(http.StatusForbidden, gin.H{ "error": "You can only reschedule your own bookings", }) return } if err.Error() == "booking cannot be rescheduled" { c.JSON(http.StatusBadRequest, gin.H{ "error": "This booking cannot be rescheduled (must be at least 2 hours before scheduled time)", }) return } if err.Error() == "new schedule slot is not available" { c.JSON(http.StatusConflict, gin.H{ "error": "The new time slot is not available", }) return } if err.Error() == "booking not found" || err.Error() == "invalid new schedule" { c.JSON(http.StatusNotFound, gin.H{ "error": "Booking or new schedule not found", }) return } c.JSON(http.StatusInternalServerError, gin.H{ "error": "Failed to reschedule booking", "details": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "message": "Booking rescheduled successfully", }) }