package service import ( "context" "sort" "time" "github.com/calendarapi/internal/models" "github.com/calendarapi/internal/repository" "github.com/calendarapi/internal/utils" "github.com/google/uuid" ) type AvailabilityService struct { queries *repository.Queries calendar *CalendarService event *EventService } func NewAvailabilityService(queries *repository.Queries, calendar *CalendarService, event *EventService) *AvailabilityService { return &AvailabilityService{queries: queries, calendar: calendar, event: event} } func (s *AvailabilityService) GetBusyBlocks(ctx context.Context, userID uuid.UUID, calendarID uuid.UUID, rangeStart, rangeEnd time.Time) (*models.AvailabilityResponse, error) { if _, err := s.calendar.GetRole(ctx, calendarID, userID); err != nil { return nil, err } pgCalID := utils.ToPgUUID(calendarID) events, err := s.queries.ListEventsByCalendarInRange(ctx, repository.ListEventsByCalendarInRangeParams{ CalendarID: pgCalID, EndTime: utils.ToPgTimestamptz(rangeStart), StartTime: utils.ToPgTimestamptz(rangeEnd), }) if err != nil { return nil, models.ErrInternal } recurring, err := s.queries.ListRecurringEventsByCalendar(ctx, repository.ListRecurringEventsByCalendarParams{ CalendarID: pgCalID, StartTime: utils.ToPgTimestamptz(rangeEnd), }) if err != nil { return nil, models.ErrInternal } var busy []models.BusyBlock for _, ev := range events { if ev.RecurrenceRule.Valid { continue } busy = append(busy, models.BusyBlock{ Start: utils.FromPgTimestamptz(ev.StartTime), End: utils.FromPgTimestamptz(ev.EndTime), EventID: utils.FromPgUUID(ev.ID), }) } for _, ev := range recurring { occs := s.event.expandRecurrence(ev, rangeStart, rangeEnd) for _, occ := range occs { if occ.OccurrenceStartTime != nil { busy = append(busy, models.BusyBlock{ Start: *occ.OccurrenceStartTime, End: *occ.OccurrenceEndTime, EventID: occ.ID, }) } } } sort.Slice(busy, func(i, j int) bool { return busy[i].Start.Before(busy[j].Start) }) if busy == nil { busy = []models.BusyBlock{} } return &models.AvailabilityResponse{ CalendarID: calendarID, RangeStart: rangeStart, RangeEnd: rangeEnd, Busy: busy, }, nil }