package action

import (
	"encoding/json"
	"fmt"

	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"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"
	templateversion "github.com/tidbcloud/cluster-service-ng/pkg/template/version"
	"github.com/tidbcloud/cluster-service-ng/pkg/utils"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)

const ApplyAutoscalerActionName = "apply-autoscaler"

var tidbGroupGVK = schema.GroupVersionKind{Group: "core.pingcap.com", Version: "v1alpha1", Kind: "TiDBGroup"}

type ApplyAutoscalerAction struct {
	sharedPoolRepo     repo.SharedPoolRepo
	dataplaneManager   dataplane.Manager
	templateVersionSvc *templateversion.Service
}

func NewApplyAutoscalerAction(
	sharedPoolRepo repo.SharedPoolRepo,
	dataplaneManager dataplane.Manager,
	templateVersionSvc *templateversion.Service,
) actions.Action {
	return &ApplyAutoscalerAction{
		sharedPoolRepo:     sharedPoolRepo,
		dataplaneManager:   dataplaneManager,
		templateVersionSvc: templateVersionSvc,
	}
}

func (a *ApplyAutoscalerAction) Name() string {
	return ApplyAutoscalerActionName
}

func (a *ApplyAutoscalerAction) Invoke(args actions.ActionArgs) error {
	templateArgsJSON := ""
	if arg, ok := args[workflow.AutoscalerTemplateArgsKey]; ok {
		templateArgsJSON = arg.String()
	}
	if templateArgsJSON == "" {
		return nil
	}
	ctx := utils.GenerateContext(args)
	sharedPoolID, err := args.GetString(workflow.SharedPoolIdKey)
	if err != nil {
		return fmt.Errorf("failed to get shared pool id: %w", err)
	}
	tidbGroupID, err := args.GetString(workflow.TiDBGroupIdKey)
	if err != nil {
		return fmt.Errorf("failed to get TiDBGroup id: %w", err)
	}
	sharedPool, err := a.sharedPoolRepo.Load(ctx, sharedPoolID, false)
	if err != nil {
		return fmt.Errorf("failed to load shared pool: %w", err)
	}
	if !sharedPool.EnableCapacityPlanner {
		return nil
	}
	remoteClient, err := a.dataplaneManager.GetClient(sharedPool.GetDataplaneName())
	if err != nil {
		return fmt.Errorf("failed to get dataplane client: %w", err)
	}

	templateArgs := map[string]string{}
	if err := json.Unmarshal([]byte(templateArgsJSON), &templateArgs); err != nil {
		return fmt.Errorf("failed to unmarshal autoscaler template args: %w", err)
	}
	namespace := "tidb" + sharedPoolID
	name := entity.GetComponentGroupName(&entity.Component{Id: tidbGroupID})

	target := &unstructured.Unstructured{}
	target.SetGroupVersionKind(tidbGroupGVK)
	if err := remoteClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, target); err != nil {
		return fmt.Errorf("failed to get target TiDBGroup %s/%s: %w", namespace, name, err)
	}
	templateArgs["SharedPoolId"] = sharedPoolID
	templateArgs["ComponentGroup"] = name
	templateArgs["TargetUID"] = string(target.GetUID())
	yamlStr, resolvedName, resolvedVersion, err := templateversion.RenderWithVersion(
		ctx,
		a.templateVersionSvc,
		"tidb_group_autoscaler",
		"",
		templateArgs,
	)
	if err != nil {
		return fmt.Errorf("failed to render Autoscaler template: %w", err)
	}

	desired, _, err := k8s.GenerateUnstructuredFromYaml(yamlStr)
	if err != nil {
		return fmt.Errorf("failed to generate Autoscaler from template: %w", err)
	}
	templateversion.SetVersionAnnotations(desired, resolvedName, resolvedVersion)

	autoscaler := desired.DeepCopy()
	if _, err := controllerutil.CreateOrUpdate(ctx, remoteClient, autoscaler, func() error {
		autoscaler.Object["spec"] = desired.Object["spec"]
		autoscaler.SetAnnotations(desired.GetAnnotations())
		autoscaler.SetLabels(desired.GetLabels())
		autoscaler.SetOwnerReferences(desired.GetOwnerReferences())
		return nil
	}); err != nil {
		return fmt.Errorf("failed to apply Autoscaler %s/%s: %w", namespace, name, err)
	}
	return nil
}
