package task

import (
	"context"

	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/namer"
	"github.com/tidbcloud/ticdc-service/pkg/cloud"
	corev1 "k8s.io/api/core/v1"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

type nodePool struct {
	*baseTask
}

func NewNodePoolTask(ctx *Context) deploy.Task {
	return &nodePool{
		baseTask: &baseTask{Context: ctx},
	}
}

func (t *nodePool) Name() string {
	return "NodePool"
}

func (t *nodePool) Create(ctx context.Context) deploy.Result {
	instanceType := t.Cdc.Spec.InstanceType
	k8sVersion := t.Cdc.Spec.NodeVersion
	k8sName, err := t.CloudProvider.GetKubernetesName(ctx)
	if err != nil {
		return deploy.Error(err)
	}
	k8sID, err := t.CloudProvider.GetKubernetesID(ctx)
	if err != nil {
		return deploy.Error(err)
	}
	nodeZones, err := t.CloudProvider.GetNodeZones(ctx)
	if err != nil {
		return deploy.Error(err)
	}
	nodeSubnets, err := t.CloudProvider.GetNodeSubnets(ctx)
	if err != nil {
		return deploy.Error(err)
	}
	podSubnets, err := t.CloudProvider.GetPodSubnets(ctx)
	if err != nil {
		return deploy.Error(err)
	}

	secondaryNodeSubnets, err := t.CloudProvider.GetSecondaryNodeSubnets(ctx)
	if err != nil {
		return deploy.Error(err)
	}
	nodeSubnets = append(nodeSubnets, secondaryNodeSubnets...)

	nodePoolName := t.getNodePoolName(instanceType, k8sName, k8sVersion, nodeZones, secondaryNodeSubnets)
	name := types.NamespacedName{
		Name:      nodePoolName,
		Namespace: t.Cdc.Namespace,
	}
	taints := []v1.Taint{
		{
			Key:    "dedicated",
			Value:  t.getTaintValue(secondaryNodeSubnets),
			Effect: corev1.TaintEffectNoSchedule,
		},
	}
	// cloud resource tags, used for cost and billing analysis
	tags := t.getCloudResourceTags()
	// k8s resource labels
	labels := map[string]string{
		cloudv1.TagLabelPrefix + consts.TagKeyTenant:      t.getTenantID(),
		cloudv1.TagLabelPrefix + consts.TagKeyProject:     t.getProjectID(),
		cloudv1.TagLabelPrefix + consts.TagKeyUsedBy:      consts.UsedByCustomerTiDB,
		cloudv1.TagLabelPrefix + consts.TagKeyCluster:     consts.ClusterShared,
		cloudv1.TagLabelPrefix + consts.TagKeyServiceType: consts.ServiceTypeDedicated,
		// used for node schedule
		namer.ComponentSelectorKey: string(cloudv1.ComponentTiCDC),
		consts.K8sVersionLabelKey:  k8sVersion,
	}
	nodepool := cloud.NodePool{
		Name:          nodePoolName,
		InstanceType:  instanceType,
		KubeClusterID: k8sID,
		Zones:         nodeZones,
		NodeSubnets:   nodeSubnets,
		PodSubnets:    podSubnets,
		Taints:        taints,
		Tags:          tags,
		Labels:        labels,
		Region:        t.Config.Region,
		Identity:      t.ClusterRole,
	}
	pc := t.getProviderConfigName(k8sName)
	np, err := t.CloudProvider.BuildNodePool(pc, name, &nodepool)
	if err != nil {
		return deploy.Error(err)
	}
	// avoid using k8s.Apply due to we use unstructured for unsupported field in upbound sdk
	_, err = controllerutil.CreateOrUpdate(ctx, t.Local, np, func() error { return nil })
	return deploy.PanicOrDone(err)
}

func (t *nodePool) PostCreate(ctx context.Context) deploy.Result {
	// cloud resource tags, used for cost and billing analysis
	tags := t.getCloudResourceTags()
	return deploy.PanicOrDone(t.CloudProvider.PostCreateNodePool(ctx, t.Local, tags))
}

// nodepool is shared by all cdc cluster in the same dataplane k8s cluster
// So we should not delete them when a single cdc cluster is deleted
func (t *nodePool) Delete(ctx context.Context) deploy.Result {
	return deploy.Done()
}

func (t *nodePool) getCloudResourceTags() map[string]string {
	// cloud resource tags, used for cost and billing analysis
	tags := map[string]string{
		consts.TagKeyTenant:      t.getTenantID(),
		consts.TagKeyProject:     t.getProjectID(),
		consts.TagKeyUsedBy:      consts.UsedByCustomerTiDB,
		consts.TagKeyServiceType: consts.ServiceTypeDedicated,
		consts.TagKeyEnvironment: t.Env,
		consts.TagKeyComponent:   string(cloudv1.ComponentTiCDC),
	}
	return tags
}
