package ngoperation

import (
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"

	eventent "github.com/tidbcloud/devops/entity/event"
	opent "github.com/tidbcloud/devops/entity/operation"
	"github.com/tidbcloud/devops/pkg/service/operation/actions"
	evbus "github.com/tidbcloud/eventbus-datastruct/event"
	apiv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/cluster-agent/v1"
	mgmtv1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
)

var (
	ActionRestartNextGenClusterComponent = opent.ActionMetadata{
		Name: "RestartNextGenClusterComponent",
		Type: opent.ActionTypeAuto,
	}

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

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

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

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

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

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
	}

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

	return nil
}

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

	req, err := a.getParam(instance, cluster)
	if err != nil {
		return actions.FailedActionResult(err), nil
	}

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

	rebootInfo, _ := json.Marshal(req)
	// send event
	displayEv := &evbus.DisplayEvent{
		DisplayEventType: string(eventent.RebootEvent),
		DisplayStatus:    evbus.EventStatusSucceeded,
		Severity:         evbus.EventSeverityInfo,
		Summary:          fmt.Sprintf(string(eventent.RebootSummary), string(rebootInfo)),
		HappenedAtMs:     uint64(time.Now().UnixMilli()),
		Author:           instance.Applicant,
		TenantID:         cluster.OrganizationId,
		ProjectID:        0,
		ClusterID:        cluster.ClusterId,
	}
	err = a.comps.EventBus.DBaaSDisplayEvent.Publish(displayEv)
	if err != nil {
		a.comps.Logger.Error(err, "publish restart cluster component event failed")
	}

	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, cluster *mgmtv1.Cluster) (*apiv1.RestartReq, error) {
	if cluster.DeployType != mgmtv1.DeployType_DEPLOY_TYPE_NEXTGEN {
		return nil, fmt.Errorf("unsupported deploy type: %s", cluster.DeployType.String())
	}

	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])
	}

	group := instance.Params[opent.ParamRestartTargetGroup]
	if group == "" {
		return nil, fmt.Errorf("component_group_id is required")
	}
	if !strings.HasPrefix(group, "db-") {
		group = fmt.Sprintf("db-%s", group)
	}

	targetName := instance.Params[opent.ParamRestartTarget]
	supported := false
	isTiProxyTarget := false
	targetNameLower := strings.ToLower(targetName)
	supportedComponents := []string{"tidb", "tiproxy"}
	for _, comp := range supportedComponents {
		matched := strings.Contains(targetNameLower, comp)
		if tt == apiv1.RestartTargetType_COMPONENT {
			matched = strings.EqualFold(targetName, comp)
		}
		if !matched {
			continue
		}
		supported = true
		isTiProxyTarget = comp == "tiproxy"
		break
	}
	if !supported {
		return nil, fmt.Errorf("unsupported target '%s'", targetName)
	}
	if isTiProxyTarget {
		if err := rejectEssentialV2ClusterTiProxyAction(cluster, ActionRestartNextGenClusterComponent.Name); err != nil {
			return nil, err
		}
	}

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