// Package cronutil provides a thin wrapper over github.com/robfig/cron/v3
// for parsing the five-field cron expressions used by Galaxy services to
// describe periodic schedules such as turn_schedule. It exposes only the
// parser and a Next computation; no scheduler runtime, no logging, and no
// concurrency primitives.
package cronutil

import (
	"fmt"
	"time"

	"github.com/robfig/cron/v3"
)

// fiveFieldParser parses standard five-field cron expressions
// (minute, hour, day-of-month, month, day-of-week). The grammar matches
// what Galaxy services accept for turn_schedule and is the only grammar
// supported by this package; six-field expressions with a seconds field
// are rejected.
var fiveFieldParser = cron.NewParser(
	cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow,
)

// Schedule holds a parsed five-field cron expression and computes the
// next firing time after a given moment. The zero value is not usable;
// callers obtain a Schedule from Parse.
type Schedule struct {
	inner cron.Schedule
}

// Parse parses expr as a five-field cron expression and returns the
// resulting Schedule. Parse returns an error if expr is empty, contains
// a seconds field, or is otherwise rejected by the underlying parser.
func Parse(expr string) (Schedule, error) {
	inner, err := fiveFieldParser.Parse(expr)
	if err != nil {
		return Schedule{}, fmt.Errorf("cronutil: parse %q: %w", expr, err)
	}
	return Schedule{inner: inner}, nil
}

// Next returns the next firing time strictly after after. The returned
// time is always in UTC; callers passing UTC values therefore get UTC
// values back. Calling Next on a zero-value Schedule panics.
func (s Schedule) Next(after time.Time) time.Time {
	return s.inner.Next(after.UTC()).UTC()
}
