package scheduler

import (
	"context"
	"time"

	"git.intra.yksa.space/gsn/predictor/internal/pkg/errcodes"
	"github.com/go-co-op/gocron"
	"go.uber.org/zap"
)

type Job interface {
	GetInterval() time.Duration
	GetTimeout() time.Duration
	GetCount() int
	GetAsync() bool
	Execute(context.Context) error
}

type Scheduler struct {
	scheduler *gocron.Scheduler
	logger    *zap.Logger
}

func New(logger *zap.Logger) *Scheduler {
	scheduler := gocron.NewScheduler(time.UTC)

	return &Scheduler{
		scheduler: scheduler,
		logger:    logger,
	}
}

func (s *Scheduler) AddJob(job Job) error {
	interval := job.GetInterval()
	timeout := job.GetTimeout()
	count := job.GetCount()
	async := job.GetAsync()

	// Validate job parameters
	if !async && count != 1 {
		return errcodes.ErrSchedulerInvalidJob
	}
	if timeout > interval {
		return errcodes.ErrSchedulerTimeoutTooLong
	}

	// Create job function with timeout
	jobFunc := func() {
		ctx, cancel := context.WithTimeout(context.Background(), timeout)
		defer cancel()

		if err := job.Execute(ctx); err != nil {
			s.logger.Error("job execution failed",
				zap.Error(err),
				zap.Duration("interval", interval),
				zap.Duration("timeout", timeout))
		} else {
			s.logger.Debug("job executed successfully",
				zap.Duration("interval", interval),
				zap.Duration("timeout", timeout))
		}
	}

	// Add job to scheduler
	schedulerJob := s.scheduler.Every(interval)

	if !async {
		schedulerJob = schedulerJob.SingletonMode()
	}

	if count > 0 {
		schedulerJob = schedulerJob.LimitRunsTo(count)
	}

	schedulerJob.Do(jobFunc)

	s.logger.Info("job added to scheduler",
		zap.Duration("interval", interval),
		zap.Duration("timeout", timeout),
		zap.Int("count", count),
		zap.Bool("async", async))

	return nil
}

func (s *Scheduler) Start() {
	s.scheduler.StartAsync()
	s.logger.Info("scheduler started")
}

func (s *Scheduler) Stop() {
	s.scheduler.Stop()
	s.logger.Info("scheduler stopped")
}

func (s *Scheduler) IsRunning() bool {
	return s.scheduler.IsRunning()
}
