package workload

import (
	"context"

	"github.com/robfig/cron"
	"go.uber.org/dig"
	"golang.org/x/sync/singleflight"

	"github.com/tidbcloud/pov-service/internal/client/email"
	"github.com/tidbcloud/pov-service/internal/config"
	"github.com/tidbcloud/pov-service/internal/gateway"
	"github.com/tidbcloud/pov-service/internal/pkg/log"
	"github.com/tidbcloud/pov-service/internal/repo"
	"github.com/tidbcloud/pov-service/internal/service"
	"github.com/tidbcloud/pov-service/internal/service/workload/hook"
	"github.com/tidbcloud/pov-service/internal/service/workload/task"
)

type RunnerParams struct {
	dig.In

	*config.Config
	log.Logger

	Gateway  gateway.Workload
	Svc      service.Workload
	DB       repo.WorkloadTest
	AuditDB  repo.AuditLog
	EmailCli email.Client
	*cron.Cron
}

type RunnerResult struct {
	dig.Out

	service.Runnable `group:"runnable"`
}

type runner struct {
	processorManager *task.ProcessorManager
	cron             *cron.Cron
}

func NewRunner(params RunnerParams) RunnerResult {
	processHooks := []task.ProcessorHook{
		// Send email after creating cluster for mannual workload test
		hook.NewSendEmailAtClusterComplete(
			params.EmailCli,
			params.EmailClusterCompleteTemplateID,
			params.EmailFromAddress,
		),
		// Send email after preparing data for mannual workload test
		hook.NewSendEmailAtPrepareComplete(
			params.EmailCli,
			params.EmailPrepareCompleteTemplateID,
			params.EmailFromAddress,
		),
		// Send finishing email
		hook.NewSendEmailAtComplete(
			params.EmailCli,
			params.EmailCompleteTemplateID,
			params.EmailFromAddress,
		),
		hook.NewPreserveData(params.Svc),
	}

	transHooks := []task.PhaseTransitionHook{
		// Send email at starting workload traffic for auto workload test
		hook.NewSendEmailAtTrafficAutoStart(
			params.EmailCli,
			params.EmailTrafficStartTemplateID,
			params.EmailFromAddress,
		),
		hook.NewReleaseCluster(params.Gateway),
	}

	r := runner{
		cron: params.Cron,
		processorManager: task.NewProcessorManager(
			task.ProcessorManagerParams{
				Db:           params.DB,
				Gateway:      params.Gateway,
				Svc:          params.Svc,
				EmailCli:     params.EmailCli,
				AuditRepo:    params.AuditDB,
				TransHooks:   transHooks,
				ProcessHooks: processHooks,
			},
		),
	}
	return RunnerResult{
		Runnable: r,
	}
}

var singleflightGroup singleflight.Group

func (r runner) Run(ctx context.Context) error {
	log.Log.Infof("Start workload runner")

	pollFunc := func() {
		_, err, _ := singleflightGroup.Do("workload-scheduler", func() (any, error) {
			err := r.sync(ctx)
			return nil, err
		})
		if err != nil {
			log.Log.Errorf(err, "sync failed")
		}
	}
	err := r.cron.AddFunc("@every 10s", pollFunc)
	return err
}

// sync now uses the declarative processor pattern for multi-concurrency safety
func (r runner) sync(ctx context.Context) error {
	// Use the processor manager to handle all workload tests declaratively
	return r.processorManager.ProcessAll(ctx)
}
