package registry

import (
	"fmt"

	opent "github.com/tidbcloud/devops/entity/operation"
	"github.com/tidbcloud/devops/pkg/service/operation/actions"
	"github.com/tidbcloud/devops/pkg/service/operation/actions/cdcaction"
	"github.com/tidbcloud/devops/pkg/service/operation/actions/clusteraction"
	"github.com/tidbcloud/devops/pkg/service/operation/actions/kernelimageaction"
	manualaction "github.com/tidbcloud/devops/pkg/service/operation/actions/maunalaction"
	"github.com/tidbcloud/devops/pkg/service/operation/actions/metaprofileaction"
	"github.com/tidbcloud/devops/pkg/service/operation/actions/mgmtportalaction"
	"github.com/tidbcloud/devops/pkg/service/operation/actions/ngoperation"
	"github.com/tidbcloud/devops/pkg/service/operation/actions/opsaction"
	"github.com/tidbcloud/devops/pkg/service/operation/actions/projectaction"
	"github.com/tidbcloud/devops/pkg/service/operation/actions/regionaction"
	"github.com/tidbcloud/devops/pkg/service/operation/actions/serverlessaction"
	"github.com/tidbcloud/devops/pkg/service/operation/actions/tcaction"
	"github.com/tidbcloud/devops/pkg/service/operation/actions/tenantaction"
)

type Registry map[opent.ActionName]actions.Action

func (r Registry) Get(name opent.ActionName) (actions.Action, bool) {
	action, exist := r[name]
	return action, exist
}

func (r Registry) GetAsAuto(name opent.ActionName) (actions.AutoAction, bool) {
	action, exist := r[name]
	if !exist {
		return nil, false
	}

	autoAction, ok := action.(actions.AutoAction)
	return autoAction, ok
}

func (r Registry) GetAsManual(name opent.ActionName) (actions.MaunalAction, bool) {
	action, exist := r[name]
	if !exist {
		return nil, false
	}

	manualAction, ok := action.(actions.MaunalAction)
	return manualAction, ok
}

func (r Registry) Add(action actions.Action) error {
	name := action.Metadata().Name

	if _, exist := r[name]; exist {
		return fmt.Errorf("action %s already exists", name)
	}

	r[name] = action
	return nil
}

func NewDefaultRegistry(comps *actions.Components) (Registry, error) {
	r := Registry{}

	autoFactories := []func(*actions.Components) actions.AutoAction{
		actions.NewAlwaysSucceed,
		actions.NewAlwaysFail,
		// clusteraction
		clusteraction.NewPauseCluster,
		clusteraction.NewResumeCluster,
		clusteraction.NewScaleCluster,
		clusteraction.NewCancelScaleCluster,
		clusteraction.NewRestart,
		clusteraction.NewModifyClusterVolume,
		clusteraction.NewScaleClusterPdComponent,
		clusteraction.NewScaleClusterTiProxyComponent,
		clusteraction.NewResetDMJob,
		clusteraction.NewCreateMigrationCluster,
		clusteraction.NewMigrateClusterPrivateLink,
		clusteraction.NewApplyHeterogeneousDeploy,
		clusteraction.NewDeleteHeterogeneousDeploy,
		clusteraction.NewCreateDataSynchronization,
		// tenantaction
		tenantaction.NewAddImpersonate,
		tenantaction.NewDelImpersonate,
		tenantaction.NewUpdateTenantQuota,
		tenantaction.NewUpdateRcuQuota,
		tenantaction.NewAddUserToProject,
		tenantaction.NewAddUserToTenant,
		tenantaction.NewDeleteProject,
		tenantaction.NewDeleteTenant,
		tenantaction.NewDeleteUser,
		tenantaction.NewRemoveUserFromProject,
		tenantaction.NewRemoveUserFromTenant,
		tenantaction.NewUpdateTenant,
		tenantaction.NewCreateCredits,
		tenantaction.NewBillsRecalculate,
		tenantaction.NewCreateReservedCapacityTemplate,
		tenantaction.NewUpdateReservedCapacityTemplateStatus,
		tenantaction.NewDeleteReservedCapacityTemplate,
		// tc action
		tcaction.NewPauseTC,
		tcaction.NewResumeTC,
		tcaction.NewDeleteASTSAndResumeTC,
		tcaction.NewScaleTC,
		tcaction.NewCreateOperation,
		tcaction.NewStartZonalShift,
		tcaction.NewOfflineAZ,
		tcaction.NewRecoverAZ,
		tcaction.NewStopZonalShift,
		// region action
		regionaction.NewDeployNewRegion,
		regionaction.NewDeployNewBYOCRegion,
		regionaction.NewDeployNewNextGenRegion,
		regionaction.NewUpgradeControlPlane,
		regionaction.NewUpgradeAdminNodes,
		regionaction.NewUpgradeO11yK8sNodes,
		regionaction.NewUpgradeO11yTiDBNodes,
		regionaction.NewUpgradeCDCNodes,
		regionaction.NewUpdateRegionOpsAuditLogSetting,
		regionaction.NewRegisterRegion,
		regionaction.NewUpdateRegion,
		// project action
		// ops action
		opsaction.NewUpdateDefaultVersion,
		opsaction.NewApplyConfigChangeViaConfigCenter,
		opsaction.NewSyncGitopsVersions,
		opsaction.NewUpdateDefaultTemplateBinding,
		opsaction.NewDeleteDefaultTemplateBinding,
		// mgmt portal action
		mgmtportalaction.NewPatchSku,
		mgmtportalaction.NewPatchSpu,
		// serverless action
		serverlessaction.NewUpdateClusterResourceConfig,
		// serverless ops action (ops-service via serverless-service gRPC)
		serverlessaction.NewRestartTiDBPods,
		// cdc action
		cdcaction.NewRestartCDC,
		cdcaction.NewRestartChangefeed,
		cdcaction.NewTiCDCDeletePodsAndResumeReconcile,
		cdcaction.NewTiCDCPauseReconcile,
		cdcaction.NewPauseChangefeed,
		cdcaction.NewResumeChangefeed,
		// nextgen action
		ngoperation.NewScaleScaleResourcePool,
		ngoperation.NewModifyResourcePoolVolume,
		ngoperation.NewRestartNextGenClusterComponent,
		ngoperation.NewRestartNextGenResourcePoolComponent,
		ngoperation.NewReplaceNodeForNextGenResourcePool,
		ngoperation.NewScaleNextGenTiDBGroup,
		ngoperation.NewScaleNextGenTiProxy,
		ngoperation.NewAddCassandraLabel,
		ngoperation.NewSetCassandra,
		ngoperation.NewManageTiProxyExternalDNS,
		ngoperation.NewCreateResourcePool,
		ngoperation.NewCreateTiKVGroup,
		ngoperation.NewDeleteTiKVGroup,
		ngoperation.NewSetDefaultResourcePool,
		ngoperation.NewDeleteResourcePool,
		ngoperation.NewUpdateResourcePoolInstanceLimit,
		ngoperation.NewPauseNextGenResourcePool,
		ngoperation.NewResumeNextGenResourcePool,
		ngoperation.NewOverrideFlexibleBackupRetention,
		ngoperation.NewModifyNgComponentImage,
		ngoperation.NewCreatePlacementPolicy,
		ngoperation.NewUpdatePlacementPolicy,
		ngoperation.NewDeletePlacementPolicy,
		ngoperation.NewCreateChangefeedVPCPeering,
		ngoperation.NewDeleteChangefeedVPCPeering,
		ngoperation.NewScaleNextGenTiDBGroupQuota,
		ngoperation.NewScaleNextGenTiProxyQuota,
		ngoperation.NewScaleResourcePoolQuota,
		ngoperation.NewSetHPAScaleBehavior,
		ngoperation.NewSetSharedPoolPDDeploymentMode,
		ngoperation.NewSetSharedPoolTiKVRemoteWorkers,
		// kernel image action
		kernelimageaction.NewResolveKernelImageDigest,
		kernelimageaction.NewDeliverKernelImage,
		kernelimageaction.NewSyncBYOCImage,
		// project action
		projectaction.NewAddSecondaryCidr,
		// meta profile action
		metaprofileaction.NewCreateMetaProfile,
		// traffic replay action
		clusteraction.NewCreateTrafficReplay,
		clusteraction.NewUpdateTrafficReplay,
		clusteraction.NewStartTrafficReplay,
		clusteraction.NewStopTrafficReplay,
		clusteraction.NewDeleteTrafficReplay,
	}

	maunalFactories := []func(*actions.Components) actions.MaunalAction{
		manualaction.NewNotifyOncaller,
		manualaction.NewPerformOperation,
	}

	for _, f := range autoFactories {
		action := f(comps)

		if action.Metadata().Name == "" {
			return nil, fmt.Errorf("action %T has no name", action)
		}
		if action.Metadata().Type != opent.ActionTypeAuto {
			return nil, fmt.Errorf("action %s has wrong type %s", action.Metadata().Name, action.Metadata().Type)
		}

		err := r.Add(action)
		if err != nil {
			return nil, err
		}
	}

	for _, f := range maunalFactories {
		action := f(comps)

		if action.Metadata().Name == "" {
			return nil, fmt.Errorf("action %T has no name", action)
		}
		if action.Metadata().Type != opent.ActionTypeManual {
			return nil, fmt.Errorf("action %s has wrong type %s", action.Metadata().Name, action.Metadata().Type)
		}

		err := r.Add(action)
		if err != nil {
			return nil, err
		}
	}

	return r, nil
}
