package network

import (
	"github.com/tidbcloud/eks-provider/api/v1alpha1"
	"github.com/tidbcloud/eks-provider/controllers/network/task"
	commontask "github.com/tidbcloud/eks-provider/controllers/network/task/common"
	ekstask "github.com/tidbcloud/eks-provider/controllers/network/task/eks"
	gketask "github.com/tidbcloud/eks-provider/controllers/network/task/gke"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
)

func GetNetworkTasks(r *NetworkReconciler, c *task.Context) []deploy.Task {
	switch c.Network.Spec.Provider {
	case consts.ProviderTypeEKS:
		return GetEKSNetworkTasks(r, c)
	case consts.ProviderTypeGKE:
		return GetGKENetworkTasks(r, c)
	}

	return nil
}

func GetHibernatedNetworkTasks(r *NetworkReconciler, c *task.Context) (tasks []deploy.Task) {
	switch c.Network.Spec.Provider {
	case consts.ProviderTypeEKS:
		full := v1alpha1.IsFullManage(c.Network)
		if !full {
			return
		}
		tasks = GetEKSFullManagedHibernatedNetworkTasks(r, c)
	case consts.ProviderTypeGKE:
		tasks = GetGKEHibernatedNetworkTasks(r, c)
		return
	}

	return
}

func GetHibernatingNetworkTasks(r *NetworkReconciler, c *task.Context) (tasks []deploy.Task) {
	switch c.Network.Spec.Provider {
	case consts.ProviderTypeEKS:
		full := v1alpha1.IsFullManage(c.Network)
		if !full {
			return
		}
		tasks = GetEKSFullManagedHibernatingNetworkTasks(r, c)
	case consts.ProviderTypeGKE:
		tasks = GetGKEHibernatingNetworkTasks(r, c)
		return
	}

	return
}

func GetGKEHibernatedNetworkTasks(r *NetworkReconciler, c *task.Context) (tasks []deploy.Task) {
	tasks = []deploy.Task{
		gketask.NewGuardian(c),
		commontask.NewAccount(c, r.AcctScheduler),
		gketask.NewProviderConfig(c),
		gketask.NewVPC(c, r.GCP),
	}
	return tasks
}

func GetGKEHibernatingNetworkTasks(r *NetworkReconciler, c *task.Context) (tasks []deploy.Task) {
	allTasks := GetGKENetworkTasks(r, c)

	hibernatedTasks := GetGKEHibernatedNetworkTasks(r, c)

	var hibernetingTasks []deploy.Task
	// Subnetwork must deleted otherwise GKE cluster recreate will fail
	hibernetingTasks = append(hibernetingTasks, gketask.NewSubnetworkCleanUp(c, r.GCP))

	for _, task := range allTasks {
		if containsTask(hibernatedTasks, task) {
			continue
		}
		hibernetingTasks = append(hibernetingTasks, task)
	}

	return hibernetingTasks
}

func GetEKSFullManagedHibernatedNetworkTasks(r *NetworkReconciler, c *task.Context) (tasks []deploy.Task) {
	tasks = []deploy.Task{
		ekstask.NewGuardian(c),
		commontask.NewAccount(c, r.AcctScheduler),
		ekstask.NewProviderConfig(c),
		ekstask.NewVPC(c, r.AWS),
	}

	return tasks
}

// GetEKSFullManagedHibernatingNetworkTasks get tasks that when network status is hiberneting status.
// If network is hiberated status, we will remains vpc and bucket resources, so we should skip hibernated task when status is hibernating
func GetEKSFullManagedHibernatingNetworkTasks(r *NetworkReconciler, c *task.Context) (tasks []deploy.Task) {
	allTasks := GetEKSNetworkTasks(r, c)

	hibernatedTasks := GetEKSFullManagedHibernatedNetworkTasks(r, c)

	var hibernetingTasks []deploy.Task
	hibernetingTasks = append(hibernetingTasks, ekstask.NewVpcEndpointCleanup(c))
	hibernetingTasks = append(hibernetingTasks, ekstask.NewNatGatewayCleanup(c))
	hibernetingTasks = append(hibernetingTasks, ekstask.NewAddressCleanup(c))

	for _, task := range allTasks {
		if containsTask(hibernatedTasks, task) {
			continue
		}
		hibernetingTasks = append(hibernetingTasks, task)
	}
	return hibernetingTasks
}

func containsTask(tasks []deploy.Task, task deploy.Task) bool {
	for _, t := range tasks {
		if t.Name() == task.Name() {
			return true
		}
	}
	return false
}

func GetEKSNetworkTasks(r *NetworkReconciler, c *task.Context) (tasks []deploy.Task) {
	tasks = []deploy.Task{
		ekstask.NewGuardian(c),
		commontask.NewAccount(c, r.AcctScheduler),
		ekstask.NewProviderConfig(c),
		ekstask.NewVPC(c, r.AWS),
		ekstask.NewEKS(c, r.AWS),
		ekstask.NewNodeRole(c),
		ekstask.NewNodegroup(c),
		ekstask.NewConnection(c), // ensure remote client ready in this task
		ekstask.NewCNI(c),
		commontask.NewNamespace(c),
		// ↓ self monitoring
		commontask.NewKubeStateMetrics(c),
		commontask.NewNodeExporter(c),
		commontask.NewLoki(c),
		// ↑ self monitoring
		commontask.NewClusterRole(c),
		ekstask.NewRBAC(c),
		ekstask.NewClusterAutoscaler(c),
		ekstask.NewCSI(c),
		ekstask.NewStorageClass(c),
		commontask.NewCertManager(c),
		commontask.NewPCAIssuer(c),
		commontask.NewSelfSignedCAClusterIssuer(c),
		// commontask.NewTiDBServerSelfSignedClusterIssuer(c),
		ekstask.NewAWSLoadBalancerController(c),
		commontask.NewOperator(c),
		commontask.NewClusterGateway(c),
		commontask.NewProxy(c),
		commontask.NewDataflowAgent(c),
		ekstask.NewReporter(c),
		commontask.NewCoreDNS(c),
		commontask.NewEventExporter(c),
		ekstask.NewS3GatewayEndpoint(c),
	}

	if c.Config.Network.TiflowOperator.Enable {
		tasks = append(tasks, commontask.NewTiflowOperator(c))
	}

	tasks = append(tasks, commontask.NewO11yCluster(c))

	return tasks
}

func GetGKENetworkTasks(r *NetworkReconciler, c *task.Context) (tasks []deploy.Task) {
	tasks = []deploy.Task{
		gketask.NewGuardian(c),
		commontask.NewAccount(c, r.AcctScheduler),
		gketask.NewProviderConfig(c),
		gketask.NewGCPServiceAccount(c),
		gketask.NewArtifactRegistryPolicy(c, r.GCP),
		gketask.NewVPC(c, r.GCP),
		gketask.NewNAT(c),
		gketask.NewGKE(c, r.GCP),
		gketask.NewProjectPolicy(c, r.GCP),
		gketask.NewConnection(c), // ensure remote client ready in this task
		gketask.NewNodePool(c, r.GCP),
		gketask.NewOIDC(c),
		gketask.NewGCRBucketPolicy(c),
		gketask.NewPSCFirewall(c),
		commontask.NewNamespace(c),
		// ↓ self monitoring
		commontask.NewKubeStateMetrics(c),
		commontask.NewNodeExporter(c),
		commontask.NewLoki(c),
		// ↑ self monitoring
		commontask.NewClusterRole(c),
		gketask.NewStorageClass(c),
		commontask.NewClusterGateway(c),
		commontask.NewProxy(c),
		commontask.NewDataflowAgent(c),
		commontask.NewCertManager(c),
		// TODO: combine with common pca issuer task
		gketask.NewPCAIssuer(c),
		commontask.NewSelfSignedCAClusterIssuer(c),
		// commontask.NewTiDBServerSelfSignedClusterIssuer(c),
		commontask.NewOperator(c),
		commontask.NewCoreDNS(c),
		gketask.NewCredential(c),
		commontask.NewEventExporter(c),
		gketask.NewGoogleApiPsc(c, r.GCP),
		gketask.NewReporter(c),
	}

	if c.Config.Network.TiflowOperator.Enable {
		tasks = append(tasks, commontask.NewTiflowOperator(c))
	}

	tasks = append(tasks, commontask.NewO11yCluster(c))

	return tasks
}
