package cluster

import (
	"fmt"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/eks-provider/api/v1alpha1"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/pkg/controllers/common"
	"github.com/tidbcloud/infra-provider/pkg/procedure"
	"github.com/tidbcloud/infra-provider/pkg/util"
	util2 "github.com/tidbcloud/infra-provider/pkg/util"
)

const (
	bucketNameAnnotation string = "bucket"
)

func TagsForBucket(network *cloudv1.Network, name string) map[string]string {
	tags := cloudv1.TagsFromLabels(network.Labels)
	// this tag key has uppercase letter which is forbidden in GCP, so only add it for AWS
	if network.Spec.Provider == consts.ProviderTypeEKS {
		tags = util.AddResourceName(tags, name)
	}
	tags = util.ConcatenateTags(tags, map[string]string{
		// override the network "usedby":"dbaas-control-plane-shoot" label
		common.UsedBy:    common.CustomerTiDB,
		common.Component: string(cloudv1.ComponentTiflow),
	})

	return tags
}

func FormatBucketName(project string, network *cloudv1.Network, master string) string {
	const (
		labelLengthLimit = 63
		hashSize         = 8
		resultLimit      = labelLengthLimit - hashSize - 1
	)
	region := network.Spec.Region
	infraProject := network.GetProjectId()
	result := fmt.Sprintf("dbaas-%s-%s-%s", project, region, infraProject)
	if len(result) > resultLimit {
		result = result[:resultLimit]
	}

	return fmt.Sprintf("%s-%s", result, util2.FnvHash(master))
}

func UpdateBucketReadyForMaster(master *v1alpha1.TiflowClusterMaster, isReady bool) {
	UpdateCondition(&master.Status.Conditions, v1alpha1.TiflowClusterBucketReady, isReady)
}

func AddBucketAnnotationToMaster(master *v1alpha1.TiflowClusterMaster, bucketName string) {
	if master.Annotations == nil {
		master.Annotations = map[string]string{}
	}

	master.Annotations[bucketNameAnnotation] = bucketName
}

func UpdateCondition(cond *v1alpha1.Conditions, ctype v1alpha1.ConditionType, isReady bool) {
	condition := v1alpha1.Condition{
		Type:               ctype,
		Status:             corev1.ConditionFalse,
		LastTransitionTime: metav1.Now(),
	}
	if isReady {
		condition.Status = corev1.ConditionTrue
	}
	if !procedure.ConditionEqual(cond.GetCondition(ctype), condition) {
		cond.SetCondition(condition)
	}
}

func LabelsForNodeGroup(baseCtx *BaseContext, tidbClusterName string) map[string]string {
	labels := baseCtx.Namer.Labels()
	labels = util.ConcatenateTags(labels, cloudv1.TagsFromLabels(baseCtx.Network.Labels))
	labels = util.ConcatenateTags(labels, map[string]string{
		// override the network "usedby":"dbaas-control-plane-shoot" label
		common.UsedBy:    common.CustomerTiDB,
		common.Component: string(cloudv1.ComponentTiflow),
		common.Cluster:   tidbClusterName,
	})
	return labels
}

// ComponentNodePoolSelector get all node pools/groups selector for the component.
// the selector doesn't include spec label to handle job scaling
// in which case there might be 2 node pools/groups.
func ComponentNodePoolSelector(ctx *BaseContext) *client.MatchingLabelsSelector {
	return &client.MatchingLabelsSelector{
		Selector: labels.Set(ctx.Namer.LabelsWithoutSpecLabel()).AsSelector(),
	}
}
