package action

import (
	"fmt"

	"github.com/tidbcloud/cluster-service-ng/domain/workflow"
	"github.com/tidbcloud/cluster-service-ng/pkg/dataplane"
	"github.com/tidbcloud/cluster-service-ng/pkg/k8s"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/actions"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo"
	"github.com/tidbcloud/cluster-service-ng/pkg/utils"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	corev1 "k8s.io/api/core/v1"
	schedulingv1 "k8s.io/api/scheduling/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/utils/ptr"
)

const (
	priorityClassNameTiDB               = "tidb-ng-component-critical"
	priorityClassNamePlaceholder        = "placeholder-low-priority"
	priorityClassValueTiDB              = 1000000000
	priorityClassValuePlaceholder       = -9
	priorityClassDescriptionTiDB        = "Only used for TiDB components"
	priorityClassDescriptionPlaceholder = "Very low priority for placeholder pods"
)

type CreatePriorityClassAction struct {
	dataplaneManager dataplane.Manager

	sharedPoolRepo repo.SharedPoolRepo
}

const CreatePriorityClassActionName = "create-priority-class"

func NewCreatePriorityClassAction(sharedPoolRepo repo.SharedPoolRepo, dataplaneManager dataplane.Manager) actions.Action {
	return &CreatePriorityClassAction{
		sharedPoolRepo:   sharedPoolRepo,
		dataplaneManager: dataplaneManager,
	}
}

func (a *CreatePriorityClassAction) Name() string {
	return CreatePriorityClassActionName
}

// Invoke creates a priority class in the k8s cluster
func (a *CreatePriorityClassAction) Invoke(args actions.ActionArgs) error {
	// generate context
	ctx := utils.GenerateContext(args)
	tilog.Info(ctx, "create priority class action invoked")

	// get sharedPool id
	sharedPoolId, err := args.GetString(workflow.SharedPoolIdKey)
	if err != nil {
		return fmt.Errorf("failed to get sharedPool id: %w", err)
	}

	// load sharedPool
	sharedPool, err := a.sharedPoolRepo.Load(ctx, sharedPoolId, false)
	if err != nil {
		return fmt.Errorf("failed to load sharedPool: %w", err)
	}

	remoteCli, err := a.dataplaneManager.GetClient(sharedPool.GetDataplaneName())
	if err != nil {
		tilog.Error(ctx, "failed to get remote client for shared pool", zap.String("dataplaneName", sharedPool.GetDataplaneName()), zap.Error(err))
		return fmt.Errorf("failed to get remote client for shared pool: %w", err)
	}

	{
		// for tidb pods
		pc := &schedulingv1.PriorityClass{
			ObjectMeta: metav1.ObjectMeta{
				Name: priorityClassNameTiDB,
			},
			Value:            priorityClassValueTiDB,
			GlobalDefault:    false,
			Description:      priorityClassDescriptionTiDB,
			PreemptionPolicy: ptr.To(corev1.PreemptLowerPriority),
		}
		if err := k8s.CreateIfNotExist(ctx, remoteCli, pc); err != nil {
			return fmt.Errorf("failed to create or update priority class: %w", err)
		}
	}
	{
		// for placeholder pods
		pc := &schedulingv1.PriorityClass{
			ObjectMeta: metav1.ObjectMeta{
				Name: priorityClassNamePlaceholder,
			},
			Value:            priorityClassValuePlaceholder,
			GlobalDefault:    false,
			Description:      priorityClassDescriptionPlaceholder,
			PreemptionPolicy: ptr.To(corev1.PreemptLowerPriority),
		}
		if err := k8s.CreateIfNotExist(ctx, remoteCli, pc); err != nil {
			return fmt.Errorf("failed to create or update priority class: %w", err)
		}
	}

	return nil
}
