package service import ( "context" "encoding/json" "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 TaskReminderScheduler interface { ScheduleTaskReminder(ctx context.Context, reminderID, taskID, ownerID uuid.UUID, triggerAt time.Time) error } type TaskReminderService struct { queries *repository.Queries scheduler TaskReminderScheduler } func NewTaskReminderService(queries *repository.Queries, scheduler TaskReminderScheduler) *TaskReminderService { return &TaskReminderService{queries: queries, scheduler: scheduler} } type CreateTaskReminderRequest struct { Type string Config map[string]interface{} ScheduledAt time.Time } func (s *TaskReminderService) Create(ctx context.Context, userID uuid.UUID, taskID uuid.UUID, req CreateTaskReminderRequest) (*models.TaskReminder, error) { if _, err := s.queries.GetTaskByID(ctx, repository.GetTaskByIDParams{ ID: utils.ToPgUUID(taskID), OwnerID: utils.ToPgUUID(userID), }); err != nil { if err == pgx.ErrNoRows { return nil, models.ErrNotFound } return nil, models.ErrInternal } validTypes := map[string]bool{"push": true, "email": true, "webhook": true, "telegram": true, "nostr": true} if !validTypes[req.Type] { return nil, models.NewValidationError("invalid reminder type") } configJSON := []byte("{}") if req.Config != nil { configJSON, _ = json.Marshal(req.Config) } id := uuid.New() rem, err := s.queries.CreateTaskReminder(ctx, repository.CreateTaskReminderParams{ ID: utils.ToPgUUID(id), TaskID: utils.ToPgUUID(taskID), Type: req.Type, Column4: configJSON, ScheduledAt: utils.ToPgTimestamptz(req.ScheduledAt), }) if err != nil { return nil, models.ErrInternal } if s.scheduler != nil { _ = s.scheduler.ScheduleTaskReminder(ctx, id, taskID, userID, req.ScheduledAt) } return taskReminderFromDB(rem), nil } func (s *TaskReminderService) List(ctx context.Context, userID uuid.UUID, taskID uuid.UUID) ([]models.TaskReminder, error) { if _, err := s.queries.GetTaskByID(ctx, repository.GetTaskByIDParams{ ID: utils.ToPgUUID(taskID), OwnerID: utils.ToPgUUID(userID), }); err != nil { if err == pgx.ErrNoRows { return nil, models.ErrNotFound } return nil, models.ErrInternal } rows, err := s.queries.ListTaskReminders(ctx, utils.ToPgUUID(taskID)) if err != nil { return nil, models.ErrInternal } reminders := make([]models.TaskReminder, 0, len(rows)) for _, r := range rows { reminders = append(reminders, *taskReminderFromDB(r)) } return reminders, nil } func (s *TaskReminderService) Delete(ctx context.Context, userID uuid.UUID, taskID uuid.UUID, reminderID uuid.UUID) error { if _, err := s.queries.GetTaskByID(ctx, repository.GetTaskByIDParams{ ID: utils.ToPgUUID(taskID), OwnerID: utils.ToPgUUID(userID), }); err != nil { if err == pgx.ErrNoRows { return models.ErrNotFound } return models.ErrInternal } return s.queries.DeleteTaskReminder(ctx, utils.ToPgUUID(reminderID)) } func taskReminderFromDB(r repository.TaskReminder) *models.TaskReminder { rem := &models.TaskReminder{ ID: utils.FromPgUUID(r.ID), TaskID: utils.FromPgUUID(r.TaskID), Type: r.Type, ScheduledAt: utils.FromPgTimestamptz(r.ScheduledAt), CreatedAt: utils.FromPgTimestamptz(r.CreatedAt), } if len(r.Config) > 0 { json.Unmarshal(r.Config, &rem.Config) } return rem }