package selfmanaged

import (
	"encoding/base64"
	"fmt"
	"sort"

	"github.com/crossplane-contrib/provider-aws/apis/autoscaling/v1beta1"
	ec2v1alpha1 "github.com/crossplane-contrib/provider-aws/apis/ec2/v1alpha1"
	"k8s.io/utils/pointer"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/config"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/pkg/nodegroup"
	"github.com/tidbcloud/infra-provider/pkg/util"
)

// GetTiDBClusterNodeGroup returns the self-managed nodegroup CR and launch template CR for a TiDB Cluster.
// It includes k8s node version info in the nodegroup name.
func GetTiDBClusterNodeGroup(
	cluster *cloudv1.Cluster,
	ng cloudv1.NodegroupSpec,
	ngName string,
	eksName string,
	eksEndpoint string,
	eksCA []byte,
	region string,
	nodeVersion string,
	subnetIds []string,
	securityGroupIDs []*string,
	providerConfigName string,
	instanceProfileName string,
	amiCfg config.WorkerNodeAMIConfig,
	systemConfiguration string,
) (group *v1beta1.AutoScalingGroup, ngLaunchTemplate *ec2v1alpha1.LaunchTemplate, err error) {

	tags := ng.Labels
	tags = util.AddResourceName(tags, ngName)
	size, err := util.ParseDiskSizeAsGiB(ng.MachineSpec.SystemVolumeSize)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to parse system volume size: %w", err)
	}

	var amiID string
	switch ng.MachineSpec.MachineArch {
	case consts.ARM64ArchName:
		amiID = amiCfg.ARMAMIIDs[nodeVersion]
	case consts.AMD64ArchName:
		amiID = amiCfg.X86AMIIDs[nodeVersion]
	}
	if amiID == "" {
		return nil, nil, fmt.Errorf("AMI ID should not be empty")
	}

	ngLaunchTemplate = nodegroup.GetBaseLaunchTemplate(ngName, region, providerConfigName, ng.Labels, tags, nodegroup.GetBlockDeviceMappingsForBottlerocket(size))
	MutateLaunchTemplate(ngLaunchTemplate, instanceProfileName, amiID, ng.MachineSpec.MachineType, GetBottlerocketUserData(eksName, eksEndpoint, base64.StdEncoding.EncodeToString(eksCA), ng.Labels, ng.Taints, systemConfiguration), securityGroupIDs)

	includeAutoscalerTags := cluster.FeatureEnabled(consts.ClusterFeatureUseTiDBOperatorV2) ||
		!cluster.FeatureEnabled(consts.ClusterFeatureDisableClusterAutoscaler)
	asgTags := getASGTags(eksName, ngName, includeAutoscalerTags)
	group = GetAutoScalingGroup(ngName, region, providerConfigName, nodeVersion, ng.Labels, asgTags, subnetIds, ng.Minimum, ng.Maximum)

	return group, ngLaunchTemplate, nil
}

// getASGTags returns ASG tags for the nodegroup.
// When includeAutoscalerTags is false (e.g. cluster-autoscaler disabled for self-managed nodegroup),
// omits cluster-autoscaler discovery tags so the autoscaler does not manage this ASG.
func getASGTags(eksName, ngName string, includeAutoscalerTags bool) []*v1beta1.Tag {
	// TODO(cluster-controller): fix `eksName` for seperate cluster-controller
	tags := []*v1beta1.Tag{
		{
			Key:               pointer.String("eks:cluster-name"),
			Value:             pointer.String(eksName),
			PropagateAtLaunch: pointer.Bool(true),
		},
		{
			Key:               pointer.String("eks:nodegroup-name"),
			Value:             pointer.String(ngName),
			PropagateAtLaunch: pointer.Bool(true),
		},

		{
			Key:               pointer.String("kubernetes.io/cluster/" + eksName),
			Value:             pointer.String("owned"),
			PropagateAtLaunch: pointer.Bool(true),
		},
	}
	if includeAutoscalerTags {
		tags = append(tags,
			&v1beta1.Tag{
				Key:               pointer.String("k8s.io/cluster-autoscaler/enabled"),
				Value:             pointer.String("true"),
				PropagateAtLaunch: pointer.Bool(true),
			},
			&v1beta1.Tag{
				Key:               pointer.String("k8s.io/cluster-autoscaler/" + eksName),
				Value:             pointer.String("owned"),
				PropagateAtLaunch: pointer.Bool(true),
			},
		)
	}
	sort.Slice(tags, func(i, j int) bool { return *(tags[i].Key) < *(tags[j].Key) })
	return tags
}
