package repositories import ( "errors" "fmt" "time" "attune-heart-therapy/internal/models" "gorm.io/gorm" ) // bookingRepository implements the BookingRepository interface type bookingRepository struct { db *gorm.DB } // NewBookingRepository creates a new instance of BookingRepository func NewBookingRepository(db *gorm.DB) BookingRepository { return &bookingRepository{ db: db, } } // Create creates a new booking in the database func (r *bookingRepository) Create(booking *models.Booking) error { if booking == nil { return errors.New("booking cannot be nil") } if err := r.db.Create(booking).Error; err != nil { return fmt.Errorf("failed to create booking: %w", err) } return nil } // GetByID retrieves a booking by its ID with user preloaded func (r *bookingRepository) GetByID(id uint) (*models.Booking, error) { if id == 0 { return nil, errors.New("invalid booking ID") } var booking models.Booking if err := r.db.Preload("User").First(&booking, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, fmt.Errorf("booking with ID %d not found", id) } return nil, fmt.Errorf("failed to get booking by ID: %w", err) } return &booking, nil } // GetByUserID retrieves all bookings for a specific user with user preloaded func (r *bookingRepository) GetByUserID(userID uint) ([]models.Booking, error) { if userID == 0 { return nil, errors.New("invalid user ID") } var bookings []models.Booking if err := r.db.Preload("User").Where("user_id = ?", userID). Order("scheduled_at DESC").Find(&bookings).Error; err != nil { return nil, fmt.Errorf("failed to get bookings for user %d: %w", userID, err) } return bookings, nil } // Update updates an existing booking in the database func (r *bookingRepository) Update(booking *models.Booking) error { if booking == nil { return errors.New("booking cannot be nil") } if booking.ID == 0 { return errors.New("booking ID is required for update") } // Check if booking exists var existingBooking models.Booking if err := r.db.First(&existingBooking, booking.ID).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("booking with ID %d not found", booking.ID) } return fmt.Errorf("failed to check booking existence: %w", err) } // Update the booking if err := r.db.Save(booking).Error; err != nil { return fmt.Errorf("failed to update booking: %w", err) } return nil } // Delete soft deletes a booking by its ID func (r *bookingRepository) Delete(id uint) error { if id == 0 { return errors.New("invalid booking ID") } // Check if booking exists var booking models.Booking if err := r.db.First(&booking, id).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("booking with ID %d not found", id) } return fmt.Errorf("failed to check booking existence: %w", err) } // Soft delete the booking if err := r.db.Delete(&booking).Error; err != nil { return fmt.Errorf("failed to delete booking: %w", err) } return nil } // GetUpcomingBookings retrieves all upcoming bookings for notification scheduling func (r *bookingRepository) GetUpcomingBookings() ([]models.Booking, error) { var bookings []models.Booking // Get bookings that are scheduled and in the future if err := r.db.Preload("User"). Where("status = ? AND scheduled_at > ?", models.BookingStatusScheduled, time.Now()). Order("scheduled_at ASC"). Find(&bookings).Error; err != nil { return nil, fmt.Errorf("failed to get upcoming bookings: %w", err) } return bookings, nil } // GetByPaymentID retrieves a booking by its payment ID with user preloaded func (r *bookingRepository) GetByPaymentID(paymentID string) (*models.Booking, error) { if paymentID == "" { return nil, errors.New("payment ID cannot be empty") } var booking models.Booking if err := r.db.Preload("User").Where("payment_id = ?", paymentID).First(&booking).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, fmt.Errorf("booking with payment ID %s not found", paymentID) } return nil, fmt.Errorf("failed to get booking by payment ID: %w", err) } return &booking, nil }