package eks

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"time"

	k8serrors "k8s.io/apimachinery/pkg/util/errors"
	ctrl "sigs.k8s.io/controller-runtime"

	"github.com/tidbcloud/eks-provider/controllers/cluster/task"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/pkg/util"
)

var defaultNodeGroupRequeue = ctrl.Result{RequeueAfter: 10 * time.Second}

type Nodegroup struct {
	*task.BaseTask
}

func NewNodegroup(ctx *task.Context) *Nodegroup {
	return &Nodegroup{BaseTask: &task.BaseTask{Context: ctx}}
}

func (t *Nodegroup) Name() string {
	return "Nodegroup"
}

// nodegroup name format:
// tidbcloud-FnvHash({clusterName}-{componentName}-{zoneSuffix})-FnvHash({instanceType})-{networkNamespace}-{networkName}-md5Hash({namespacedNetworkName})
// {networkNamespace}-{networkName} might be truncated if the full length exceeded 64
// md5Hash({namespacedName}) only takes the first 8 char of the hash
func (t *Nodegroup) Create(ctx context.Context) deploy.Result {
	// skip it if self managed nodegroup is enabled
	if util.GetAnnotation(t.Cluster, consts.UseSelfManagedNodegroupAnnotation) != consts.AnnoValTrue {
		return deploy.Error(fmt.Errorf("self managed nodegroup is enabled"))
	}
	return deploy.Done()
}

func tidbNodeGroupName(name string) string {
	return consts.TiDBCloudPrefix + name
}

var _ cloudv1.ClusterComponent = &component{}

type component struct {
	cloudv1.ClusterComponent
	replicas int
}

// GetReplicas overrides `cloudv1.ClusterComponent.GetReplicas` to return the replicas of the component.
//
// It is used as the desired node count of the component.
func (c component) GetReplicas() int {
	return c.replicas
}

func appendZonesLabelForNodegroup(ng *cloudv1.NodegroupSpec) {
	if len(ng.Zones) == 0 {
		return
	}
	zones := make([]string, len(ng.Zones)) // in fact, there is only one zone now
	copy(zones, ng.Zones)
	sort.Strings(zones)
	if ng.Labels == nil {
		ng.Labels = make(map[string]string)
	}
	ng.Labels[consts.NodeGroupZonesLabelKey] = strings.Join(zones, ",")
}

func chooseNonRequeueOnlyErrorsFist(errs []error) error {
	var (
		requeueOnlyErr     error
		nonRequeueOnlyErrs []error
	)
	for _, err := range errs {
		if k8s.RequeueOnly(err) {
			requeueOnlyErr = err // overwrite is ok
		} else {
			nonRequeueOnlyErrs = append(nonRequeueOnlyErrs, err)
		}
	}
	if len(nonRequeueOnlyErrs) != 0 {
		return k8serrors.NewAggregate(nonRequeueOnlyErrs)
	}
	return requeueOnlyErr
}

func getNodeLabelsForScaleUp(ngLabels map[string]string) (nodeLabels map[string]string) {
	nodeLabels = make(map[string]string)
	for k, v := range ngLabels {
		if k != consts.GardenerNodeInstanceType {
			nodeLabels[k] = v
		}
	}
	return
}
