package common

import (
	"context"
	"fmt"
	"time"

	corev1 "k8s.io/api/core/v1"
	schedulingv1 "k8s.io/api/scheduling/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/utils/ptr"

	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/appbox/task"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/deploy"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/k8s"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/util"
)

const (
	priorityClassNameDataplaneNodeO11yCritical    = "dataplane-node-o11y-critical"
	priorityClassNameDataplaneNodeO11yNonCritical = "dataplane-node-o11y-noncritical"
	priorityClassNamePlaceholderLow               = "placeholder-low-priority"
	priorityClassNameSystemClusterCritical        = "system-cluster-critical"
	priorityClassNameSystemNodeCritical           = "system-node-critical"
	priorityClassNameTiDBClusterO11yCritical      = "tidb-cluster-o11y-critical"
	priorityClassNameTiDBComponentCritical        = "tidb-ng-component-critical"
	priorityClassNameTiDBComponentNonCritical     = "tidb-ng-component-noncritical"
)

type priorityClassDef struct {
	name        string
	value       int32
	description string
}

var commonPriorityClasses = []priorityClassDef{
	{
		name:        priorityClassNameSystemNodeCritical,
		value:       2000001000,
		description: "System-critical pods that must run on every node",
	},
	{
		name:        priorityClassNameSystemClusterCritical,
		value:       2000000000,
		description: "System-critical pods that must run for cluster health",
	},
	{
		name:        priorityClassNameTiDBComponentCritical,
		value:       1000000000,
		description: "Critical TiDB components",
	},
	{
		name:        priorityClassNameDataplaneNodeO11yCritical,
		value:       999999998,
		description: "Critical observability components running on dataplane nodes",
	},
	{
		name:        priorityClassNameTiDBClusterO11yCritical,
		value:       999999998,
		description: "Critical observability components running with TiDB clusters",
	},
	{
		name:        priorityClassNameTiDBComponentNonCritical,
		value:       800000000,
		description: "Non-critical TiDB components",
	},
	{
		name:        priorityClassNameDataplaneNodeO11yNonCritical,
		value:       -1,
		description: "Non-critical observability components running on dataplane nodes",
	},
	{
		name:        priorityClassNamePlaceholderLow,
		value:       -9,
		description: "Very low priority for placeholder pods",
	},
}

// PriorityClass bootstraps common priority classes in the dataplane cluster.
// The task only creates missing classes and does not mutate existing resources.
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"
}

func (t *PriorityClass) Create(ctx context.Context) deploy.Result {
	t.BeginAt = util.Pointer(time.Now())
	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}

	for _, pcDef := range commonPriorityClasses {
		pc := &schedulingv1.PriorityClass{
			ObjectMeta: metav1.ObjectMeta{
				Name: pcDef.name,
			},
			Value:            pcDef.value,
			GlobalDefault:    false,
			Description:      pcDef.description,
			PreemptionPolicy: ptr.To(corev1.PreemptLowerPriority),
		}
		if err := k8s.CreateIfNotExist(ctx, cli, pc); err != nil {
			return deploy.Panic(fmt.Errorf("create priority class %s: %w", pcDef.name, err))
		}
	}

	return deploy.Done()
}
