package action

import (
	"fmt"

	"github.com/tidbcloud/cluster-service-ng/config"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow"
	"github.com/tidbcloud/cluster-service-ng/pkg/dataplane"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/actions"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo"
	templateversion "github.com/tidbcloud/cluster-service-ng/pkg/template/version"
	"github.com/tidbcloud/cluster-service-ng/pkg/utils"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

type ApplyDataCleanerAction struct {
	dataplaneManager   dataplane.Manager
	sharedPoolRepo     repo.SharedPoolRepo
	templateVersionSvc *templateversion.Service
	imageConfig        *config.ImageConfig
	bizConfig          *config.BizConfig
}

const ApplyDataCleanerActionName = "apply-data-cleaner"

func NewApplyDataCleanerAction(
	dataplaneManager dataplane.Manager,
	sharedPoolRepo repo.SharedPoolRepo,
	templateVersionSvc *templateversion.Service,
	imageConfig *config.ImageConfig,
	bizConfig *config.BizConfig,
) actions.Action {
	return &ApplyDataCleanerAction{
		dataplaneManager:   dataplaneManager,
		sharedPoolRepo:     sharedPoolRepo,
		templateVersionSvc: templateVersionSvc,
		imageConfig:        imageConfig,
		bizConfig:          bizConfig,
	}
}

func (a *ApplyDataCleanerAction) Name() string {
	return ApplyDataCleanerActionName
}

func (a *ApplyDataCleanerAction) Invoke(args actions.ActionArgs) error {
	// generate context
	ctx := utils.GenerateContext(args)
	tilog.Info(ctx, "create data cleaner action invoked")

	if a.imageConfig.DataCleanerImage == "" {
		tilog.Info(ctx, "data cleaner image is not set, skip applying data cleaner")
		return nil
	}

	sharedPoolId, err := args.GetString(workflow.SharedPoolIdKey)
	if err != nil {
		return fmt.Errorf("failed to get sharedPool id: %w", err)
	}

	// load sharedPool
	sharedPool, err := a.sharedPoolRepo.Load(ctx, sharedPoolId, false)
	if err != nil {
		return fmt.Errorf("failed to load sharedPool: %w", err)
	}

	if a.imageConfig.ImagePullPolicy == "" {
		// set default image pull policy if not set
		a.imageConfig.ImagePullPolicy = "IfNotPresent"
	}

	if a.bizConfig.DataCleanerCron == "" {
		// set default cron if not set
		a.bizConfig.DataCleanerCron = "0 * * * *" // default to run every hour
	}

	// render template
	templArgs := map[string]string{
		"ClusterId": sharedPool.GetId(),
		"DataCleanerImage": fmt.Sprintf("%s/%s",
			a.imageConfig.Registry,
			a.imageConfig.DataCleanerImage,
		),
		"PDAddress":       sharedPool.GetPdAddress(),
		"ImagePullPolicy": a.imageConfig.ImagePullPolicy,
		"Cron":            a.bizConfig.DataCleanerCron,
	}

	remoteClient, err := a.dataplaneManager.GetClient(sharedPool.GetDataplaneName())
	if err != nil {
		tilog.Error(ctx, "failed to get remote client for shared pool", zap.String("dataplaneName", sharedPool.GetDataplaneName()), zap.Error(err))
		return fmt.Errorf("failed to get remote client for shared pool: %w", err)
	}

	// data cleaner cron job
	err = templateExecuteWithVersionResolver(ctx, remoteClient, "data_cleaner", "", templArgs, false, a.templateVersionSvc)
	if err != nil {
		tilog.Error(ctx, "failed to apply data cleaner", zap.String("sharedpool", sharedPool.GetId()), zap.Error(err))
		return fmt.Errorf("failed to apply data cleaner: %w", err)
	}
	tilog.Info(ctx, "apply data cleaner successfully", zap.String("sharedpool", sharedPool.GetId()))

	return nil
}
