package service import ( "context" "time" "github.com/calendarapi/internal/models" "github.com/calendarapi/internal/repository" "github.com/calendarapi/internal/utils" "github.com/google/uuid" "github.com/jackc/pgx/v5" ) type ReminderService struct { queries *repository.Queries calendar *CalendarService scheduler ReminderScheduler } func NewReminderService(queries *repository.Queries, calendar *CalendarService, scheduler ReminderScheduler) *ReminderService { return &ReminderService{queries: queries, calendar: calendar, scheduler: scheduler} } func (s *ReminderService) AddReminders(ctx context.Context, userID uuid.UUID, eventID uuid.UUID, minutesBefore []int32) (*models.Event, error) { ev, err := s.queries.GetEventByID(ctx, utils.ToPgUUID(eventID)) if err != nil { if err == pgx.ErrNoRows { return nil, models.ErrNotFound } return nil, models.ErrInternal } calID := utils.FromPgUUID(ev.CalendarID) role, err := s.calendar.GetRole(ctx, calID, userID) if err != nil { return nil, err } if role != "owner" && role != "editor" { return nil, models.ErrForbidden } for _, mins := range minutesBefore { if err := utils.ValidateReminderMinutes(mins); err != nil { return nil, err } rID := uuid.New() _, err := s.queries.CreateReminder(ctx, repository.CreateReminderParams{ ID: utils.ToPgUUID(rID), EventID: utils.ToPgUUID(eventID), MinutesBefore: mins, }) if err != nil { return nil, models.ErrInternal } startTime := utils.FromPgTimestamptz(ev.StartTime) triggerAt := startTime.Add(-time.Duration(mins) * time.Minute) if triggerAt.After(time.Now()) && s.scheduler != nil { _ = s.scheduler.ScheduleReminder(ctx, eventID, rID, userID, triggerAt) } } reminders, _ := loadRemindersHelper(ctx, s.queries, eventID) attendees, _ := loadAttendeesHelper(ctx, s.queries, eventID) attachments, _ := loadAttachmentsHelper(ctx, s.queries, eventID) return eventFromDB(ev, reminders, attendees, attachments), nil } func (s *ReminderService) DeleteReminder(ctx context.Context, userID uuid.UUID, eventID uuid.UUID, reminderID uuid.UUID) error { ev, err := s.queries.GetEventByID(ctx, utils.ToPgUUID(eventID)) if err != nil { if err == pgx.ErrNoRows { return models.ErrNotFound } return models.ErrInternal } calID := utils.FromPgUUID(ev.CalendarID) role, err := s.calendar.GetRole(ctx, calID, userID) if err != nil { return err } if role != "owner" && role != "editor" { return models.ErrForbidden } return s.queries.DeleteReminder(ctx, repository.DeleteReminderParams{ ID: utils.ToPgUUID(reminderID), EventID: utils.ToPgUUID(eventID), }) } func loadRemindersHelper(ctx context.Context, q *repository.Queries, eventID uuid.UUID) ([]models.Reminder, error) { rows, err := q.ListRemindersByEvent(ctx, utils.ToPgUUID(eventID)) if err != nil { return []models.Reminder{}, err } result := make([]models.Reminder, len(rows)) for i, r := range rows { result[i] = models.Reminder{ID: utils.FromPgUUID(r.ID), MinutesBefore: r.MinutesBefore} } return result, nil } func loadAttendeesHelper(ctx context.Context, q *repository.Queries, eventID uuid.UUID) ([]models.Attendee, error) { rows, err := q.ListAttendeesByEvent(ctx, utils.ToPgUUID(eventID)) if err != nil { return []models.Attendee{}, err } result := make([]models.Attendee, len(rows)) for i, a := range rows { var uid *uuid.UUID if a.UserID.Valid { u := utils.FromPgUUID(a.UserID) uid = &u } result[i] = models.Attendee{ID: utils.FromPgUUID(a.ID), UserID: uid, Email: utils.FromPgText(a.Email), Status: a.Status} } return result, nil } func loadAttachmentsHelper(ctx context.Context, q *repository.Queries, eventID uuid.UUID) ([]models.Attachment, error) { rows, err := q.ListAttachmentsByEvent(ctx, utils.ToPgUUID(eventID)) if err != nil { return []models.Attachment{}, err } result := make([]models.Attachment, len(rows)) for i, a := range rows { result[i] = models.Attachment{ID: utils.FromPgUUID(a.ID), FileURL: a.FileUrl} } return result, nil }