package appbox

import (
	pkgconfig "github.com/tidbcloud/infra-provider/pkg/config"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/appbox/task"
	akstask "github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/appbox/task/aks"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/appbox/task/common"
	ekstask "github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/appbox/task/eks"
	gketask "github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/appbox/task/gke"
)

func GetAPPBoxTasks(r *APPBoxReconciler, c *task.Context) (tasks []deploy.Task) {
	switch c.Config.Provider {
	case consts.ProviderTypeEKS:
		return GetEKSAPPBoxTasks(r, c)
	case consts.ProviderTypeGKE:
		return GetGKEAPPBoxTasks(r, c)
	case consts.ProviderTypeAKS:
		return GetAKSAPPBoxTasks(r, c)
	}
	return nil
}

func GetEKSAPPBoxTasks(r *APPBoxReconciler, c *task.Context) (tasks []deploy.Task) {
	tasks = []deploy.Task{
		common.NewGuardian(c),
		ekstask.NewEKS(c, r.AWSCli),
		common.NewNodePoolGC(c),
		ekstask.NewNodeRole(c),
		ekstask.NewTiCDCNodeRole(c),
		ekstask.NewNodegroup(c),
		ekstask.NewOIDC(c),
		// infra components
		ekstask.NewAddons(c),
		common.NewNamespace(c),
		ekstask.NewClusterAutoscaler(c),
		common.NewNodeExporter(c),
		common.NewNodeLocalDNS(c),
		common.NewKubeStateMetrics(c),
		common.NewEventExporter(c),
		common.NewCertManager(c),
		common.NewPCAIssuer(c), // PCAIssuer relies on cert-manager to succeed
		common.NewSelfSignedCAClusterIssuer(c),
		// common.NewTiDBServerSelfSignedClusterIssuer(c),
		ekstask.NewAWSLBController(c), // AWS LB Controller relies on cert-manager crds.
		// dedicated components
	}
	tasks = append(tasks, getTiDBOperatorTasks(c)...)
	tasks = append(tasks,
		common.NewClusterGateway(c),
		common.NewClusterRole(c),
		common.NewClusterProxy(c),
		ekstask.NewSLIReporter(c),
	)
	if c.APPBox.GetAnnotations()[v1alpha1.AnnotationKeyDisableTiflowOperator] != "true" {
		tasks = append(tasks, common.NewTiFlowOperator(c))
	}
	tasks = append(tasks,
		// data platform components
		common.NewDataflowAgent(c),
		common.NewO11y(c),
		ekstask.NewRBAC(c),
	)
	return tasks
}

func GetGKEAPPBoxTasks(r *APPBoxReconciler, c *task.Context) (tasks []deploy.Task) {
	tasks = []deploy.Task{
		common.NewGuardian(c),
		gketask.NewGCPServiceAccount(c),
		gketask.NewGKE(c, r.GCPCli),
		common.NewNodePoolGC(c),
		gketask.NewProjectPolicy(c, r.GCPCli),
		gketask.NewNodePool(c, r.GCPCli),
		gketask.NewOIDC(c),
		gketask.NewGCRBucketPolicy(c),
	}
	if c.Config.APPBoxConfig.EnableArtifactRegistry {
		tasks = append(tasks, gketask.NewArtifactRegistryPolicy(c, r.GCPCli))
	}
	tasks = append(tasks,
		common.NewNamespace(c),
		common.NewKubeStateMetrics(c),
		common.NewNodeExporter(c),
		// Enable it on gke after reading https://www.signicat.com/blog/dont-use-nodelocal-dnscache-on-gke
		// and https://cloud.google.com/kubernetes-engine/docs/how-to/nodelocal-dns-cache.
		// common.NewNodeLocalDNS(c),
		common.NewEventExporter(c),
		common.NewCertManager(c),
		gketask.NewPCAIssuer(c),
		common.NewSelfSignedCAClusterIssuer(c),
		// common.NewTiDBServerSelfSignedClusterIssuer(c),
	)
	tasks = append(tasks, getTiDBOperatorTasks(c)...)
	tasks = append(tasks,
		common.NewClusterGateway(c),
		common.NewClusterRole(c),
		common.NewClusterProxy(c),
		gketask.NewSLIReporter(c),
	)
	if c.APPBox.GetAnnotations()[v1alpha1.AnnotationKeyDisableTiflowOperator] != "true" {
		tasks = append(tasks, common.NewTiFlowOperator(c))
	}
	tasks = append(tasks,
		common.NewDataflowAgent(c),
		common.NewO11y(c),
		gketask.NewRBAC(c),
	)
	return tasks
}

func GetAKSAPPBoxTasks(r *APPBoxReconciler, c *task.Context) (tasks []deploy.Task) {
	tasks = []deploy.Task{
		common.NewGuardian(c),
		akstask.NewAKSManagedIdentity(c),
		akstask.NewAKS(c),
		common.NewNodePoolGC(c),
		akstask.NewNodeRoleAssignment(c),
		akstask.NewOIDC(c),
		common.NewNamespace(c),
		common.NewKubeStateMetrics(c),
		common.NewNodeExporter(c),
		common.NewEventExporter(c),
		common.NewCertManager(c),
		akstask.NewPCAIssuer(c),
		common.NewSelfSignedCAClusterIssuer(c),
		// common.NewTiDBServerSelfSignedClusterIssuer(c),
	}
	tasks = append(tasks, getTiDBOperatorTasks(c)...)
	tasks = append(tasks,
		common.NewClusterGateway(c),
		common.NewClusterRole(c),
		common.NewClusterProxy(c),
		akstask.NewSLIReporter(c),
	)
	if c.APPBox.GetAnnotations()[v1alpha1.AnnotationKeyDisableTiflowOperator] != "true" {
		tasks = append(tasks, common.NewTiFlowOperator(c))
	}
	tasks = append(tasks,
		common.NewDataflowAgent(c),
		common.NewO11y(c), // TODO, support in o11y
		akstask.NewRBAC(c),
	)
	return tasks
}

// getTiDBOperatorTasks returns the TiDB Operator tasks.
// If the feature gate of tidb operator v2 is enabled, we will deploy both v1 and v2,
// because DM relies on v1.
func getTiDBOperatorTasks(c *task.Context) []deploy.Task {
	tasks := []deploy.Task{
		common.NewTiDBOperator(c),
	}

	if c.VPC == nil {
		return tasks // for UT
	}

	tenantID := c.VPC.GetLabels()[v1alpha1.TagTenantKey]
	projectID := c.VPC.GetLabels()[v1alpha1.TagProjectKey]
	if pkgconfig.FeatureEnabled(c.Config.FeatureGates, pkgconfig.DeployTiDBOperatorV2, tenantID, projectID) {
		tasks = append(tasks, common.NewTiDBOperatorV2(c))
	}
	return tasks
}
