package scheduler

import (
	"time"

	cron "github.com/go-co-op/gocron/v2"
)

type Task func()

type Scheduler interface {
	Start() error
	Stop() error
	Jobs() []Job
	NewJob(td time.Duration, fn any, args ...any) (Job, error)
}

type Job interface {
	ID() string
	LastRun() (time.Time, error)
	Name() string
	NextRun() (time.Time, error)
	NextRuns(int) ([]time.Time, error)
}

type Options struct{}

type Option func(*Options) error

type scheduler struct {
	scheduler cron.Scheduler
}

func NewScheduler(opts ...Option) (Scheduler, error) {
	s, err := cron.NewScheduler()
	if err != nil {
		return nil, err
	}
	return &scheduler{scheduler: s}, nil
}

func (s *scheduler) Start() error {
	s.scheduler.Start()
	return nil
}

func (s *scheduler) Stop() error {
	if err := s.scheduler.Shutdown(); err != nil {
		return err
	}
	return nil
}

func (s *scheduler) NewJob(td time.Duration, fn any, args ...any) (Job, error) {
	j, err := s.scheduler.NewJob(cron.DurationJob(td), cron.NewTask(fn, args...))
	if err != nil {
		return nil, err
	}
	return &job{job: j}, nil
}

func (s *scheduler) Jobs() []Job {
	jobs := s.scheduler.Jobs()
	ret := make([]Job, len(jobs))
	for idx := range jobs {
		ret[idx] = &job{job: jobs[idx]}
	}
	return ret
}

type job struct {
	job cron.Job
}

func (j *job) ID() string {
	return j.job.ID().String()
}

func (j *job) LastRun() (time.Time, error) {
	return j.job.LastRun()
}

func (j *job) Name() string {
	return j.job.Name()
}

func (j *job) NextRun() (time.Time, error) {
	return j.job.NextRun()
}

func (j *job) NextRuns(n int) ([]time.Time, error) {
	return j.job.NextRuns(n)
}