package common

import (
	"context"
	"fmt"

	corev1 "k8s.io/api/core/v1"
	schedulingv1 "k8s.io/api/scheduling/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	"github.com/tidbcloud/eks-provider/controllers/cluster/task"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
)

const (
	/*
		According to other priority class values, use `1000000000` for tidb components:
		NAME                              VALUE
		dataplane-node-o11y-critical      999999998
		dataplane-node-o11y-noncritical   -1
		over-provisioning-pod             -5
		system-cluster-critical           2000000000
		system-node-critical              2000001000
		tidb-cluster-backup-critical      900000000
		tidb-cluster-o11y-critical        999999998
	*/
	priorityClassValue       = 1000000000
	priorityClassDescription = "Only used for TiDB components"
)

type PriorityClass struct {
	*task.BaseTask
}

func NewPriorityClass(ctx *task.Context) *PriorityClass {
	return &PriorityClass{BaseTask: &task.BaseTask{Context: ctx}}
}

func (t *PriorityClass) Name() string {
	return "PriorityClass"
}

// Create will always create a priority class for tidb components.
func (t *PriorityClass) Create(ctx context.Context) deploy.Result {
	pc := &schedulingv1.PriorityClass{
		ObjectMeta: metav1.ObjectMeta{
			Name: consts.PriorityClasName,
			// Add the finalizer to the priority class to avoid it being deleted by mistake.
			// In this case, we cannot create more Pods that use the deleted PriorityClass.
			Finalizers: []string{task.Finalizer},
		},
		Value:         priorityClassValue,
		GlobalDefault: false,
		Description:   priorityClassDescription,
		// For existing clusters, their priority is zero,
		// to avoid they are preempted by new clusters with the priority class,
		// set the policy to Never.
		// See: https://kubernetes.io/zh-cn/docs/concepts/scheduling-eviction/pod-priority-preemption/#non-preempting-priority-class
		PreemptionPolicy: ptr.To(corev1.PreemptNever),
	}
	cli := t.EnvProvider.GetClient()
	if _, err := controllerutil.CreateOrUpdate(ctx, cli, pc, func() error { return nil }); err != nil {
		return deploy.Error(fmt.Errorf("failed to create or update priority class: %v", err))
	}

	return deploy.Done()
}

func (t *PriorityClass) Delete(ctx context.Context) deploy.Result {
	// No need to delete the priority class
	return deploy.Done()
}
