package billing import ( "context" "fmt" "log/slog" "time" "git.coopcloud.tech/wiki-cafe/member-console/internal/workflows/queues" "go.temporal.io/sdk/client" ) const ( // SweepScheduleID is the unique identifier for the scheduled-change sweep schedule. SweepScheduleID = "billing-scheduled-change-sweep" // SweepWorkflowID is the workflow ID used for sweep executions. SweepWorkflowID = "billing-scheduled-change-sweep-workflow" // DefaultSweepInterval is the default interval between sweeps. DefaultSweepInterval = 1 * time.Hour ) // ScheduleManager manages Temporal schedules for billing maintenance. type ScheduleManager struct { client client.Client logger *slog.Logger } // NewScheduleManager creates a new ScheduleManager. func NewScheduleManager(c client.Client, logger *slog.Logger) *ScheduleManager { return &ScheduleManager{client: c, logger: logger} } // SweepScheduleConfig holds configuration for the sweep schedule. type SweepScheduleConfig struct { // Interval is how often to run the sweep. Interval time.Duration // TriggerImmediately runs the sweep immediately when creating the schedule. TriggerImmediately bool } // EnsureSweepSchedule creates or updates the scheduled-change sweep schedule. // If the schedule already exists, it updates the spec; otherwise it creates it. func (m *ScheduleManager) EnsureSweepSchedule(ctx context.Context, cfg SweepScheduleConfig) error { if cfg.Interval == 0 { cfg.Interval = DefaultSweepInterval } scheduleClient := m.client.ScheduleClient() handle := scheduleClient.GetHandle(ctx, SweepScheduleID) if _, err := handle.Describe(ctx); err == nil { m.logger.Info("updating existing billing sweep schedule", slog.Duration("interval", cfg.Interval)) if err := handle.Update(ctx, client.ScheduleUpdateOptions{ DoUpdate: func(schedule client.ScheduleUpdateInput) (*client.ScheduleUpdate, error) { schedule.Description.Schedule.Spec = &client.ScheduleSpec{ Intervals: []client.ScheduleIntervalSpec{{Every: cfg.Interval}}, } schedule.Description.Schedule.Action = &client.ScheduleWorkflowAction{ ID: SweepWorkflowID, Workflow: SweepScheduledChangesWorkflow, TaskQueue: queues.Main, } return &client.ScheduleUpdate{Schedule: &schedule.Description.Schedule}, nil }, }); err != nil { return fmt.Errorf("failed to update sweep schedule: %w", err) } return nil } m.logger.Info("creating billing sweep schedule", slog.Duration("interval", cfg.Interval), slog.Bool("triggerImmediately", cfg.TriggerImmediately)) if _, err := scheduleClient.Create(ctx, client.ScheduleOptions{ ID: SweepScheduleID, Spec: client.ScheduleSpec{Intervals: []client.ScheduleIntervalSpec{{Every: cfg.Interval}}}, Action: &client.ScheduleWorkflowAction{ ID: SweepWorkflowID, Workflow: SweepScheduledChangesWorkflow, TaskQueue: queues.Main, }, TriggerImmediately: cfg.TriggerImmediately, }); err != nil { return fmt.Errorf("failed to create sweep schedule: %w", err) } m.logger.Info("billing sweep schedule created", slog.String("scheduleID", SweepScheduleID)) return nil }