package diagcontroller

import (
	"context"
	"crypto/rand"
	"math/big"
	"time"

	"github.com/pingcap/log"
	"go.uber.org/zap"

	"github.com/tidbcloud/observability/pkg/diagcontroller/tasks"
)

type TaskController struct {
	task tasks.Tasker
}

func NewTaskController(task tasks.Tasker) *TaskController {
	return &TaskController{
		task: task,
	}
}

func (c *TaskController) Start(ctx context.Context) {
	c.dispatchTasks(ctx)
}

func (c *TaskController) dispatchTasks(ctx context.Context) {
	dispatchTask := func() {
		if err := c.task.DispatchTask(); err != nil {
			log.Error("dispatch task failed", zap.Error(err))
		}
	}
	// dispatch task after a random time, to avoid all diagcontroller dispatch task at the same time
	log.Info("wait for a random time to dispatch task for the first time")
	afterRandomTime(ctx, 2*time.Minute)
	dispatchTask()

	ticker := time.NewTicker(c.task.Interval())
	for {
		select {
		case <-ctx.Done():
			ticker.Stop()
			return
		case <-ticker.C:
			dispatchTask()
		}
	}
}

func afterRandomTime(ctx context.Context, maxTime time.Duration) {
	randDuration := gentRandomDuration(0, maxTime)
	log.Info("sleep", zap.Duration("Duration", randDuration))
	select {
	case <-time.After(randDuration):
		return
	case <-ctx.Done():
		return
	}
}

func gentRandomDuration(minTime, maxTime time.Duration) time.Duration {
	randomInt, _ := rand.Int(rand.Reader, big.NewInt(int64(maxTime-minTime)))
	randomDuration := minTime + time.Duration(randomInt.Int64())
	return randomDuration
}
