package workflow

import (
	"time"

	"github.com/tidbcloud/fastflow"
	"github.com/tidbcloud/fastflow/pkg/entity/run"
	"github.com/tidbcloud/pkgv2/db"

	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/account"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/clusterconfig"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/clusterproxy"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/eventbus"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/regionserver"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/adapter/remoteencryptor"
	auditlogrepo "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/auditlog"
	cidrrepo "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/cidr"
	clusterrepo "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/cluster"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/cmek"
	endpointrepo "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/endpoint"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/lock"
	privatelinkservicemigrationrepo "github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/privatelinkservicemigration"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/region"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/repository/tidbgroup"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/service/auditlog"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/service/workflow/action"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/domain/service/workflow/dag"
	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/pkg/config"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

// Global action instances - each action is created exactly once
type actionInstances struct {
	generationConfiguration                           run.Action
	ensureVPC                                         run.Action
	createCluster                                     run.Action
	pollUntilSynced                                   run.Action
	createLoadBalancer                                run.Action
	createPrivateLinkService                          run.Action
	setGlobalClusterStatus                            run.Action
	execExtraSetUpSql                                 run.Action
	pauseCluster                                      run.Action
	waitScheduledTime                                 run.Action
	resumeCluster                                     run.Action
	deletePrivateLinkService                          run.Action
	deleteTidbGroup                                   run.Action
	deleteCluster                                     run.Action
	deleteClusterMeta                                 run.Action
	deleteGKEHeterogeneousBeforeDeleteCluster         run.Action
	applyCluster                                      run.Action
	createTidbGroup                                   run.Action
	updatePublicLb                                    run.Action
	createPrivateLinkConnection                       run.Action
	deletePrivateLinkConnection                       run.Action
	createVpcPeering                                  run.Action
	acceptVpcPeering                                  run.Action
	deleteVpcPeering                                  run.Action
	addSecondaryCidr                                  run.Action
	createAuditLogFilterRule                          run.Action
	deleteAuditLogFilterRule                          run.Action
	replaceAuditLogFilterRule                         run.Action
	updateAuditLogConfig                              run.Action
	deleteTidbGroupPrivateLinkService                 run.Action
	disableTidbGroupDNS                               run.Action
	pollUntilTidbGroupAvailable                       run.Action
	setTidbGroupDNSAndEnable                          run.Action
	cutoverPrivateLinkService                         run.Action
	createTidbGroupPrivateLinkService                 run.Action
	pollPrivateLinkServiceMigrationAvailable          run.Action
	cutoverEndpointMeta                               run.Action
	pollPrivateLinkServiceMigrationStatus             run.Action
	createPrivateLinkServiceWithExtraTidbGroup        run.Action
	scaleComponentActionWithConfigOrigin              run.Action
	scaleComponentActionWithConfigFollower            run.Action
	enableGKEPolicyControl                            run.Action
	pollUntilGKEPolicyControlEnabled                  run.Action
	applyGKEHeterogeneousDeployResources              run.Action
	pollUntilGKEHeterogeneousDeployReadyForGatekeeper run.Action
	createGKEHeterogeneousGatekeeperAssign            run.Action
	deleteGKEHeterogeneousGatekeeperAssign            run.Action
	deleteGKEHeterogeneousDeploy                      run.Action
	deleteGKEHeterogeneousDeployBeforeScale           run.Action
	PauseClusterTc                                    run.Action
	PollUntilClusterTcResumed                         run.Action
	DeleteAstsAndResumeClusterTc                      run.Action
	PollUntilClusterTcPaused                          run.Action
	GenerateConfigAndApplyToConfigCenter              run.Action
	setPrivateLinkGroupAddressType                    run.Action
}

func RegisterDags(
	flow workflow.Workflow,
	componentRepo clusterrepo.ComponentRepo,
	clusterRepo clusterrepo.Repo,
	cidrRepo cidrrepo.Repo,
	regionRepo region.Repo,
	tidbGroupRepo tidbgroup.Repo,
	trafficFilterRepo endpointrepo.TrafficFilterRepo,
	lockRepo lock.Repo,
	cmekRepo cmek.Repo,
	vpcPeeringRepo endpointrepo.VpcPeeringRepo,
	privateConnRepo endpointrepo.PrivateLinkEndpointRepo,
	pausePlanRepo clusterrepo.PausePlanRepo,
	auditLogFilterRuleRepo auditlogrepo.FilterRuleRepo,
	auditLogConfigRepo auditlogrepo.ConfigRepo,
	privatelinkservicemigrationRepo privatelinkservicemigrationrepo.Repo,
	auditLogSvc auditlog.Service,
	regionalCli regionserver.Client,
	clusterConfigCli clusterconfig.Client,
	accountCli account.Client,
	clusterProxyProvider clusterproxy.Provider,
	encryptor remoteencryptor.Client,
	committer eventbus.WALCommitter,
	transaction db.Transactioner,
	cfg *config.Config,
) error {
	insufficientResourceTimeout, err := time.ParseDuration(cfg.BizConfig.InsufficientResourceTimeout)
	if err != nil {
		return err
	}

	// Initialize all action instances exactly once
	actions := &actionInstances{
		setPrivateLinkGroupAddressType: action.NewSetPrivateLinkGroupAddressTypeAction(
			clusterRepo,
			regionRepo,
			tidbGroupRepo,
			regionalCli,
		),
		generationConfiguration: action.NewGenerationConfigurationAction(clusterRepo, clusterConfigCli),
		ensureVPC:               action.NewEnsureVPCAction(clusterRepo, regionRepo, cidrRepo, lockRepo, regionalCli, transaction),
		createCluster: action.NewCreateClusterAction(
			clusterRepo,
			regionRepo,
			tidbGroupRepo,
			cidrRepo,
			cmekRepo,
			clusterConfigCli,
			regionalCli,
			accountCli,
			committer,
			transaction,
		),
		pollUntilSynced:          action.NewPollUntilSyncedAction(regionalCli, clusterRepo, regionRepo, committer, insufficientResourceTimeout),
		createLoadBalancer:       action.NewCreateLoadBalancerAction(clusterRepo, tidbGroupRepo, regionRepo, regionalCli),
		createPrivateLinkService: action.NewCreatePrivateLinkServiceAction(clusterRepo, tidbGroupRepo, regionRepo, regionalCli),
		setGlobalClusterStatus:   action.NewSetGlobalClusterStatusAction(clusterRepo, committer, transaction),
		execExtraSetUpSql: action.NewExecExtraSetUpSqlAction(
			clusterRepo,
			regionRepo,
			accountCli,
			clusterProxyProvider,
			encryptor,
			regionalCli,
			transaction,
		),
		pauseCluster:             action.NewPauseClusterAction(regionalCli, clusterRepo, regionRepo, committer),
		waitScheduledTime:        action.NewWaitScheduledTimeAction(),
		resumeCluster:            action.NewResumeClusterAction(regionalCli, clusterRepo, regionRepo, pausePlanRepo, committer, transaction),
		deletePrivateLinkService: action.NewDeletePrivateLinkServiceAction(clusterRepo, regionRepo, regionalCli),
		deleteTidbGroup:          action.NewDeleteTidbGroupAction(clusterRepo, regionRepo, regionalCli, committer),
		deleteCluster:            action.NewDeleteClusterAction(regionalCli, clusterRepo, regionRepo, committer, transaction),
		deleteClusterMeta:        action.NewDeleteClusterMetaAction(clusterRepo, clusterConfigCli),
		deleteGKEHeterogeneousBeforeDeleteCluster: action.NewDeleteGKEHeterogeneousBeforeDeleteClusterAction(
			regionalCli,
			clusterRepo,
			regionRepo,
			tidbGroupRepo,
		),
		applyCluster:                action.NewApplyClusterAction(regionalCli, clusterRepo, regionRepo, clusterConfigCli),
		createTidbGroup:             action.NewCreateTidbGroupAction(clusterRepo, tidbGroupRepo, regionRepo, trafficFilterRepo, regionalCli),
		updatePublicLb:              action.NewUpdatePublicLbAction(clusterRepo, tidbGroupRepo, regionRepo, trafficFilterRepo, regionalCli),
		createPrivateLinkConnection: action.NewCreatePrivateLinkConnectionAction(clusterRepo, privateConnRepo, regionRepo, regionalCli),
		deletePrivateLinkConnection: action.NewDeletePrivateLinkConnectionAction(clusterRepo, tidbGroupRepo, regionRepo, regionalCli),
		createVpcPeering:            action.NewCreateVpcPeeringAction(regionRepo, vpcPeeringRepo, regionalCli),
		acceptVpcPeering:            action.NewAcceptVpcPeeringAction(regionRepo, vpcPeeringRepo, regionalCli),
		deleteVpcPeering:            action.NewDeleteVpcPeeringAction(regionRepo, regionalCli),
		addSecondaryCidr:            action.NewAddSecondaryCidrAction(regionRepo, cidrRepo, regionalCli),
		createAuditLogFilterRule:    action.NewCreateAuditLogFilterRuleAction(clusterRepo, regionRepo, auditLogFilterRuleRepo, auditLogSvc),
		deleteAuditLogFilterRule:    action.NewDeleteAuditLogFilterRuleAction(clusterRepo, regionRepo, auditLogSvc),
		replaceAuditLogFilterRule:   action.NewReplaceAuditLogFilterRuleAction(clusterRepo, regionRepo, auditLogFilterRuleRepo, auditLogSvc),
		updateAuditLogConfig: action.NewUpdateAuditLogConfigAction(
			clusterRepo,
			regionRepo,
			auditLogConfigRepo,
			regionalCli,
			auditLogSvc,
		),
		deleteTidbGroupPrivateLinkService: action.NewDeleteTidbGroupPrivateLinkServiceAction(
			clusterRepo,
			privatelinkservicemigrationRepo,
			tidbGroupRepo,
			regionRepo,
			regionalCli,
		),
		disableTidbGroupDNS: action.NewDisableTidbGroupDNSAction(
			clusterRepo,
			privatelinkservicemigrationRepo,
			regionRepo,
			tidbGroupRepo,
			regionalCli,
		),
		pollUntilTidbGroupAvailable: action.NewPollUntilTidbGroupAvailableAction(
			regionalCli,
			clusterRepo,
			tidbGroupRepo,
			privatelinkservicemigrationRepo,
			regionRepo,
		),
		setTidbGroupDNSAndEnable: action.NewSetTidbGroupDNSAndEnableAction(
			clusterRepo,
			tidbGroupRepo,
			trafficFilterRepo,
			privatelinkservicemigrationRepo,
			regionRepo,
			regionalCli,
		),
		cutoverPrivateLinkService: action.NewCutoverPrivateLinkServiceAction(
			clusterRepo,
			tidbGroupRepo,
			privatelinkservicemigrationRepo,
			regionRepo,
			regionalCli,
		),
		createTidbGroupPrivateLinkService: action.NewCreateTidbGroupPrivateLinkServiceAction(
			clusterRepo,
			privatelinkservicemigrationRepo,
			regionRepo,
			regionalCli,
		),
		pollPrivateLinkServiceMigrationAvailable: action.NewPollPrivateLinkServiceMigrationAvailableAction(
			clusterRepo,
			tidbGroupRepo,
			privatelinkservicemigrationRepo,
			regionRepo,
			regionalCli,
		),
		cutoverEndpointMeta:                   action.NewCutoverEndpointMetaAction(clusterRepo, tidbGroupRepo, privateConnRepo, regionRepo),
		pollPrivateLinkServiceMigrationStatus: action.NewPollPrivateLinkServiceMigrationStatusAction(privatelinkservicemigrationRepo),
		createPrivateLinkServiceWithExtraTidbGroup: action.NewCreatePrivateLinkServiceWithExtraTidbGroupAction(
			clusterRepo,
			tidbGroupRepo,
			regionRepo,
			componentRepo,
			regionalCli,
		),
		scaleComponentActionWithConfigOrigin: action.NewScaleComponentActionWithConfigOrigin(
			tidbGroupRepo,
			regionRepo,
			clusterRepo,
			componentRepo,
			regionalCli,
			clusterConfigCli,
		),
		scaleComponentActionWithConfigFollower: action.NewScaleComponentActionWithConfigFollower(
			tidbGroupRepo,
			regionRepo,
			clusterRepo,
			componentRepo,
			regionalCli,
			clusterConfigCli,
		),
		enableGKEPolicyControl: action.NewEnableGKEPolicyControlAction(
			clusterRepo,
			regionRepo,
			regionalCli,
		),
		pollUntilGKEPolicyControlEnabled: action.NewPollUntilGKEPolicyControlEnabledAction(
			clusterRepo,
			regionRepo,
			regionalCli,
		),
		applyGKEHeterogeneousDeployResources: action.NewApplyGKEHeterogeneousDeployResourcesAction(
			clusterRepo,
			regionRepo,
			regionalCli,
		),
		pollUntilGKEHeterogeneousDeployReadyForGatekeeper: action.NewPollUntilGKEHeterogeneousDeployReadyForGatekeeperAction(
			clusterRepo,
			regionRepo,
			tidbGroupRepo,
			regionalCli,
		),
		createGKEHeterogeneousGatekeeperAssign: action.NewCreateGKEHeterogeneousGatekeeperAssignAction(
			clusterRepo,
			regionRepo,
			tidbGroupRepo,
			regionalCli,
		),
		deleteGKEHeterogeneousGatekeeperAssign: action.NewDeleteGKEHeterogeneousGatekeeperAssignAction(
			clusterRepo,
			regionRepo,
			regionalCli,
		),
		deleteGKEHeterogeneousDeploy: action.NewDeleteGKEHeterogeneousDeployAction(
			clusterRepo,
			regionRepo,
			regionalCli,
		),
		deleteGKEHeterogeneousDeployBeforeScale: action.NewDeleteGKEHeterogeneousDeployBeforeScaleAction(
			clusterRepo,
			componentRepo,
			regionRepo,
			regionalCli,
		),
		PauseClusterTc:                       action.NewPauseClusterTcAction(clusterRepo, regionRepo, regionalCli, committer),
		PollUntilClusterTcResumed:            action.NewPollUntilClusterTcResumedAction(clusterRepo, regionRepo, regionalCli, committer),
		DeleteAstsAndResumeClusterTc:         action.NewDeleteAstsAndResumeClusterTcAction(clusterRepo, regionRepo, regionalCli, committer),
		PollUntilClusterTcPaused:             action.NewPollUntilClusterTcPausedAction(clusterRepo, regionRepo, regionalCli, committer),
		GenerateConfigAndApplyToConfigCenter: action.NewGenerateConfigAndApplyToConfigCenterAction(clusterConfigCli, clusterRepo),
	}

	// Build action slices for each DAG by referencing the shared instances
	createClusterActions := []run.Action{
		actions.generationConfiguration,
		actions.ensureVPC,
		actions.createCluster,
		actions.pollUntilSynced,
		actions.createLoadBalancer,
		actions.createPrivateLinkService,
		actions.setGlobalClusterStatus,
		actions.execExtraSetUpSql,
	}

	pauseClusterActions := []run.Action{
		actions.pauseCluster,
		actions.pollUntilSynced,
		actions.setGlobalClusterStatus,
	}

	resumeClusterActions := []run.Action{
		actions.waitScheduledTime,
		actions.resumeCluster,
		actions.pollUntilSynced,
		actions.setGlobalClusterStatus,
	}

	deleteClusterActions := []run.Action{
		actions.deletePrivateLinkService,
		actions.deleteTidbGroup,
		actions.deleteGKEHeterogeneousBeforeDeleteCluster,
		actions.deleteCluster,
		actions.deleteClusterMeta,
	}

	applyClusterActions := []run.Action{
		actions.applyCluster,
		actions.pollUntilSynced,
		actions.setGlobalClusterStatus,
	}

	createTidbGroupActions := []run.Action{
		actions.createTidbGroup,
	}

	deleteTidbGroupActions := []run.Action{
		actions.deleteTidbGroup,
		actions.deletePrivateLinkService,
	}

	updatePublicLbActions := []run.Action{
		actions.updatePublicLb,
	}

	disableTidbGroupDNSActions := []run.Action{
		actions.disableTidbGroupDNS,
	}

	setTidbGroupDNSAndEnableActions := []run.Action{
		actions.setTidbGroupDNSAndEnable,
	}

	deletePrivateLinkServiceActions := []run.Action{
		actions.deletePrivateLinkService,
	}

	createPrivateLinkConnectionActions := []run.Action{
		actions.createPrivateLinkConnection,
	}

	deletePrivateLinkConnectionActions := []run.Action{
		actions.deletePrivateLinkConnection,
	}

	createVpcPeeringActions := []run.Action{
		actions.createVpcPeering,
		actions.acceptVpcPeering,
	}

	deleteVpcPeeringActions := []run.Action{
		actions.deleteVpcPeering,
	}

	addSecondaryCidrActions := []run.Action{
		actions.addSecondaryCidr,
	}

	createAuditLogFilterRuleActions := []run.Action{
		actions.createAuditLogFilterRule,
	}

	deleteAuditLogFilterRuleActions := []run.Action{
		actions.deleteAuditLogFilterRule,
	}

	replaceAuditLogFilterRuleActions := []run.Action{
		actions.replaceAuditLogFilterRule,
	}

	updateAuditLogConfigActions := []run.Action{
		actions.updateAuditLogConfig,
	}

	cutoverPrivateLinkServiceActions := []run.Action{
		actions.deleteTidbGroupPrivateLinkService,
		actions.disableTidbGroupDNS,
		actions.pollUntilTidbGroupAvailable,
		actions.setTidbGroupDNSAndEnable,
		actions.cutoverPrivateLinkService,
		actions.createTidbGroupPrivateLinkService,
		actions.pollPrivateLinkServiceMigrationAvailable,
		actions.cutoverEndpointMeta,
	}

	pollPrivateLinkServiceMigrationStatusActions := []run.Action{
		actions.pollPrivateLinkServiceMigrationStatus,
	}

	shutdownUpgradeClusterActions := []run.Action{
		actions.PauseClusterTc,
		actions.PollUntilClusterTcResumed,
		actions.DeleteAstsAndResumeClusterTc,
		actions.PollUntilClusterTcPaused,
		actions.GenerateConfigAndApplyToConfigCenter,
		actions.applyCluster,
		actions.pollUntilSynced,
		actions.setGlobalClusterStatus,
	}

	gkeHeterogeneousActions := []run.Action{
		actions.enableGKEPolicyControl,
		actions.pollUntilGKEPolicyControlEnabled,
		actions.applyGKEHeterogeneousDeployResources,
		actions.pollUntilGKEHeterogeneousDeployReadyForGatekeeper,
		actions.createGKEHeterogeneousGatekeeperAssign,
		actions.deleteGKEHeterogeneousGatekeeperAssign,
		actions.deleteGKEHeterogeneousDeploy,
	}

	// Register DAGs with shared action instances
	if err := flow.RegisterDag(dag.CreateClusterDag(), createClusterActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.PauseClusterDag(), pauseClusterActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.ResumeClusterDag(), resumeClusterActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.DeleteClusterDag(), deleteClusterActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.ApplyClusterDag(), applyClusterActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.CreateTidbGroupDag(), createTidbGroupActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.DeleteTidbGroupDag(), deleteTidbGroupActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.UpdatePublicLbDag(), updatePublicLbActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.DisableTidbGroupDNSDag(), disableTidbGroupDNSActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.SetTidbGroupDNSAndEnableDag(), setTidbGroupDNSAndEnableActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.DeletePrivateLinkServiceDag(), deletePrivateLinkServiceActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.CreatePrivateLinkConnectionDag(), createPrivateLinkConnectionActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.DeletePrivateLinkConnectionDag(), deletePrivateLinkConnectionActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.CreateVpcPeeringDag(), createVpcPeeringActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.DeleteVpcPeeringDag(), deleteVpcPeeringActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.AddSecondaryCidrDag(), addSecondaryCidrActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.CreateAuditLogFilterRuleDag(), createAuditLogFilterRuleActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.DeleteAuditLogFilterRuleDag(), deleteAuditLogFilterRuleActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.ReplaceAuditLogFilterRuleDag(), replaceAuditLogFilterRuleActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.UpdateAuditLogConfigDag(), updateAuditLogConfigActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.CutoverPrivateLinkServiceDag(), cutoverPrivateLinkServiceActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.PollPrivateLinkServiceMigrationStatusDag(), pollPrivateLinkServiceMigrationStatusActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.ShutdownUpgradeClusterDag(), shutdownUpgradeClusterActions); err != nil {
		return err
	}

	if err := flow.RegisterDag(dag.SetPrivateLinkGroupAddressTypeDag(), []run.Action{
		actions.setPrivateLinkGroupAddressType,
	}); err != nil {
		return err
	}

	// GKE heterogeneous deploy DAGs are created dynamically via RegisterDynamicDagThenRun.
	// Actions must be registered with fastflow so the dynamic DAG builder can look them up.
	fastflow.RegisterAction(gkeHeterogeneousActions)

	// Actions required by dynamic scaling dags.
	fastflow.RegisterAction(
		[]run.Action{
			actions.createPrivateLinkServiceWithExtraTidbGroup,
			actions.scaleComponentActionWithConfigOrigin,
			actions.scaleComponentActionWithConfigFollower,
			actions.deleteGKEHeterogeneousDeployBeforeScale,
		},
	)

	return nil
}
