package dag

import (
	"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/runtime/dag"
)

const (
	PlanCreateSharedPool = "create-sharedpool"
	PlanDeleteSharedPool = "delete-sharedpool"
	PlanPauseSharedPool  = "pause-sharedpool"
	PlanResumeSharedPool = "resume-sharedpool"

	PlanUpdateComponent                             = "update-component"
	PlanApplyComponent                              = "apply-component"
	PlanCreateComponent                             = "create-component"
	PlanDeleteTiKVGroup                             = "delete-tikvgroup"
	PlanSetSharedPoolPDDeploymentModeToMonolithic   = "set-sharedpool-pd-deployment-mode-to-monolithic"
	PlanSetSharedPoolPDDeploymentModeToMicroservice = "set-sharedpool-pd-deployment-mode-to-microservice"

	PlanCreateTiDBCluster = "create-tidbcluster"
	PlanUpdateTiDBCluster = "update-tidbcluster"
	PlanDeleteTiDBCluster = "delete-tidbcluster"

	PlanCreateTiDBGroup = "create-tidbgroup"
	PlanUpdateTiDBGroup = "update-tidbgroup"
	PlanDeleteTiDBGroup = "delete-tidbgroup"

	// for dm cluster
	PlanApplyDMCluster = "apply-dm-cluster"

	// for node instance group
	PlanCreateNodeInstanceGroup                = "create-node-instance-group"
	PlanReactivateNodeInstanceGroup            = "reactivate-node-instance-group"
	PlanDeprecateNodeInstanceGroup             = "deprecate-node-instance-group"
	PlanDeleteNodeInstanceGroup                = "delete-node-instance-group"
	PlanSetNodeInstanceGroupInstanceTypeStatus = "set-node-instance-group-instance-type-status"

	// for same shared pool restore
	PlanCreateLightweightRestore = "create-lightweight-restore"
	PlanDeleteLightweightRestore = "delete-lightweight-restore"
	// for cross shared pool restore
	PlanCreateLightweightXRestore = "create-lightweight-x-restore"
	PlanDeleteLightweightXRestore = "delete-lightweight-x-restore"
	// for classic restore
	PlanCreateClassicRestore = "create-classic-restore"
	PlanDeleteClassicRestore = "delete-classic-restore"
	// for manual backup
	PlanCreateManualBackup         = "create-manual-backup"
	PlanDeleteManualBackup         = "delete-manual-backup"
	PlanCreateManualBackupRestore  = "create-manual-backup-restore"
	PlanDeleteManualBackupRestore  = "delete-manual-backup-restore"
	PlanCreateManualBackupXRestore = "create-manual-backup-x-restore"
	PlanDeleteManualBackupXRestore = "delete-manual-backup-x-restore"
	// tibr gc
	PlanUpdateBackupGcSetting = "update-backup-gc-setting"
	// upgrade br components
	PlanUpgradeTiBRAutoBackup = "upgrade-tibr-auto-backup"
	PlanUpgradeTiBRGC         = "upgrade-tibr-gc"

	// sem config
	PlanApplySemConfig = "apply-sem-config"

	// tiproxy external DNS
	PlanSetTiProxyExternalDNS = "set-tiproxy-external-dns"

	// cassandra-proxy
	PlanCreateCassandraProxy = "create-cassandra-proxy"

	// export
	PlanCreateExport = "create-export"
	PlanCancelExport = "cancel-export"

	// coprocessor worker online lifecycle
	PlanEnableCoprocessorWorker  = "enable-coprocessor-worker"
	PlanDisableCoprocessorWorker = "disable-coprocessor-worker"
)

// createClusterMeta -> createCloudResource ->  createClusterResource
func createSharedPoolDag(dagPlans *dag.Plans) *dag.Plans {
	// root node
	createSharedPool := dagPlans.NewPlanWithRootNode(PlanCreateSharedPool)

	// cluster resource
	createSharedPool.
		// write metering metadata if all previous steps successed
		SafeAddDependNode(action.SendMeteringMetadataActionName).
		// tici
		SafeAddDependNode(action.ApplyTiCiActionName).
		// br setup
		SafeAddDependNode(braction.MarkGCSettingSPRunningActionName).
		SafeAddDependNode(braction.ApplyBucketLifecycleRulesActionName).
		SafeAddDependNode(braction.ApplyTiBRGCActionName).
		SafeAddDependNode(braction.InitGCSettingForCreatingActionName).
		SafeAddDependNode(braction.CreateTiBRAutoBackupActionName).
		SafeAddDependNode(braction.InitBRConfigsInConfigCenterActionName).
		// svc
		SafeAddDependNode(action.ApplyDefaultSvcActionName).
		// to gen dataplane metadata
		SafeAddDependNode(action.ApplyDataCleanerActionName).
		SafeAddDependNode(action.ApplyClusterAgentActionName).
		SafeAddDependNode(action.ApplyComponentHpaActionName).
		SafeAddDependNode(action.CreateTiDBSemConfigActionName).
		SafeAddDependNode(action.BootstrapTiDBGroupActionName).
		// cluster components
		// SafeAddDependNode(action.ApplyTiDBDiagnoseActionName).
		SafeAddDependNode(action.ApplyTiDBPrivilegedActionName).
		SafeAddDependNode(action.ApplyComponentGroupActionName).
		SafeAddDependNode(action.ApplyTiProxyGroupActionName).
		SafeAddDependNode(action.ApplyPDGroupActionName).
		// issuer and cert
		SafeAddDependNode(action.CreateClusterCertActionName).
		SafeAddDependNode(action.CreateClusterIssuerActionName).
		// deploy placeholder deployment
		SafeAddDependNode(action.CreatePlaceholderDeploymentActionName).
		// deploy vac
		SafeAddDependNode(action.CreateVolumeAttributesClassActionName).
		SafeAddDependNode(action.CreateServiceAccountActionName).
		SafeAddDependNode(action.CreatePriorityClassActionName).
		SafeAddDependNode(action.ApplyClusterActionName).
		// wait dataplane ready
		SafeAddDependNode(action.PreCreateSharedPoolActionName).
		// create shared pool config
		SafeAddDependNode(action.CreateSharedPoolConfigName).
		// o11y sharedpool agent
		SafeAddDependNode(action.CreateO11ySharedPoolAgentActionName).
		// cloud resource
		// bucket
		SafeAddDependNode(action.CreateBucketActionName).
		// dataplane
		SafeAddDependNode(action.CreateDataplaneActionName)

	return dagPlans
}

func updateComponentDag(dagPlans *dag.Plans) *dag.Plans {
	// root node
	updateComponent := dagPlans.NewPlanWithRootNode(PlanUpdateComponent)

	updateComponent.
		// write metering metadata if all previous steps successed
		SafeAddDependNode(action.SendMeteringMetadataActionName).
		// clean up old resources
		SafeAddDependNode(action.CleanUpCertActionName).
		// cluster resource
		SafeAddDependNode(action.ApplyClusterAgentActionName).
		SafeAddDependNode(action.ApplyDataCleanerActionName).
		SafeAddDependNode(action.CreateClusterCertActionName). // with different component id, need new cert
		// first create tidb group
		// to gen dataplane metadata
		SafeAddDependNode(action.ApplyComponentHpaActionName).
		// scale or update shared pool if needed
		SafeAddDependNode(action.ApplyTiDBPrivilegedActionName).
		SafeAddDependNode(action.UpdateComponentGroupActionName).
		// ensure the operator observes NoConstraints before any component version update
		SafeAddDependNode(action.EnsureClusterUpgradePolicyActionName).
		// create vac before apply any component group
		SafeAddDependNode(action.CreateVolumeAttributesClassActionName)

	return dagPlans
}

func applyComponentDag(dagPlans *dag.Plans) *dag.Plans {
	// root node
	applyComponent := dagPlans.NewPlanWithRootNode(PlanApplyComponent)

	applyComponent.
		// cluster resource
		SafeAddDependNode(action.ApplyDataCleanerActionName).
		SafeAddDependNode(action.ApplyClusterAgentActionName).
		// apply cluster components
		SafeAddDependNode(action.ApplyComponentGroupActionName).
		SafeAddDependNode(action.ApplyTiProxyGroupActionName).
		SafeAddDependNode(action.ApplyTiDBGroupActionName).
		SafeAddDependNode(action.ApplyPDGroupActionName)

	return dagPlans
}

// createComponentDag creates the DAG for creating a new component in an existing shared pool.
// This is used for creating new TiKV groups with proper metering, VAC, cert, and component apply.
func createComponentDag(dagPlans *dag.Plans) *dag.Plans {
	// root node
	createComponent := dagPlans.NewPlanWithRootNode(PlanCreateComponent)

	createComponent.
		// write metering metadata if all previous steps succeeded
		SafeAddDependNode(action.SendMeteringMetadataActionName).
		// create certificates for the new component
		SafeAddDependNode(action.CreateClusterCertActionName).
		// create VAC before applying any component group
		SafeAddDependNode(action.CreateVolumeAttributesClassActionName).
		// apply the component group (renders tikv_group.templ and creates TiKVGroup CR)
		SafeAddDependNode(action.ApplyComponentGroupActionName).
		// create component config in config center
		SafeAddDependNode(action.CreateComponentConfigName)

	return dagPlans
}

func deleteTiKVGroupDag(dagPlans *dag.Plans) *dag.Plans {
	deleteTiKVGroup := dagPlans.NewPlanWithRootNode(PlanDeleteTiKVGroup)

	deleteTiKVGroup.
		// send metering metadata after all steps succeed
		SafeAddDependNode(action.SendMeteringMetadataActionName).
		// clean up cert after metadata no longer marks the component running
		SafeAddDependNode(action.CleanUpCertActionName).
		// remove component from shared pool metadata after the CR is gone
		SafeAddDependNode(action.CommitDeleteComponentActionName).
		// clean up config-center component config before metadata is removed
		SafeAddDependNode(action.DeleteTiKVComponentConfigActionName).
		// wait TiKVGroup finalizers to complete
		SafeAddDependNode(action.WaitTiKVGroupGoneActionName).
		// apply component with DELETING phase
		SafeAddDependNode(action.ApplyComponentGroupActionName)

	return dagPlans
}

func setSharedPoolPDDeploymentModeDag(dagPlans *dag.Plans) *dag.Plans {
	// SafeAddDependNode builds a dependency chain where the last node in the
	// chain becomes ready first. Keep CommitPDDeploymentModeAction as the root's
	// direct dependency so repo metadata is committed only after dataplane state
	// has reached the target mode.
	toMicroservice := dagPlans.NewPlanWithRootNode(PlanSetSharedPoolPDDeploymentModeToMicroservice)
	// Execution order: create component certificates -> apply microservice
	// components -> patch PDGroup mode -> wait components ready -> commit final metadata.
	toMicroservice.
		SafeAddDependNode(action.CommitPDDeploymentModeActionName).
		SafeAddDependNode(action.WaitPDMicroserviceComponentsReadyName).
		SafeAddDependNode(action.PatchPDDeploymentModeActionName).
		SafeAddDependNode(action.ApplyPDMicroserviceComponentsActionName).
		SafeAddDependNode(action.CreateClusterCertActionName)

	toMonolithic := dagPlans.NewPlanWithRootNode(PlanSetSharedPoolPDDeploymentModeToMonolithic)
	// Execution order: patch PDGroup mode -> delete microservice components ->
	// wait components gone -> commit final metadata.
	toMonolithic.
		SafeAddDependNode(action.CommitPDDeploymentModeActionName).
		SafeAddDependNode(action.WaitPDMicroserviceComponentsGoneName).
		SafeAddDependNode(action.DeletePDMicroserviceComponentsActionName).
		SafeAddDependNode(action.PatchPDDeploymentModeActionName)

	return dagPlans
}

func deleteSharedPoolDag(dagPlans *dag.Plans) *dag.Plans {
	// root node
	deleteSharedPool := dagPlans.NewPlanWithRootNode(PlanDeleteSharedPool)

	deleteSharedPool.
		// send metering metadata for shared pool deletion
		SafeAddDependNode(action.SendMeteringMetadataActionName).
		SafeAddDependNode(action.DeleteSharedPoolMetaName).
		// delete dataplane
		SafeAddDependNode(action.PruneDataplaneActionName).
		// delete internal bucket
		SafeAddDependNode(action.ApplyInternalBucketLifecycleRulesActionName).
		SafeAddDependNode(action.ClearDataPlaneActionName).
		// delete o11y agent
		SafeAddDependNode(action.DeleteO11ySharedPoolAgentActionName).
		// br
		SafeAddDependNode(braction.DeleteAllTiBRsActionName).
		SafeAddDependNode(braction.MarkGCSettingSPDeletedActionName). // mark gc setting sp deleted when all gc settings changes are applied
		SafeAddDependNode(braction.DeleteTiBRGCActionName).
		SafeAddDependNode(braction.EnableDeletingManualBackupLifecycleRulesActionName).
		// A deleted PAUSED pool retains pause_resume_state. Apply the normal
		// deletion expiration rules before TiBRGC is removed, bypassing Pause
		// protection so BR objects can still be physically cleaned up.
		SafeAddDependNode(braction.ApplyDeletingBRLifecycleRulesActionName).
		SafeAddDependNode(braction.UpdateGCSettingForSPDeletingActionName). // after sharedpool deleted, we should change gc settings
		SafeAddDependNode(braction.DrainArchiveBeforeDeleteActionName).
		SafeAddDependNode(braction.FlushWALActionName) // flush wal before destroy cluster
	return dagPlans
}

func pauseSharedPoolDag(dagPlans *dag.Plans) *dag.Plans {
	pause := dagPlans.NewPlanWithRootNode(PlanPauseSharedPool)
	pause.SafeAddDependNode(action.CompletePauseActionName).
		SafeAddDependNode(action.SendPausedMeteringMetadataActionName).
		SafeAddDependNode(action.WaitSuspendedActionName).
		SafeAddDependNode(action.ApplySuspendComputeActionName).
		SafeAddDependNode(braction.DisableManualBackupLifecycleRulesActionName).
		// The pool is already in PAUSING state here. Applying lifecycle rules at
		// this point preserves the configured retention values but disables BR
		// object expiration before compute (and TiBRGC) is suspended.
		SafeAddDependNode(braction.ApplyPausedBRLifecycleRulesActionName).
		SafeAddDependNode(braction.StrictFlushWALActionName).
		SafeAddDependNode(action.SendPausingMeteringMetadataActionName).
		SafeAddDependNode(action.PausePrecheckActionName)
	return dagPlans
}

func resumeSharedPoolDag(dagPlans *dag.Plans) *dag.Plans {
	resume := dagPlans.NewPlanWithRootNode(PlanResumeSharedPool)
	resume.SafeAddDependNode(action.CompleteResumeActionName).
		SafeAddDependNode(action.SendActiveMeteringMetadataActionName).
		SafeAddDependNode(braction.RestoreManualBackupLifecycleRulesActionName).
		SafeAddDependNode(braction.RestoreBRLifecycleRulesActionName).
		SafeAddDependNode(braction.TriggerArchiveAfterResumeActionName).
		SafeAddDependNode(braction.DrainArchiveBeforeResumeActionName).
		SafeAddDependNode(action.WaitAvailableActionName).
		SafeAddDependNode(action.ApplyResumeComputeActionName).
		SafeAddDependNode(action.SendResumingMeteringMetadataActionName)
	return dagPlans
}

func createTiDBClusterDag(dagPlans *dag.Plans) *dag.Plans {
	// root node
	createTiDBCluster := dagPlans.NewPlanWithRootNode(PlanCreateTiDBCluster)

	createTiDBCluster.
		// write metering metadata if all previous steps successed
		SafeAddDependNode(action.SendMeteringMetadataActionName).
		SafeAddDependNode(action.CreateClusterCertActionName).
		// create export config
		SafeAddDependNode(exportaction.InitExportConfigsInConfigCenterActionName).
		// add HPA after the tiproxy group exists
		SafeAddDependNode(action.ApplyComponentHpaActionName).
		// create tiproxy group
		SafeAddDependNode(action.ApplyTiProxyGroupActionName).
		// scale shared pool if needed
		SafeAddDependNode(action.UpdateComponentGroupActionName).
		SafeAddDependNode(action.CreateKeyspaceRoleActionName).
		SafeAddDependNode(action.SetKeySpaceActionName).
		// create cluster config
		SafeAddDependNode(action.CreateTiDBClusterMetaName).
		// wait dataplane ready
		SafeAddDependNode(action.PreCreateTiDBClusterActionName)

	return dagPlans
}

func updateTiDBClusterDag(dagPlans *dag.Plans) *dag.Plans {
	// root node
	updateTiDBCluster := dagPlans.NewPlanWithRootNode(PlanUpdateTiDBCluster)

	updateTiDBCluster.
		// DAG children execute before parents; write metering after the child chain succeeds.
		SafeAddDependNode(action.SendMeteringMetadataActionName).
		SafeAddDependNode(action.ApplyComponentHpaActionName).
		// scale shared pool if needed
		// scale tidb/tiproxy group if needed
		SafeAddDependNode(action.UpdateComponentGroupActionName).
		SafeAddDependNode(action.UpdateResourceGroupActionName)

	return dagPlans
}

func deleteTiDBClusterDag(dagPlans *dag.Plans) *dag.Plans {
	// root node
	deleteTiDBCluster := dagPlans.NewPlanWithRootNode(PlanDeleteTiDBCluster)

	deleteTiDBCluster.
		// DAG children execute before parents; write metering after the child chain succeeds.
		SafeAddDependNode(action.SendMeteringMetadataActionName).
		SafeAddDependNode(action.DeleteTiDBClusterMetaName).
		// clean up old resources
		SafeAddDependNode(action.CleanUpCertActionName).
		SafeAddDependNode(action.DeleteKeyspaceRoleActionName).
		SafeAddDependNode(action.DeleteKeySpaceActionName).
		SafeAddDependNode(action.DeletePlacementPolicyActionName).
		// to gen dataplane metadata
		SafeAddDependNode(action.ApplyTiProxyGroupActionName).
		SafeAddDependNode(action.ApplyCassandraProxyActionName).
		SafeAddDependNode(action.DeleteCassandraProxyLBActionName).
		SafeAddDependNode(action.DeleteCassandraProxyPLSActionName).
		// Run this lightweight k8s-only cleanup as the leaf so it is not gated on
		// subsequent dataplane/cert cleanup steps.
		SafeAddDependNode(action.DeleteO11yTiDBGroupAgentsByClusterActionName)

	return dagPlans
}

func createTiDBGroupDag(dagPlans *dag.Plans) *dag.Plans {
	// root node
	createTiDBGroup := dagPlans.NewPlanWithRootNode(PlanCreateTiDBGroup)

	// cluster resource
	createTiDBGroup.
		SafeAddDependNode(action.ApplyLoadBalanceActionName).
		SafeAddDependNode(action.SetKeySpaceActionName).
		// need new storage class
		SafeAddDependNode(action.CreateTiDBAuthTokenSecretActionName).
		// new tidb group with different id, need new cert
		SafeAddDependNode(action.CreateClusterCertActionName).
		// first create tidb group
		// to gen dataplane metadata
		SafeAddDependNode(action.ApplyComponentHpaActionName).
		// create the TiDBGroup Autoscaler after the target exists and before HPA handling
		SafeAddDependNode(action.ApplyAutoscalerActionName).
		// create tidb group
		SafeAddDependNode(action.ApplyTiDBGroupActionName).
		// create tidb group config
		SafeAddDependNode(action.CreateTiDBGroupConfigName).
		// create vac before apply any component group
		SafeAddDependNode(action.CreateVolumeAttributesClassActionName).
		// o11y tidbgroup agent
		SafeAddDependNode(action.CreateO11yTiDBGroupAgentActionName).
		// cluster meta
		SafeAddDependNode(action.PreCreateTiDBGroupActionName)

	return dagPlans
}

func updateTiDBGroupDag(dagPlans *dag.Plans) *dag.Plans {
	// root node
	updateTiDBGroup := dagPlans.NewPlanWithRootNode(PlanUpdateTiDBGroup)

	updateTiDBGroup.
		SafeAddDependNode(action.ApplyComponentHpaActionName).
		// only update tidb group
		SafeAddDependNode(action.UpdateComponentGroupActionName).
		// create lb before apply any component group
		SafeAddDependNode(action.ApplyLoadBalanceActionName)

	return dagPlans
}

func deleteTiDBGroupDag(dagPlans *dag.Plans) *dag.Plans {
	// root node
	deleteTiDBGroup := dagPlans.NewPlanWithRootNode(PlanDeleteTiDBGroup)

	deleteTiDBGroup.
		// clean up old resources
		SafeAddDependNode(action.CleanUpCertActionName).
		// cluster resource
		SafeAddDependNode(action.DeleteLoadBalanceActionName).
		// to gen dataplane metadata
		SafeAddDependNode(action.ApplyComponentHpaActionName).
		// DAG depend nodes are executed in reverse order, so this runs after ApplyTiDBGroup.
		SafeAddDependNode(action.DeleteTiDBGroupConfigName).
		SafeAddDependNode(action.ApplyTiDBGroupActionName)
	return dagPlans
}

func createLightweightRestoreDag(dagPlans *dag.Plans) *dag.Plans {
	createLightweightRestore := dagPlans.NewPlanWithRootNode(PlanCreateLightweightRestore)
	createLightweightRestore.
		SafeAddDependNode(braction.MarkLightWeightRestoreSucceededActionName).
		SafeAddDependNode(braction.RestartTiDBGroupsAfterRestoreActionName).
		SafeAddDependNode(braction.ExecuteKernelLightWeightRestoreActionName).
		SafeAddDependNode(braction.ScaleFoeLWRestoreActionName).           // do it as needed
		SafeAddDependNode(braction.GetPitrBackupInfoForRestoreActionName). // do it as needed
		SafeAddDependNode(braction.CreateTiBRRestoreActionName)
		// TODO: may need to scale tikv before restore
	return dagPlans
}

func deleteLightweightRestoreDag(dagPlans *dag.Plans) *dag.Plans {
	deleteLightweightRestore := dagPlans.NewPlanWithRootNode(PlanDeleteLightweightRestore)
	deleteLightweightRestore.
		SafeAddDependNode(braction.MarkLightWeightRestoreDeletedActionName).
		SafeAddDependNode(braction.DeleteTiBRRestoreActionName)
		// TODO: currently, kernel doesn't support delete/cancel non finished restore tasks and clean up data
		// SafeAddDependNode(action.DeleteKernelLightWeightRestoreTasksActionName)

	return dagPlans
}

func createLightweightXRestoreDag(dagPlans *dag.Plans) *dag.Plans {
	createLightweightXRestore := dagPlans.NewPlanWithRootNode(PlanCreateLightweightXRestore)
	createLightweightXRestore.
		SafeAddDependNode(braction.MarkLightWeightRestoreSucceededActionName).
		SafeAddDependNode(braction.RevokeXRestoreBucketAccessActionName). // do it as needed
		SafeAddDependNode(braction.RestartTiDBGroupsAfterRestoreActionName).
		SafeAddDependNode(braction.ExecuteKernelLightWeightRestorePackedActionName).
		SafeAddDependNode(braction.ExecuteKernelLightWeightPackBackupActionName).
		SafeAddDependNode(braction.ScaleFoeLWRestoreActionName).           // do it as needed
		SafeAddDependNode(braction.GetPitrBackupInfoForRestoreActionName). // do it as needed
		SafeAddDependNode(braction.CreateTiBRRestoreActionName).
		SafeAddDependNode(braction.GrantXRestoreBucketAccessActionName)
	return dagPlans
}

func deleteLightweightXRestoreDag(dagPlans *dag.Plans) *dag.Plans {
	deleteLightweightXRestore := dagPlans.NewPlanWithRootNode(PlanDeleteLightweightXRestore)
	deleteLightweightXRestore.
		SafeAddDependNode(braction.MarkLightWeightRestoreDeletedActionName).
		SafeAddDependNode(braction.RevokeXRestoreBucketAccessActionName). // do it as needed
		SafeAddDependNode(braction.DeleteTiBRRestoreActionName)
		// TODO: currently, kernel doesn't support delete/cancel non finished restore tasks and clean up data
		// SafeAddDependNode(action.DeleteKernelLightWeightRestoreTasksActionName).

	return dagPlans
}

func createClassicRestoreDag(dagPlans *dag.Plans) *dag.Plans {
	createClassicRestore := dagPlans.NewPlanWithRootNode(PlanCreateClassicRestore)
	createClassicRestore.
		SafeAddDependNode(braction.MarkClassicRestoreSucceededActionName).
		SafeAddDependNode(braction.RestartTiDBGroupsAfterClassicRestoreActionName).
		SafeAddDependNode(braction.DeleteCloudAdminAfterClassicRestoreActionName).
		SafeAddDependNode(braction.ExecuteClassicRestoreActionName).
		SafeAddDependNode(braction.ScaleForClassicRestoreActionName) // do it as needed
	return dagPlans
}

func deleteClassicRestoreDag(dagPlans *dag.Plans) *dag.Plans {
	deleteClassicRestore := dagPlans.NewPlanWithRootNode(PlanDeleteClassicRestore)
	deleteClassicRestore.
		SafeAddDependNode(braction.MarkClassicRestoreDeletedActionName).
		SafeAddDependNode(braction.DeleteClassicRestoreCRActionName)
	return dagPlans
}

func createManualBackupDag(dagPlans *dag.Plans) *dag.Plans {
	createManualBackup := dagPlans.NewPlanWithRootNode(PlanCreateManualBackup)
	createManualBackup.
		SafeAddDependNode(braction.MarkManualBackupSucceededActionName).
		SafeAddDependNode(braction.DeleteTiBRManualBackupActionName).
		//TODO: add backup ts info and size info
		SafeAddDependNode(braction.ExecuteKernelManualCopyBackupActionName).
		SafeAddDependNode(braction.ExecuteKernelPackManualBackupActionName).
		SafeAddDependNode(braction.ExecuteKernelManualBackupActionName).
		SafeAddDependNode(braction.CreateTiBRManualBackupActionName)
	return dagPlans
}

func deleteManualBackupDag(dagPlans *dag.Plans) *dag.Plans {
	deleteManualBackup := dagPlans.NewPlanWithRootNode(PlanDeleteManualBackup)
	deleteManualBackup.
		SafeAddDependNode(braction.MarkManualBackupDeletedActionName).
		SafeAddDependNode(braction.DeleteManualBackupDataActionName).
		SafeAddDependNode(braction.DeleteTiBRManualBackupActionName).
		SafeAddDependNode(braction.MarkManualBackupDeletingActionName)
	return dagPlans
}

func applySemConfigDag(dagPlans *dag.Plans) *dag.Plans {
	applySemConfig := dagPlans.NewPlanWithRootNode(PlanApplySemConfig)
	applySemConfig.
		SafeAddDependNode(action.RestartTiDBGroupsActionName).
		SafeAddDependNode(action.ApplyTiDBSemConfigActionName)
	return dagPlans
}

func setTiProxyExternalDNSDag(dagPlans *dag.Plans) *dag.Plans {
	setTiProxyExternalDNS := dagPlans.NewPlanWithRootNode(PlanSetTiProxyExternalDNS)
	setTiProxyExternalDNS.
		SafeAddDependNode(action.ApplyTiProxyExternalDNSActionName)
	return dagPlans
}

// createCassandraProxyDag drives the cassandra-proxy provisioning path.
// PrivateLink is unconditional, so there is a single create plan.
//
// Dependency chain. The DAG runtime executes leaf-first, so runtime order is:
//
//	ApplyCassandraProxy -> ApplyCassandraProxyLB -> ApplyCassandraProxyPLS -> WaitCassandraProxyReady -> SendMeteringMetadata
func createCassandraProxyDag(dagPlans *dag.Plans) *dag.Plans {
	createCassandraProxy := dagPlans.NewPlanWithRootNode(PlanCreateCassandraProxy)
	createCassandraProxy.
		SafeAddDependNode(action.SendMeteringMetadataActionName).
		SafeAddDependNode(action.WaitCassandraProxyReadyActionName).
		SafeAddDependNode(action.ApplyCassandraProxyPLSActionName).
		SafeAddDependNode(action.ApplyCassandraProxyLBActionName).
		SafeAddDependNode(action.ApplyCassandraProxyActionName)
	return dagPlans
}

// use for when user update backup gc setting
func applyBackupGcSettingDag(dagPlans *dag.Plans) *dag.Plans {
	applyBackupGcSetting := dagPlans.NewPlanWithRootNode(PlanUpdateBackupGcSetting)
	applyBackupGcSetting.
		SafeAddDependNode(braction.ApplyBucketLifecycleRulesActionName).
		SafeAddDependNode(braction.ApplyTiBRGCActionName).
		SafeAddDependNode(braction.ApplyTransitionSafeLifecycleRulesActionName)
	return dagPlans
}

func upgradeTiBRAutoBackupDag(dagPlans *dag.Plans) *dag.Plans {
	upgradeTiBRAutoBackup := dagPlans.NewPlanWithRootNode(PlanUpgradeTiBRAutoBackup)
	upgradeTiBRAutoBackup.
		SafeAddDependNode(braction.CreateTiBRAutoBackupActionName).
		SafeAddDependNode(braction.DeleteTiBRAutoBackupActionName).
		SafeAddDependNode(braction.ValidateTiBRAutoBackupPlacementActionName)
	return dagPlans
}

func upgradeTiBRGCDag(dagPlans *dag.Plans) *dag.Plans {
	upgradeTiBRGC := dagPlans.NewPlanWithRootNode(PlanUpgradeTiBRGC)
	upgradeTiBRGC.
		SafeAddDependNode(braction.ApplyTiBRGCActionName)
	return dagPlans
}

func applyDMClusterDag(dagPlans *dag.Plans) *dag.Plans {
	applyDMCluster := dagPlans.NewPlanWithRootNode(PlanApplyDMCluster)
	applyDMCluster.
		SafeAddDependNode(action.ApplyDMClusterActionName)
	return dagPlans
}

func createExportDag(dagPlans *dag.Plans) *dag.Plans {
	createExport := dagPlans.NewPlanWithRootNode(PlanCreateExport)
	createExport.
		SafeAddDependNode(exportaction.DeleteExportDumplingActionName).
		SafeAddDependNode(exportaction.PollExportDumplingActionName).
		SafeAddDependNode(exportaction.CreateExportDumplingActionName).
		SafeAddDependNode(exportaction.PollExportTiDBGroupActionName)
	return dagPlans
}

func cancelExportDag(dagPlans *dag.Plans) *dag.Plans {
	cancelExport := dagPlans.NewPlanWithRootNode(PlanCancelExport)
	cancelExport.
		SafeAddDependNode(exportaction.DeleteExportDumplingActionName)
	return dagPlans
}

func createNodeInstanceGroupDag(dagPlans *dag.Plans) *dag.Plans {
	createNodeInstanceGroup := dagPlans.NewPlanWithRootNode(PlanCreateNodeInstanceGroup)
	createNodeInstanceGroup.
		SafeAddDependNode(action.CreateNodeInstanceGroupActionName)
	return dagPlans
}

func reactivateNodeInstanceGroupDag(dagPlans *dag.Plans) *dag.Plans {
	reactivateNodeInstanceGroup := dagPlans.NewPlanWithRootNode(PlanReactivateNodeInstanceGroup)
	reactivateNodeInstanceGroup.
		SafeAddDependNode(action.ReactivateNodeInstanceGroupActionName)
	return dagPlans
}

func deprecateNodeInstanceGroupDag(dagPlans *dag.Plans) *dag.Plans {
	deprecateNodeInstanceGroup := dagPlans.NewPlanWithRootNode(PlanDeprecateNodeInstanceGroup)
	deprecateNodeInstanceGroup.
		SafeAddDependNode(action.DeprecateNodeInstanceGroupActionName)
	return dagPlans
}

func deleteNodeInstanceGroupDag(dagPlans *dag.Plans) *dag.Plans {
	deleteNodeInstanceGroup := dagPlans.NewPlanWithRootNode(PlanDeleteNodeInstanceGroup)
	deleteNodeInstanceGroup.
		SafeAddDependNode(action.DeleteNodeInstanceGroupActionName)
	return dagPlans
}

func setNodeInstanceGroupInstanceTypeStatusDag(dagPlans *dag.Plans) *dag.Plans {
	setNodeInstanceGroupInstanceTypeStatus := dagPlans.NewPlanWithRootNode(PlanSetNodeInstanceGroupInstanceTypeStatus)
	setNodeInstanceGroupInstanceTypeStatus.
		SafeAddDependNode(action.SetNodeInstanceGroupInstanceTypeStatusActionName)
	return dagPlans
}

func createManualBackupRestoreDag(dagPlans *dag.Plans) *dag.Plans {
	createManualBackupRestore := dagPlans.NewPlanWithRootNode(PlanCreateManualBackupRestore)
	createManualBackupRestore.
		SafeAddDependNode(braction.MarkManualRestoreSucceededActionName).
		SafeAddDependNode(braction.RestartTiDBGroupsAfterManualRestoreActionName).
		SafeAddDependNode(braction.ExecuteKernelRestoreFromManualBackupActionName).
		SafeAddDependNode(braction.ScaleForManualBackupRestoreActionName).
		SafeAddDependNode(braction.CreateTiBRManualRestoreActionName)
	return dagPlans
}

func createManualBackupXRestoreDag(dagPlans *dag.Plans) *dag.Plans {
	createManualBackupXRestore := dagPlans.NewPlanWithRootNode(PlanCreateManualBackupXRestore)
	createManualBackupXRestore.
		SafeAddDependNode(braction.MarkManualRestoreSucceededActionName).
		SafeAddDependNode(braction.RestartTiDBGroupsAfterManualRestoreActionName).
		SafeAddDependNode(braction.RevokeManualRestoreBucketAccessActionName).
		SafeAddDependNode(braction.ExecuteKernelRestoreFromManualBackupActionName).
		SafeAddDependNode(braction.ScaleForManualBackupRestoreActionName).
		SafeAddDependNode(braction.CreateTiBRManualRestoreActionName).
		SafeAddDependNode(braction.GrantManualRestoreBucketAccessActionName)
	return dagPlans
}

func deleteManualBackupRestoreDag(dagPlans *dag.Plans) *dag.Plans {
	deleteManualBackupRestore := dagPlans.NewPlanWithRootNode(PlanDeleteManualBackupRestore)
	deleteManualBackupRestore.
		SafeAddDependNode(braction.MarkManualRestoreDeletedActionName).
		SafeAddDependNode(braction.DeleteTiBRManualRestoreActionName)
	return dagPlans
}

func deleteManualBackupXRestoreDag(dagPlans *dag.Plans) *dag.Plans {
	deleteManualBackupXRestore := dagPlans.NewPlanWithRootNode(PlanDeleteManualBackupXRestore)
	deleteManualBackupXRestore.
		SafeAddDependNode(braction.MarkManualRestoreDeletedActionName).
		SafeAddDependNode(braction.RevokeManualRestoreBucketAccessActionName).
		SafeAddDependNode(braction.DeleteTiBRManualRestoreActionName)
	return dagPlans
}

func enableCoprocessorWorkerDag(dagPlans *dag.Plans) *dag.Plans {
	enableCoprocessorWorker := dagPlans.NewPlanWithRootNode(PlanEnableCoprocessorWorker)
	enableCoprocessorWorker.
		// send metering metadata after all steps succeed
		SafeAddDependNode(action.SendMeteringMetadataActionName).
		// wait TiKVGroup remote worker rollout to complete
		SafeAddDependNode(action.WaitTiKVGroupRemoteWorkerRolloutActionName).
		// patch TiKVGroup remoteWorkers to point to coprocessor worker
		SafeAddDependNode(action.UpdateComponentGroupActionName).
		// wait coprocessor worker to be ready (after HPA is applied, so the live
		// replica count HPA may have set is what we wait to converge on)
		SafeAddDependNode(action.WaitCoprocessorWorkerReadyActionName).
		// apply HPA/autoscale policy for coprocessor worker
		SafeAddDependNode(action.ApplyComponentHpaActionName).
		// apply coprocessor worker group
		SafeAddDependNode(action.ApplyComponentGroupActionName).
		// create VAC before applying component
		SafeAddDependNode(action.CreateVolumeAttributesClassActionName).
		// create cluster cert for coprocessor worker
		SafeAddDependNode(action.CreateClusterCertActionName)
	return dagPlans
}

func disableCoprocessorWorkerDag(dagPlans *dag.Plans) *dag.Plans {
	disableCoprocessorWorker := dagPlans.NewPlanWithRootNode(PlanDisableCoprocessorWorker)
	disableCoprocessorWorker.
		// send metering metadata after all steps succeed
		SafeAddDependNode(action.SendMeteringMetadataActionName).
		// clean up cert after coprocessor worker is gone
		SafeAddDependNode(action.CleanUpCertActionName).
		// wait coprocessor worker CR to be gone
		SafeAddDependNode(action.WaitCoprocessorWorkerGoneActionName).
		// apply coprocessor worker with DELETING phase
		SafeAddDependNode(action.ApplyComponentGroupActionName).
		// delete HPA/autoscale policy for coprocessor worker
		SafeAddDependNode(action.ApplyComponentHpaActionName).
		// wait TiKVGroup remote worker rollout to complete (pointing back to default worker)
		SafeAddDependNode(action.WaitTiKVGroupRemoteWorkerRolloutActionName).
		// patch TiKVGroup remoteWorkers to point back to default worker
		SafeAddDependNode(action.UpdateComponentGroupActionName).
		// wait default tikv worker group to be ready before repointing TiKVGroup back to it
		SafeAddDependNode(action.WaitDefaultTiKVWorkerReadyActionName)
	return dagPlans
}
