package clusteraction

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	apiv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/cluster-agent/v1"

	opent "github.com/tidbcloud/devops/entity/operation"
	"github.com/tidbcloud/devops/pkg/service/operation/actions"
	mgmtv1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
)

var (
	ActionRestart = opent.ActionMetadata{
		Name: "Restart",
		Type: opent.ActionTypeAuto,
	}

	restartTargetMap = map[string]apiv1.RestartTargetType{
		"pod":       apiv1.RestartTargetType_POD,
		"node":      apiv1.RestartTargetType_NODE,
		"component": apiv1.RestartTargetType_COMPONENT,
		"cluster":   apiv1.RestartTargetType_CLUSTER,
	}
)

var (
	_ actions.Action = &restart{}
)

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

func NewRestart(comps *actions.Components) actions.AutoAction {
	return &restart{comps: comps}
}

func (a *restart) Metadata() opent.ActionMetadata {
	return ActionRestart
}

func (a *restart) PreCheck(ctx context.Context, instance *opent.Instance) error {
	cluster, err := actions.CheckClusterExistence(ctx, a.comps.CoreSvc, instance)
	if err != nil {
		return err
	}

	if cluster.DeployType != mgmtv1.DeployType_DEPLOY_TYPE_DEDICATED {
		return fmt.Errorf("unsupported deploy type: %s", cluster.DeployType.String())
	}

	_, err = a.getParam(instance)
	if err != nil {
		return err
	}

	return nil
}

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

	resp, err := a.comps.MgmtHTTP.ClusterAgent(req.ClusterId).Restart(ctx, req)
	if err != nil {
		return actions.FailedActionResult(err), nil
	}

	return actions.RunningActionResult(fmt.Sprintf("sent request to restart %s: %s", instance.Params[opent.ParamRestartTargetType], resp.Message), nil), nil
}

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

func (a *restart) getParam(instance *opent.Instance) (*apiv1.RestartReq, error) {
	cid := instance.Params[opent.ParamClusterID]
	if cid == "" {
		return nil, fmt.Errorf("cluster_id is required")
	}

	tt, ok := restartTargetMap[instance.Params[opent.ParamRestartTargetType]]
	if !ok {
		return nil, fmt.Errorf("invalid restart target type: %s", instance.Params[opent.ParamRestartTargetType])
	}

	targetName := instance.Params[opent.ParamRestartTarget]
	// Add a prefix "db-" to targetName if users want to restart a pod or node but the targetName does not have the prefix.
	if (tt == apiv1.RestartTargetType_POD || tt == apiv1.RestartTargetType_NODE) && !strings.HasPrefix(targetName, "db-") {
		targetName = fmt.Sprintf("db-%s", targetName)
	}

	p := &apiv1.RestartReq{
		OperationId: strconv.FormatUint(instance.ID, 10),
		ClusterId:   cid,
		TargetType:  tt,
		TargetName:  targetName,
		Force:       instance.Params[opent.ParamForceRestart] == "true",
	}
	return p, nil
}
