package dag

import (
	"fmt"
	"os"
	"strings"
	"time"

	meteringsdkmetawriter "github.com/pingcap/metering_sdk/writer/meta"
	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/config"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow/action"
	braction "github.com/tidbcloud/cluster-service-ng/domain/workflow/action/br"
	exportaction "github.com/tidbcloud/cluster-service-ng/domain/workflow/action/export"
	"github.com/tidbcloud/cluster-service-ng/pkg/clusterconfig"
	"github.com/tidbcloud/cluster-service-ng/pkg/clustergateway"
	"github.com/tidbcloud/cluster-service-ng/pkg/dataplane"
	"github.com/tidbcloud/cluster-service-ng/pkg/dataplanemgr"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/actions"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/dag"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/executor"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/etcd/runtime"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo"
	templateversion "github.com/tidbcloud/cluster-service-ng/pkg/template/version"
	"github.com/tidbcloud/pkgv2/db"
	kubeclient "sigs.k8s.io/controller-runtime/pkg/client"
)

const (
	ExecutorScope                 = "cluster-ng-server"
	coprocessorWorkerWaitInterval = time.Second * 10
	coprocessorWorkerWaitTimeout  = time.Minute * 60
)

func newCoprocessorWorkerWaitAction(asyncAction actions.AsyncAction, interval, timeout time.Duration) actions.Action {
	return actions.CreateActionFromAsync(asyncAction, interval, timeout)
}

// DagExecutor process sharedpool and tidbgroup related task.
func NewDagExecutor(
	sharedPoolRepo repo.SharedPoolRepo,
	pauseResumeOperationRepo repo.PauseResumeOperationRepo,
	lockRepo repo.LockRepo,
	cassandraProxyStateRepo repo.CassandraProxyStateRepo,
	tidbClusterRepo repo.TiDBClusterRepo,
	tidbGroupRepo repo.TiDBGroupRepo,
	dmClusterRepo repo.DMClusterRepo,
	exportRepo repo.ExportRepo,
	lwRestoreRepo repo.LightweightRestoreRepo,
	classicRestoreRepo repo.ClassicRestoreRepo,
	manualBackupRepo repo.ManualBackupRepo,
	manualBackupRestoreRepo repo.ManualBackupRestoreRepo,
	scaleTikvDecisionRepo repo.ScaleTikvDecisionRepo,
	sharedPoolObjectStoreRepo repo.SharedPoolObjectStoreRepo,
	backupGcSettingRepo repo.BackupGcSettingRepo,
	dataplaneRepo repo.DataplaneRepo,
	nodeGroupStateRepo repo.DataplaneNodeInstanceGroupStateRepo,
	nodeGroupLockRepo repo.DataplaneNodeInstanceGroupLockRepo,
	dataplaneNodeGroupRepo repo.DataplaneNodeInstanceGroupRepo,
	autoscalePolicyBundleRepo repo.AutoscalePolicyBundleRepo,
	ruConfigRepo repo.RuConfigRepo,
	dataplaneManager dataplane.Manager,
	dataplaneClient dataplanemgr.Client,
	clustergatewayCli clustergateway.Client,
	clusterConfigClient clusterconfig.Client,
	kubeClient kubeclient.Client,
	metaWriter *meteringsdkmetawriter.MetaWriter,
	templateVersionSvc *templateversion.Service,
	serverConfig *config.ServerConfig,
	transaction db.Transactioner,
) (*executor.Executor, error) {
	actionLib := actions.NewActionLibrary()
	// Pause/resume prechecks need to query this executor, but actions must be
	// registered before the executor can be created. Bind it after construction.
	var localExecutor *executor.Executor
	pauseResumeDagQuery := func(plan string, filter executor.MatchFilter) (executor.ExecTaskIdStatus, error) {
		if localExecutor == nil {
			return nil, fmt.Errorf("pause/resume DAG executor is not initialized")
		}
		return localExecutor.QueryRunningsWithFilter(plan, filter)
	}

	// Register all actions
	registerActions(
		actionLib,
		sharedPoolRepo,
		pauseResumeOperationRepo,
		lockRepo,
		cassandraProxyStateRepo,
		tidbClusterRepo,
		tidbGroupRepo,
		dmClusterRepo,
		exportRepo,
		lwRestoreRepo,
		classicRestoreRepo,
		manualBackupRepo,
		manualBackupRestoreRepo,
		scaleTikvDecisionRepo,
		sharedPoolObjectStoreRepo,
		backupGcSettingRepo,
		dataplaneRepo,
		nodeGroupStateRepo,
		nodeGroupLockRepo,
		dataplaneNodeGroupRepo,
		autoscalePolicyBundleRepo,
		ruConfigRepo,
		dataplaneManager,
		dataplaneClient,
		clustergatewayCli,
		clusterConfigClient,
		kubeClient,
		metaWriter,
		templateVersionSvc,
		serverConfig,
		transaction,
		pauseResumeDagQuery,
	)

	dagPlans := dag.NewPlans()

	// init dag
	dagPlans = initDagPlans(dagPlans)

	localCli, err := dataplaneManager.GetClient(dataplane.LocalName)
	if err != nil {
		return nil, fmt.Errorf("failed to get local kube client: %w", err)
	}
	stg := runtime.NewExecutorTaskStorage(ExecutorScope, localCli)

	exec, err := executor.NewExecutor(actionLib, dagPlans, stg, stg, 10)
	if err != nil {
		return nil, fmt.Errorf("failed to create executor: %w", err)
	}
	// Complete the pause/resume precheck query binding before the executor starts.
	localExecutor = exec
	logger := executor.NewGeneralLogger("ng-server", os.Stdout)
	exec.RegisterEventReceiver(executor.NewEventDenyFilter(logger, executor.EventDagActionStgQueryIsDone))
	metrics := executor.NewMetricsRecorder("ng-server")
	exec.RegisterEventReceiver(metrics)
	return exec, nil
}

func initDagPlans(dagPlans *dag.Plans) *dag.Plans {
	// plans for sharedpool
	dagPlans = createSharedPoolDag(dagPlans)
	dagPlans = deleteSharedPoolDag(dagPlans)
	dagPlans = resumeSharedPoolDag(dagPlans)
	dagPlans = pauseSharedPoolDag(dagPlans)
	dagPlans = updateComponentDag(dagPlans)
	dagPlans = applyComponentDag(dagPlans)
	dagPlans = createComponentDag(dagPlans)
	dagPlans = deleteTiKVGroupDag(dagPlans)
	dagPlans = setSharedPoolPDDeploymentModeDag(dagPlans)

	// plans for tidbcluster
	dagPlans = createTiDBClusterDag(dagPlans)
	dagPlans = updateTiDBClusterDag(dagPlans)
	dagPlans = deleteTiDBClusterDag(dagPlans)

	// plans for tidbgroup
	dagPlans = createTiDBGroupDag(dagPlans)
	dagPlans = updateTiDBGroupDag(dagPlans)
	dagPlans = deleteTiDBGroupDag(dagPlans)

	// plans for dm cluster
	dagPlans = applyDMClusterDag(dagPlans)
	dagPlans = createExportDag(dagPlans)
	dagPlans = cancelExportDag(dagPlans)

	// plans for node instance group
	dagPlans = createNodeInstanceGroupDag(dagPlans)
	dagPlans = reactivateNodeInstanceGroupDag(dagPlans)
	dagPlans = deprecateNodeInstanceGroupDag(dagPlans)
	dagPlans = deleteNodeInstanceGroupDag(dagPlans)
	dagPlans = setNodeInstanceGroupInstanceTypeStatusDag(dagPlans)

	// plans for sem config
	dagPlans = applySemConfigDag(dagPlans)

	// plans for TiProxy external DNS
	dagPlans = setTiProxyExternalDNSDag(dagPlans)

	// plans for cassandra-proxy
	dagPlans = createCassandraProxyDag(dagPlans)

	// plans for br
	dagPlans = createLightweightRestoreDag(dagPlans)
	dagPlans = deleteLightweightRestoreDag(dagPlans)
	dagPlans = createLightweightXRestoreDag(dagPlans)
	dagPlans = deleteLightweightXRestoreDag(dagPlans)
	dagPlans = createClassicRestoreDag(dagPlans)
	dagPlans = deleteClassicRestoreDag(dagPlans)
	dagPlans = createManualBackupDag(dagPlans)
	dagPlans = deleteManualBackupDag(dagPlans)
	dagPlans = createManualBackupRestoreDag(dagPlans)
	dagPlans = createManualBackupXRestoreDag(dagPlans)
	dagPlans = deleteManualBackupRestoreDag(dagPlans)
	dagPlans = deleteManualBackupXRestoreDag(dagPlans)
	dagPlans = applyBackupGcSettingDag(dagPlans)
	dagPlans = upgradeTiBRAutoBackupDag(dagPlans)
	dagPlans = upgradeTiBRGCDag(dagPlans)

	// plans for coprocessor worker online lifecycle
	dagPlans = enableCoprocessorWorkerDag(dagPlans)
	dagPlans = disableCoprocessorWorkerDag(dagPlans)

	return dagPlans
}

func registerActions(
	actionLib *actions.ActionLibrary,
	sharedPoolRepo repo.SharedPoolRepo,
	pauseResumeOperationRepo repo.PauseResumeOperationRepo,
	lockRepo repo.LockRepo,
	cassandraProxyStateRepo repo.CassandraProxyStateRepo,
	tidbClusterRepo repo.TiDBClusterRepo,
	tidbGroupRepo repo.TiDBGroupRepo,
	dmClusterRepo repo.DMClusterRepo,
	exportRepo repo.ExportRepo,
	lwRestoreRepo repo.LightweightRestoreRepo,
	classicRestoreRepo repo.ClassicRestoreRepo,
	manualBackupRepo repo.ManualBackupRepo,
	manualBackupRestoreRepo repo.ManualBackupRestoreRepo,
	scaleTikvDecisionRepo repo.ScaleTikvDecisionRepo,
	sharedPoolObjectStoreRepo repo.SharedPoolObjectStoreRepo,
	backupGcSettingRepo repo.BackupGcSettingRepo,
	dataplaneRepo repo.DataplaneRepo,
	nodeGroupStateRepo repo.DataplaneNodeInstanceGroupStateRepo,
	nodeGroupLockRepo repo.DataplaneNodeInstanceGroupLockRepo,
	dataplaneNodeGroupRepo repo.DataplaneNodeInstanceGroupRepo,
	autoscalePolicyBundleRepo repo.AutoscalePolicyBundleRepo,
	ruConfigRepo repo.RuConfigRepo,
	dataplaneManager dataplane.Manager,
	dataplaneClient dataplanemgr.Client,
	clustergatewayCli clustergateway.Client,
	clusterConfigClient clusterconfig.Client,
	kubeClient kubeclient.Client,
	metaWriter *meteringsdkmetawriter.MetaWriter,
	templateVersionSvc *templateversion.Service,
	serverConfig *config.ServerConfig,
	transaction db.Transactioner,
	pauseResumeDagQuery func(string, executor.MatchFilter) (executor.ExecTaskIdStatus, error),
) {
	imageConfig := &serverConfig.ImageConfig
	bizConfig := &serverConfig.BizConfig
	dataPlaneMeteringConfig := serverConfig.DataPlaneMetering
	controlPlaneMeteringConfig := serverConfig.ControlPlaneMetering
	pauseResumeOwnerGuard := action.NewPauseResumeOwnerGuard(pauseResumeOperationRepo, sharedPoolRepo, lockRepo)
	must(actionLib.Register(action.DecoratePauseResumeStage(pauseResumeOperationRepo, apiv1.PauseResumeOperation_PRECHECK,
		action.NewPausePrecheckAction(sharedPoolRepo, dataplaneRepo, dataplaneManager, bizConfig.DefaultTiBRConfig.DisableFlushWAL,
			pauseResumeDagQuery, PauseResumePlans(), PauseResumeConflictPlans()))))
	must(actionLib.Register(action.DecoratePauseResumeStage(pauseResumeOperationRepo, apiv1.PauseResumeOperation_FLUSH_WAL,
		actions.GuardAction(braction.NewStrictFlushWALAction(sharedPoolRepo, dataplaneRepo, clustergatewayCli, dataplaneManager, bizConfig.DefaultTiBRConfig.DisableFlushWAL), pauseResumeOwnerGuard.Run))))
	must(actionLib.Register(action.DecoratePauseResumeStage(pauseResumeOperationRepo, apiv1.PauseResumeOperation_APPLY_SUSPEND,
		braction.NewApplyPausedBRLifecycleRulesAction(backupGcSettingRepo, sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient(), pauseResumeOwnerGuard.Run))))
	must(actionLib.Register(action.DecoratePauseResumeStage(pauseResumeOperationRepo, apiv1.PauseResumeOperation_APPLY_SUSPEND,
		braction.NewDisableManualBackupLifecycleRulesAction(sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient(), pauseResumeOwnerGuard.Run))))
	must(actionLib.Register(action.DecoratePauseResumeStage(pauseResumeOperationRepo, apiv1.PauseResumeOperation_APPLY_SUSPEND,
		actions.GuardAction(action.NewSetSuspendComputeAction(action.ApplySuspendComputeActionName, true, entity.PauseResumeStatePausing, sharedPoolRepo, dataplaneManager), pauseResumeOwnerGuard.Run))))
	must(actionLib.Register(action.DecoratePauseResumeStage(pauseResumeOperationRepo, apiv1.PauseResumeOperation_WAIT_SUSPENDED,
		action.NewPauseResumeWaitAction(action.NewWaitSuspendComputeAction(action.WaitSuspendedActionName, true, sharedPoolRepo, dataplaneManager)))))
	must(actionLib.Register(action.NewCompletePauseResumeAction(action.CompletePauseActionName, entity.PauseResumeStatePaused, true, sharedPoolRepo, pauseResumeOperationRepo, transaction, lockRepo)))
	must(actionLib.Register(action.DecoratePauseResumeStage(pauseResumeOperationRepo, apiv1.PauseResumeOperation_APPLY_RESUME,
		actions.GuardAction(action.NewSetSuspendComputeAction(action.ApplyResumeComputeActionName, false, entity.PauseResumeStateResuming, sharedPoolRepo, dataplaneManager), pauseResumeOwnerGuard.Run))))
	must(actionLib.Register(action.DecoratePauseResumeStage(pauseResumeOperationRepo, apiv1.PauseResumeOperation_WAIT_AVAILABLE,
		action.NewPauseResumeWaitAction(action.NewWaitSuspendComputeAction(action.WaitAvailableActionName, false, sharedPoolRepo, dataplaneManager)))))
	must(actionLib.Register(action.DecoratePauseResumeStage(pauseResumeOperationRepo, apiv1.PauseResumeOperation_WAIT_AVAILABLE,
		braction.NewDrainArchiveBeforeResumeAction(sharedPoolRepo, dataplaneManager, pauseResumeOwnerGuard.Run))))
	must(actionLib.Register(action.DecoratePauseResumeStage(pauseResumeOperationRepo, apiv1.PauseResumeOperation_WAIT_AVAILABLE,
		braction.NewTriggerArchiveAfterResumeAction(sharedPoolRepo, backupGcSettingRepo, dataplaneManager, clustergatewayCli, pauseResumeOwnerGuard.Run))))
	must(actionLib.Register(action.DecoratePauseResumeStage(pauseResumeOperationRepo, apiv1.PauseResumeOperation_WAIT_AVAILABLE,
		braction.NewRestoreBRLifecycleRulesAction(backupGcSettingRepo, sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient(), pauseResumeOwnerGuard.Run))))
	must(actionLib.Register(action.DecoratePauseResumeStage(pauseResumeOperationRepo, apiv1.PauseResumeOperation_WAIT_AVAILABLE,
		braction.NewRestoreManualBackupLifecycleRulesAction(sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient(), pauseResumeOwnerGuard.Run))))
	must(actionLib.Register(action.NewCompletePauseResumeAction(action.CompleteResumeActionName, entity.PauseResumeStateNone, false, sharedPoolRepo, pauseResumeOperationRepo, transaction, lockRepo)))
	must(actionLib.Register(braction.NewDrainArchiveBeforeDeleteAction(sharedPoolRepo, dataplaneManager)))
	must(actionLib.Register(action.NewUpdateResourceGroupAction(sharedPoolRepo, tidbClusterRepo, clustergatewayCli)))
	must(actionLib.Register(action.NewBootstrapTiDBGroupAction(sharedPoolRepo, dataplaneManager, clusterConfigClient)))
	must(actionLib.Register(action.NewCreateTiDBSemConfigAction(sharedPoolRepo, dataplaneManager, clusterConfigClient)))
	must(actionLib.Register(action.NewApplyTiDBSemConfigAction(sharedPoolRepo, dataplaneManager, clusterConfigClient)))
	must(actionLib.Register(action.NewRestartTiDBGroupsAction(sharedPoolRepo, tidbGroupRepo, clustergatewayCli)))
	must(actionLib.Register(action.NewApplyClusterAgentAction(dataplaneManager, sharedPoolRepo, templateVersionSvc, imageConfig, bizConfig)))
	must(actionLib.Register(action.NewApplyTiCiAction(sharedPoolRepo, dataplaneManager, dataplaneClient, templateVersionSvc, imageConfig)))
	must(actionLib.Register(action.NewApplyDataCleanerAction(dataplaneManager, sharedPoolRepo, templateVersionSvc, imageConfig, bizConfig)))
	must(actionLib.Register(action.NewCreateTiDBAuthTokenSecretAction(sharedPoolRepo, dataplaneManager)))
	must(actionLib.Register(action.NewCreateClusterCertAction(
		sharedPoolRepo, tidbClusterRepo, tidbGroupRepo, dataplaneManager, dataplaneClient.GetDataPlaneServiceClient(), bizConfig)))
	must(actionLib.Register(action.NewCreateClusterIssuerAction(sharedPoolRepo, dataplaneManager, bizConfig)))
	must(actionLib.Register(action.NewApplySharedPoolAction(sharedPoolRepo, dataplaneManager, templateVersionSvc)))
	must(actionLib.Register(action.NewApplyDefaultSvcAction(sharedPoolRepo, dataplaneManager, templateVersionSvc)))
	must(actionLib.Register(actions.CreateActionFromAsync(
		action.NewApplyPDGroupAction(
			sharedPoolRepo, dataplaneManager,
			clusterConfigClient,
			dataplaneClient.GetDataPlaneServiceClient(),
			templateVersionSvc,
			imageConfig,
			dataPlaneMeteringConfig),
		time.Second*10, time.Minute*5,
	)))
	must(actionLib.Register(action.NewApplyPDMicroserviceComponentsAction(
		sharedPoolRepo,
		dataplaneManager,
		clusterConfigClient,
		dataplaneClient.GetDataPlaneServiceClient(),
		templateVersionSvc,
		imageConfig,
		dataPlaneMeteringConfig,
	)))
	must(actionLib.Register(action.NewDeletePDMicroserviceComponentsAction(
		sharedPoolRepo,
		dataplaneManager,
		clusterConfigClient,
		dataplaneClient.GetDataPlaneServiceClient(),
		templateVersionSvc,
		imageConfig,
		dataPlaneMeteringConfig,
	)))
	must(actionLib.Register(actions.CreateActionFromAsync(
		action.NewPatchPDDeploymentModeAction(sharedPoolRepo, dataplaneManager),
		time.Second*10, time.Minute*30,
	)))
	must(actionLib.Register(actions.CreateActionFromAsync(
		action.NewWaitPDMicroserviceComponentsReadyAction(sharedPoolRepo, dataplaneManager),
		time.Second*10, time.Minute*10,
	)))
	must(actionLib.Register(actions.CreateActionFromAsync(
		action.NewWaitPDMicroserviceComponentsGoneAction(sharedPoolRepo, dataplaneManager),
		time.Second*10, time.Minute*10,
	)))
	must(actionLib.Register(action.NewCommitPDDeploymentModeAction(sharedPoolRepo)))
	must(actionLib.Register(action.NewApplyTiDBGroupAction(
		sharedPoolRepo,
		tidbGroupRepo,
		tidbClusterRepo,
		dataplaneManager,
		clusterConfigClient,
		dataplaneClient.GetLoadBalancerServiceClient(),
		dataplaneClient.GetDataPlaneServiceClient(),
		templateVersionSvc,
		imageConfig,
		dataPlaneMeteringConfig,
		bizConfig,
	)))
	must(actionLib.Register(action.NewApplyAutoscalerAction(sharedPoolRepo, dataplaneManager, templateVersionSvc)))
	must(actionLib.Register(action.NewApplyTiProxyGroupAction(
		sharedPoolRepo,
		tidbClusterRepo,
		tidbGroupRepo,
		dataplaneManager,
		clusterConfigClient,
		dataplaneClient.GetDataPlaneServiceClient(),
		dataplaneClient.GetLoadBalancerServiceClient(),
		templateVersionSvc,
		imageConfig,
		dataPlaneMeteringConfig,
	)))
	must(actionLib.Register(action.NewApplyTiProxyExternalDNSAction(
		sharedPoolRepo,
		tidbClusterRepo,
		tidbGroupRepo,
		dataplaneManager,
		dataplaneClient.GetDataPlaneServiceClient(),
	)))
	must(actionLib.Register(action.NewApplyCassandraProxyAction(
		sharedPoolRepo,
		cassandraProxyStateRepo,
		tidbClusterRepo,
		dataplaneManager,
		dataplaneClient.GetDataPlaneServiceClient(),
		templateVersionSvc,
		imageConfig,
	)))
	must(actionLib.Register(action.NewWaitCassandraProxyReadyAction(
		dataplaneManager,
		cassandraProxyStateRepo,
	)))
	must(actionLib.Register(newCoprocessorWorkerWaitAction(
		action.NewWaitCoprocessorWorkerReadyAction(dataplaneManager),
		coprocessorWorkerWaitInterval, coprocessorWorkerWaitTimeout,
	)))
	must(actionLib.Register(newCoprocessorWorkerWaitAction(
		action.NewWaitCoprocessorWorkerGoneAction(dataplaneManager),
		coprocessorWorkerWaitInterval, coprocessorWorkerWaitTimeout,
	)))
	must(actionLib.Register(newCoprocessorWorkerWaitAction(
		action.NewWaitTiKVGroupRemoteWorkerRolloutAction(dataplaneManager),
		coprocessorWorkerWaitInterval, coprocessorWorkerWaitTimeout,
	)))
	must(actionLib.Register(newCoprocessorWorkerWaitAction(
		action.NewWaitDefaultTiKVWorkerReadyAction(dataplaneManager),
		coprocessorWorkerWaitInterval, coprocessorWorkerWaitTimeout,
	)))
	must(actionLib.Register(action.NewApplyCassandraProxyLBAction(
		sharedPoolRepo,
		tidbClusterRepo,
		dataplaneClient.GetLoadBalancerServiceClient(),
	)))
	must(actionLib.Register(action.NewApplyCassandraProxyPLSAction(
		sharedPoolRepo,
		tidbClusterRepo,
		dataplaneClient.GetPrivateLinkConnectionServiceClient(),
	)))
	must(actionLib.Register(action.NewDeleteCassandraProxyPLSAction(
		sharedPoolRepo,
		dataplaneClient.GetPrivateLinkConnectionServiceClient(),
	)))
	must(actionLib.Register(action.NewDeleteCassandraProxyLBAction(
		sharedPoolRepo,
		dataplaneClient.GetLoadBalancerServiceClient(),
	)))

	must(actionLib.Register(action.NewApplyComponentGroupAction(
		sharedPoolRepo,
		dataplaneManager,
		clusterConfigClient,
		dataplaneClient.GetDataPlaneServiceClient(),
		templateVersionSvc,
		imageConfig,
		dataPlaneMeteringConfig,
		bizConfig,
	)))

	must(actionLib.Register(action.NewApplyTiDBPrivilegedAction(sharedPoolRepo, dataplaneManager, clusterConfigClient, templateVersionSvc, imageConfig)))
	must(actionLib.Register(action.NewApplyTiDBDiagnosisAction(sharedPoolRepo, dataplaneManager, clusterConfigClient, templateVersionSvc, imageConfig)))
	must(actionLib.Register(action.NewApplyComponentHpaAction(sharedPoolRepo, autoscalePolicyBundleRepo, dataplaneManager, templateVersionSvc, bizConfig, ruConfigRepo)))
	must(actionLib.Register(action.NewCreatePriorityClassAction(sharedPoolRepo, dataplaneManager)))
	must(actionLib.Register(action.NewCreateServiceAccountAction(sharedPoolRepo, dataplaneManager)))
	must(actionLib.Register(action.NewPreCreateSharedPoolAction(dataplaneManager, dataplaneClient.GetDataPlaneServiceClient())))
	must(actionLib.Register(action.NewPreCreateTiDBGroupAction(tidbClusterRepo)))
	must(actionLib.Register(action.NewPreCreateTiDBClusterAction(sharedPoolRepo)))
	must(actionLib.Register(actions.CreateActionFromAsync(
		action.NewClearDataPlaneAction(dataplaneManager, sharedPoolRepo),
		time.Second*10, time.Minute*30,
	)))
	must(actionLib.Register(action.NewPruneDataplaneAction(sharedPoolRepo, nodeGroupStateRepo, nodeGroupLockRepo, dataplaneNodeGroupRepo, dataplaneClient.GetDataPlaneServiceClient())))
	must(actionLib.Register(action.NewSetKeySpaceAction(sharedPoolRepo, tidbClusterRepo, clustergatewayCli)))
	must(actionLib.Register(action.NewDeleteKeySpaceAction(sharedPoolRepo, tidbClusterRepo, clustergatewayCli)))
	must(actionLib.Register(action.NewDeletePlacementPolicyAction(dataplaneManager)))
	must(actionLib.Register(action.NewCreateKeyspaceRoleAction(sharedPoolRepo, tidbClusterRepo, dataplaneClient.GetKeyspaceRoleServiceClient())))
	must(actionLib.Register(action.NewDeleteKeyspaceRoleAction(sharedPoolRepo, tidbClusterRepo, dataplaneClient.GetKeyspaceRoleServiceClient())))
	must(actionLib.Register(action.NewApplyLoadBalanceAction(
		sharedPoolRepo,
		tidbClusterRepo,
		tidbGroupRepo,
		dataplaneClient.GetLoadBalancerServiceClient(),
		dataplaneClient.GetDataPlaneServiceClient(),
	)))
	must(actionLib.Register(action.NewDeleteLoadBalanceAction(sharedPoolRepo, tidbGroupRepo, dataplaneClient.GetLoadBalancerServiceClient())))
	must(actionLib.Register(action.NewCleanUpCertAction(sharedPoolRepo, dataplaneManager)))
	must(actionLib.Register(actions.CreateActionFromAsync(
		action.NewWaitTiKVGroupGoneAction(dataplaneManager),
		time.Second*10, time.Hour*24,
	)))
	must(actionLib.Register(action.NewCommitDeleteComponentAction(sharedPoolRepo)))
	must(actionLib.Register(action.NewCreatePlaceholderDeploymentAction(sharedPoolRepo, dataplaneManager, bizConfig.PlaceholderReplicas)))
	must(actionLib.Register(
		actions.CreateActionFromAsync(
			action.NewCreateDataplaneAction(dataplaneClient.GetDataPlaneServiceClient(), bizConfig, controlPlaneMeteringConfig.MetadataPlan()),
			1*time.Minute, 2*time.Hour,
		),
	))
	must(actionLib.Register(
		actions.CreateActionFromAsync(
			action.NewCreateBucketAction(dataplaneClient.GetBucketServiceClient()),
			1*time.Minute, 1*time.Hour,
		),
	))
	must(actionLib.Register(
		actions.CreateActionFromAsync(
			action.NewApplyInternalBucketLifecycleRulesAction(sharedPoolRepo, dataplaneClient.GetBucketServiceClient()),
			1*time.Minute, 30*time.Minute,
		),
	))
	// o11y realted
	must(actionLib.Register(action.NewCreateO11ySharedPoolAgentAction(bizConfig.O11yBizType, bizConfig.DisableO11y, dataplaneClient, kubeClient, sharedPoolRepo, bizConfig.O11yAgentNamespace)))
	must(actionLib.Register(action.NewCreateO11yTiDBGroupAgentAction(bizConfig.O11yBizType, bizConfig.DisableO11y, dataplaneClient, kubeClient, sharedPoolRepo, bizConfig.O11yAgentNamespace)))
	must(actionLib.Register(action.NewDeleteO11ySharedPoolAgentAction(bizConfig.DisableO11y, kubeClient, bizConfig.O11yAgentNamespace)))
	must(actionLib.Register(action.NewDeleteO11yTiDBGroupAgentAction(bizConfig.DisableO11y)))
	must(actionLib.Register(action.NewDeleteO11yTiDBGroupAgentsByClusterAction(bizConfig.DisableO11y, kubeClient, bizConfig.O11yAgentNamespace)))
	// vac related
	must(actionLib.Register(action.NewCreateVolumeAttributesClassAction(sharedPoolRepo, dataplaneManager)))
	// config center
	must(actionLib.Register(action.NewCreateSharedPoolConfigAction(sharedPoolRepo, clusterConfigClient)))
	must(actionLib.Register(action.NewDeleteSharedPoolMetaAction(clusterConfigClient)))
	must(actionLib.Register(action.NewDeleteTiKVComponentConfigAction(clusterConfigClient)))
	must(actionLib.Register(action.NewCreateTiDBClusterMetaAction(tidbGroupRepo, sharedPoolRepo, clusterConfigClient)))
	must(actionLib.Register(action.NewCreateTiDBGroupConfigAction(tidbGroupRepo, clusterConfigClient)))
	must(actionLib.Register(action.NewDeleteTiDBClusterMetaAction(clusterConfigClient)))
	must(actionLib.Register(action.NewDeleteTiDBGroupConfigAction(clusterConfigClient)))
	must(actionLib.Register(action.NewCreateComponentConfigAction(sharedPoolRepo, clusterConfigClient)))
	// br related
	must(actionLib.Register(braction.NewInitBRConfigsInConfigCenterAction(sharedPoolRepo, clusterConfigClient, serverConfig)))
	must(actionLib.Register(braction.NewValidateTiBRAutoBackupPlacementAction(sharedPoolRepo, dataplaneClient.GetDataPlaneServiceClient(), bizConfig)))
	must(actionLib.Register(braction.NewCreateTiBRAutoBackupAction(sharedPoolRepo, lwRestoreRepo, dataplaneManager, templateVersionSvc, imageConfig, bizConfig, dataplaneClient.GetDataPlaneServiceClient(), clusterConfigClient)))
	must(actionLib.Register(braction.NewCreateTiBRRestoreAction(sharedPoolRepo, lwRestoreRepo, dataplaneManager, templateVersionSvc, imageConfig, bizConfig, dataplaneClient.GetDataPlaneServiceClient(), clusterConfigClient)))
	must(actionLib.Register(braction.NewRestartTiDBGroupsAfterRestoreAction(lwRestoreRepo, tidbGroupRepo, sharedPoolRepo, clustergatewayCli)))
	must(actionLib.Register(braction.NewDeleteAllTiBRsAction(sharedPoolRepo, dataplaneManager)))
	must(actionLib.Register(braction.NewDeleteTiBRRestoreAction(lwRestoreRepo, sharedPoolRepo, dataplaneManager)))
	must(actionLib.Register(braction.NewExecuteKernelLightWeightRestoreAction(sharedPoolRepo, lwRestoreRepo, clustergatewayCli, bizConfig)))
	must(actionLib.Register(braction.NewExecuteKernelLightWeightRestorePackedAction(lwRestoreRepo, sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient(), clustergatewayCli, bizConfig)))
	must(actionLib.Register(braction.NewExecuteKernelLightWeightPackBackupAction(lwRestoreRepo, sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient(), clustergatewayCli)))
	must(actionLib.Register(braction.NewGrantXRestoreBucketAccessAction(lwRestoreRepo, sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient())))
	must(actionLib.Register(braction.NewRevokeXRestoreBucketAccessAction(lwRestoreRepo, manualBackupRestoreRepo, sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient())))
	must(actionLib.Register(braction.NewMarkLightWeightRestoreSucceededAction(lwRestoreRepo, scaleTikvDecisionRepo)))
	must(actionLib.Register(braction.NewMarkLightWeightRestoreDeletedAction(lwRestoreRepo, scaleTikvDecisionRepo)))
	must(actionLib.Register(braction.NewGetPitrBackupInfoForRestoreAction(sharedPoolRepo, lwRestoreRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient(), clustergatewayCli)))
	must(actionLib.Register(braction.NewScaleForLWRestoreAction(lwRestoreRepo, sharedPoolRepo, scaleTikvDecisionRepo, clustergatewayCli, dataplaneManager, transaction, bizConfig)))
	must(actionLib.Register(braction.NewApplyBucketLifecycleRulesAction(backupGcSettingRepo, sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient())))
	must(actionLib.Register(braction.NewApplyTransitionSafeLifecycleRulesAction(backupGcSettingRepo, sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient(), dataplaneManager)))
	must(actionLib.Register(braction.NewApplyDeletingBRLifecycleRulesAction(backupGcSettingRepo, sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient())))
	must(actionLib.Register(braction.NewEnableDeletingManualBackupLifecycleRulesAction(sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient())))
	must(actionLib.Register(braction.NewApplyTiBRGCAction(backupGcSettingRepo, sharedPoolRepo, dataplaneManager, templateVersionSvc, imageConfig, bizConfig, dataplaneClient.GetDataPlaneServiceClient(), clusterConfigClient)))
	must(actionLib.Register(braction.NewDeleteTiBRGCAction(backupGcSettingRepo, sharedPoolRepo, dataplaneManager)))
	must(actionLib.Register(braction.NewUpdateGCSettingForSPDeletingAction(backupGcSettingRepo, bizConfig)))
	must(actionLib.Register(braction.NewMarkGCSettingSPRunningAction(backupGcSettingRepo)))
	must(actionLib.Register(braction.NewMarkGCSettingSPDeletedAction(backupGcSettingRepo)))
	must(actionLib.Register(braction.NewInitGCSettingForCreatingAction(backupGcSettingRepo, bizConfig)))
	must(actionLib.Register(braction.NewScaleForClassicRestoreAction(classicRestoreRepo, sharedPoolRepo, scaleTikvDecisionRepo, clustergatewayCli, dataplaneManager, transaction, bizConfig)))
	must(actionLib.Register(braction.NewExecuteClassicRestoreAction(sharedPoolRepo, dataplaneManager, classicRestoreRepo, templateVersionSvc, bizConfig, imageConfig, dataplaneClient.GetDataPlaneServiceClient(), clusterConfigClient, clustergatewayCli)))
	must(actionLib.Register(braction.NewDeleteCloudAdminAfterClassicRestoreAction(classicRestoreRepo, sharedPoolRepo, clustergatewayCli)))
	must(actionLib.Register(braction.NewRestartTiDBGroupsAfterClassicRestoreAction(classicRestoreRepo, tidbGroupRepo, sharedPoolRepo, clustergatewayCli)))
	must(actionLib.Register(braction.NewMarkClassicRestoreSucceededAction(classicRestoreRepo, scaleTikvDecisionRepo)))
	must(actionLib.Register(braction.NewDeleteClassicRestoreCRAction(classicRestoreRepo, sharedPoolRepo, dataplaneManager)))
	must(actionLib.Register(braction.NewMarkClassicRestoreDeletedAction(classicRestoreRepo, scaleTikvDecisionRepo)))
	must(actionLib.Register(braction.NewCreateTiBRManualBackupAction(sharedPoolRepo, manualBackupRepo, dataplaneManager, templateVersionSvc, imageConfig, bizConfig, dataplaneClient.GetDataPlaneServiceClient(), clusterConfigClient)))
	must(actionLib.Register(braction.NewDeleteTiBRManualBackupAction(manualBackupRepo, sharedPoolRepo, dataplaneManager)))
	must(actionLib.Register(braction.NewExecuteKernelManualBackupAction(manualBackupRepo, sharedPoolRepo, clustergatewayCli, dataplaneManager)))
	must(actionLib.Register(braction.NewExecuteKernelPackManualBackupAction(manualBackupRepo, sharedPoolRepo, clustergatewayCli, dataplaneManager)))
	must(actionLib.Register(braction.NewExecuteKernelManualCopyBackupAction(manualBackupRepo, sharedPoolRepo, clustergatewayCli, dataplaneManager, bizConfig)))
	must(actionLib.Register(braction.NewMarkManualBackupSucceededAction(manualBackupRepo)))
	must(actionLib.Register(braction.NewMarkManualBackupDeletingAction(manualBackupRepo)))
	must(actionLib.Register(braction.NewDeleteManualBackupDataAction(manualBackupRepo, sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient())))
	must(actionLib.Register(braction.NewMarkManualBackupDeletedAction(manualBackupRepo, backupGcSettingRepo, transaction)))
	// manual backup restore related
	must(actionLib.Register(braction.NewCreateTiBRManualRestoreAction(sharedPoolRepo, manualBackupRestoreRepo, dataplaneManager, templateVersionSvc, imageConfig, bizConfig, dataplaneClient.GetDataPlaneServiceClient(), clusterConfigClient)))
	must(actionLib.Register(braction.NewExecuteKernelRestoreFromManualBackupAction(manualBackupRestoreRepo, sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient(), clustergatewayCli, bizConfig)))
	must(actionLib.Register(braction.NewDeleteTiBRManualRestoreAction(manualBackupRestoreRepo, sharedPoolRepo, dataplaneManager)))
	must(actionLib.Register(braction.NewMarkManualRestoreSucceededAction(manualBackupRestoreRepo, scaleTikvDecisionRepo)))
	must(actionLib.Register(braction.NewScaleForManualBackupRestoreAction(manualBackupRestoreRepo, sharedPoolRepo, scaleTikvDecisionRepo, clustergatewayCli, dataplaneManager, transaction, bizConfig)))
	must(actionLib.Register(braction.NewGrantManualRestoreBucketAccessAction(manualBackupRestoreRepo, sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient())))
	must(actionLib.Register(braction.NewRevokeManualRestoreBucketAccessAction(manualBackupRestoreRepo, lwRestoreRepo, sharedPoolRepo, sharedPoolObjectStoreRepo, dataplaneClient.GetBucketServiceClient())))
	must(actionLib.Register(braction.NewMarkManualRestoreDeletedAction(manualBackupRestoreRepo, scaleTikvDecisionRepo)))
	must(actionLib.Register(braction.NewRestartTiDBGroupsAfterManualRestoreAction(manualBackupRestoreRepo, tidbGroupRepo, sharedPoolRepo, clustergatewayCli)))
	must(actionLib.Register(braction.NewDeleteTiBRAutoBackupAction(sharedPoolRepo, dataplaneManager)))
	must(actionLib.Register(braction.NewFlushWALAction(sharedPoolRepo, dataplaneRepo, clustergatewayCli, dataplaneManager, bizConfig.DefaultTiBRConfig.DisableFlushWAL)))
	// metering related
	meteringMetadataAction := action.NewSendMeteringMetadataAction(
		tidbClusterRepo,
		sharedPoolRepo,
		clustergatewayCli,
		metaWriter,
		getEnv(&serverConfig.BizConfig),
		serverConfig.Region,
		controlPlaneMeteringConfig.MetadataPlan(),
		controlPlaneMeteringConfig.MetadataEnabled(),
	)
	must(actionLib.Register(meteringMetadataAction))
	guardLifecycleMetadata := func(lifecycleAction actions.Action) actions.Action {
		if !controlPlaneMeteringConfig.MetadataEnabled() {
			return lifecycleAction
		}
		return actions.GuardAction(lifecycleAction, pauseResumeOwnerGuard.Run)
	}
	must(actionLib.Register(guardLifecycleMetadata(action.NewSendSharedPoolLifecycleMetadataAction(
		action.SendPausingMeteringMetadataActionName, action.SharedPoolMeteringStatePausing, false, pauseResumeOperationRepo, meteringMetadataAction))))
	must(actionLib.Register(guardLifecycleMetadata(action.NewSendSharedPoolLifecycleMetadataAction(
		action.SendPausedMeteringMetadataActionName, action.SharedPoolMeteringStatePaused, true, pauseResumeOperationRepo, meteringMetadataAction))))
	must(actionLib.Register(guardLifecycleMetadata(action.NewSendSharedPoolLifecycleMetadataAction(
		action.SendResumingMeteringMetadataActionName, action.SharedPoolMeteringStateResuming, false, pauseResumeOperationRepo, meteringMetadataAction))))
	must(actionLib.Register(guardLifecycleMetadata(action.NewSendSharedPoolLifecycleMetadataAction(
		action.SendActiveMeteringMetadataActionName, action.SharedPoolMeteringStateActive, true, pauseResumeOperationRepo, meteringMetadataAction))))
	must(actionLib.Register(actions.CreateActionFromAsync(
		action.NewEnsureClusterUpgradePolicyAction(sharedPoolRepo, dataplaneManager),
		time.Second*10,
		time.Minute*5,
	)))
	must(actionLib.Register(action.NewUpdateComponentGroupAction(
		sharedPoolRepo,
		tidbGroupRepo,
		tidbClusterRepo,
		dataplaneManager,
		clusterConfigClient,
		dataplaneClient.GetLoadBalancerServiceClient(),
		dataplaneClient.GetDataPlaneServiceClient(),
		imageConfig,
		dataPlaneMeteringConfig,
	)))
	// DM cluster related
	must(actionLib.Register(action.NewApplyDMClusterAction(sharedPoolRepo, dmClusterRepo, dataplaneManager, dataplaneClient.GetDataPlaneServiceClient(), imageConfig, clusterConfigClient)))
	// export related
	must(actionLib.Register(exportaction.NewCreateExportDumplingAction(
		exportRepo,
		sharedPoolRepo,
		tidbClusterRepo,
		tidbGroupRepo,
		dataplaneManager,
		dataplaneClient.GetDataPlaneServiceClient(),
		transaction,
		imageConfig,
		clusterConfigClient,
		templateVersionSvc,
	)))
	must(actionLib.Register(exportaction.NewInitExportConfigsInConfigCenterAction(
		tidbClusterRepo,
		clusterConfigClient,
		serverConfig,
	)))
	must(actionLib.Register(exportaction.NewPollExportTiDBGroupAction(exportRepo, tidbGroupRepo)))
	must(actionLib.Register(exportaction.NewPollExportDumplingAction(exportRepo, sharedPoolRepo, tidbClusterRepo, dataplaneManager)))
	must(actionLib.Register(exportaction.NewDeleteExportDumplingAction(
		exportRepo,
		sharedPoolRepo,
		tidbClusterRepo,
		dataplaneManager,
		transaction,
	)))

	// instance management related
	must(actionLib.Register(action.NewCreateNodeInstanceGroupAction(nodeGroupStateRepo, nodeGroupLockRepo, dataplaneClient)))
	must(actionLib.Register(action.NewReactivateNodeInstanceGroupAction(nodeGroupStateRepo, nodeGroupLockRepo, dataplaneClient)))
	must(actionLib.Register(action.NewDeprecateNodeInstanceGroupAction(nodeGroupStateRepo, nodeGroupLockRepo, dataplaneClient)))
	must(actionLib.Register(action.NewDeleteNodeInstanceGroupAction(nodeGroupStateRepo, nodeGroupLockRepo, dataplaneClient)))
	must(actionLib.Register(action.NewSetNodeInstanceGroupInstanceTypeStatusAction(nodeGroupLockRepo, dataplaneClient)))
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

func getEnv(conf *config.BizConfig) string {
	if strings.Contains(conf.O11yBizType, "dev") {
		return "dev"
	}
	if strings.Contains(conf.O11yBizType, "staging") {
		return "staging"
	}
	if strings.Contains(conf.O11yBizType, "prod") {
		return "production"
	}
	return ""
}
