package extragroup

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

	"github.com/aws/aws-sdk-go-v2/service/ec2"
	"github.com/aws/aws-sdk-go-v2/service/ec2/types"
	k8serrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/utils/pointer"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/pkg/namer"
)

// retrieveServiceSG returns the dedicated service security group for the cluster
func retrieveServiceSG(ctx context.Context, cli *ec2.Client, namer *namer.Namer) (*types.SecurityGroup, error) {
	// TODO: if hit EC2 API throttling, we could cache SG ID into cluster's annotation to minimize API calls
	res, err := cli.DescribeSecurityGroups(ctx, &ec2.DescribeSecurityGroupsInput{
		Filters: []types.Filter{
			{
				Name:   pointer.String("tag:Name"),
				Values: []string{namer.GetPublicServiceSGName()},
			},
		},
	})
	if err != nil {
		return nil, err
	}
	if len(res.SecurityGroups) == 0 {
		return nil, nil
	}
	if len(res.SecurityGroups) > 1 {
		return nil, fmt.Errorf("found %d service SG with name %s", len(res.SecurityGroups), namer.GetPublicServiceSGName())
	}
	return &res.SecurityGroups[0], nil
}

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 tidbNodeGroupName(name string) string {
	return consts.TiDBCloudPrefix + name
}

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 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
}
