package ngoperation

import (
	"context"
	"fmt"

	clusterng "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	opent "github.com/tidbcloud/devops/entity/operation"
	"github.com/tidbcloud/devops/pkg/service/operation/actions"
	nextgenv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
)

var (
	ActionUpdatePlacementPolicy = opent.ActionMetadata{
		Name: "UpdatePlacementPolicy",
		Type: opent.ActionTypeAuto,
	}
)

var _ actions.Action = &updatePlacementPolicy{}

type updatePlacementPolicy struct {
	actions.BaseAction
	comps *actions.Components
}

func NewUpdatePlacementPolicy(comps *actions.Components) actions.AutoAction {
	return &updatePlacementPolicy{comps: comps}
}

func (a *updatePlacementPolicy) Metadata() opent.ActionMetadata {
	return ActionUpdatePlacementPolicy
}

func (a *updatePlacementPolicy) PreCheck(ctx context.Context, instance *opent.Instance) error {
	_, _, err := a.getParam(ctx, instance)
	return err
}

func (a *updatePlacementPolicy) Start(ctx context.Context, instance *opent.Instance) (*actions.ActionResult, error) {
	req, rp, err := a.getParam(ctx, instance)
	if err != nil {
		return actions.FailedActionResult(err), nil
	}

	logicalRegion, provider := GetLogicalRegionAndProviderString(rp.GetRegion())
	policy, err := a.comps.MgmtHTTP.NextGenRegionServer(logicalRegion, provider).UpdatePlacementPolicy(ctx, req)
	if err != nil {
		return actions.FailedActionResult(err), nil
	}

	outputs := map[opent.OutputName]string{}
	if policy.GetPlacementPolicyId() != "" {
		outputs[opent.OutputPlacementPolicyID] = policy.GetPlacementPolicyId()
	}

	return actions.RunningActionResult(fmt.Sprintf("sent request to update placement policy: %s", placementPolicyIDForMessage(policy, req.GetPlacementPolicyId())), outputs), nil
}

func (a *updatePlacementPolicy) Result(ctx context.Context, instance *opent.Instance) (*actions.ActionResult, error) {
	return actions.SucceededActionResult(nil), nil
}

func (a *updatePlacementPolicy) getParam(ctx context.Context, instance *opent.Instance) (*clusterng.UpdatePlacementPolicyRequest, *nextgenv1.ResourcePoolProfile, error) {
	req, err := buildUpdatePlacementPolicyParam(instance)
	if err != nil {
		return nil, nil, err
	}

	rp, err := a.comps.CoreSvc.Mgmt.GetResourcePoolString(ctx, req.GetSharedPoolId())
	if err != nil {
		return nil, nil, err
	}

	return req, rp, nil
}

func buildUpdatePlacementPolicyParam(instance *opent.Instance) (*clusterng.UpdatePlacementPolicyRequest, error) {
	sharedPoolID, err := actions.GetResourcePoolID(instance)
	if err != nil {
		return nil, err
	}

	placementPolicyID, err := actions.GetClusterIDStr(instance)
	if err != nil {
		return nil, err
	}

	tikvGroupID, ok := instance.GetParam(opent.ParamTiKVGroupID)
	if !ok || tikvGroupID == "" {
		return nil, fmt.Errorf("param %s is required", opent.ParamTiKVGroupID)
	}

	return &clusterng.UpdatePlacementPolicyRequest{
		SharedPoolId:      sharedPoolID,
		PlacementPolicyId: placementPolicyID,
		TikvGroupId:       tikvGroupID,
	}, nil
}
