package nodepool

import (
	"context"

	gkev1beta1 "github.com/crossplane-contrib/provider-gcp/apis/container/v1beta1"
	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/utils/pointer"
	"sigs.k8s.io/controller-runtime/pkg/client"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/namer"
	"github.com/tidbcloud/infra-provider/pkg/util"
	"github.com/tidbcloud/infra-provider/util/collections"
)

func buildNodePoolName(ngSetName string, network *cloudv1.Network) string {
	return namer.UniqueIdentifier(network.ObjectMeta, namer.WithPrefix(ngSetName), namer.WithLimit(40))
}

func toGKENodeTaints(ngSpec *cloudv1.NodegroupSpec) []*gkev1beta1.NodeTaint {
	var taints []*gkev1beta1.NodeTaint
	for _, taint := range ngSpec.Taints {
		effect := util.ToCrossplaneTaintEffect(taint.Effect)
		if effect == "" {
			continue
		}
		taints = append(taints, &gkev1beta1.NodeTaint{
			Effect: effect,
			Key:    taint.Key,
			Value:  taint.Value,
		})
	}
	return taints
}

// see more in
// https://doc.crds.dev/github.com/crossplane/provider-gcp/container.gcp.crossplane.io/NodePool/v1beta1@v0.18.0
func GetNodePoolByNGSpecAndNetWork(ngSpec cloudv1.NodegroupSpec, network *cloudv1.Network,
	gcpProjectName string) (np *gkev1beta1.NodePool, npMut func() error) {
	refName := namer.UniqueIdentifier(network.ObjectMeta)
	poolName := buildNodePoolName(ngSpec.Name, network)
	tags := ngSpec.Labels
	tags = util.AddResourceName(tags, poolName)
	taints := toGKENodeTaints(&ngSpec)
	np = &gkev1beta1.NodePool{
		ObjectMeta: metav1.ObjectMeta{
			Name: poolName,
		},
	}
	npMut = func() error {
		np.Labels = collections.MergeMaps(np.Labels, ngSpec.Labels)
		np.Annotations = collections.MergeMaps(np.Annotations, ngSpec.Annotations)
		size, err := util.ParseDiskSizeAsGiB(ngSpec.MachineSpec.SystemVolumeSize)
		if err != nil {
			return err
		}
		np.Spec = gkev1beta1.NodePoolSpec{
			// TODO: current we let the following task can be passed, we will optimize the configuration later.
			ForProvider: gkev1beta1.NodePoolParameters{
				ClusterRef: &xpv1.Reference{Name: namer.LegacyBuildClusterNameByNetwork(network)},
				Autoscaling: &gkev1beta1.NodePoolAutoscaling{
					Enabled:      pointer.Bool(true),
					MaxNodeCount: pointer.Int64(int64(ngSpec.Maximum)),
					MinNodeCount: pointer.Int64(int64(ngSpec.Minimum)),
				},
				Config: &gkev1beta1.NodeConfig{
					DiskSizeGb:  pointer.Int64(size),
					DiskType:    pointer.String("pd-standard"), // todo
					ImageType:   pointer.String("cos_containerd"),
					Labels:      tags,
					MachineType: &ngSpec.MachineSpec.MachineType,
					OauthScopes: []string{
						"https://www.googleapis.com/auth/cloud-platform",
					},
					Taints: taints,
				},
				Locations:        ngSpec.Zones,
				InitialNodeCount: pointer.Int64(0),
				Management: &gkev1beta1.NodeManagementSpec{
					// Do it manually
					AutoUpgrade: pointer.Bool(false),
					AutoRepair:  pointer.Bool(false),
				},
			},
			ResourceSpec: xpv1.ResourceSpec{ProviderConfigReference: &xpv1.Reference{Name: refName}},
		}
		if ngSpec.NodeRole != nil {
			np.Spec.ForProvider.Config.ServiceAccount = ngSpec.NodeRole
		} else {
			sa := namer.LegacyBuildGCPNodePoolServiceAccountEmailName(network, gcpProjectName)
			np.Spec.ForProvider.Config.ServiceAccount = &sa
		}
		return nil
	}
	return np, npMut
}

func DeleteNodePoolBySpecNameAndNetWork(ctx context.Context, cli client.Client, ngSpecName string, network *cloudv1.Network) error {
	npName := buildNodePoolName(ngSpecName, network)
	np := &gkev1beta1.NodePool{ObjectMeta: metav1.ObjectMeta{Name: npName}}
	return cli.Delete(ctx, np)
}
