package ngoperation

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

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

	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"
)

var (
	ActionRestartNextGenResourcePoolComponent = opent.ActionMetadata{
		Name: "RestartNextGenResourcePoolComponent",
		Type: opent.ActionTypeAuto,
	}
)

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

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

func NewRestartNextGenResourcePoolComponent(comps *actions.Components) actions.AutoAction {
	return &restartResourcePoolComp{comps: comps}
}

func (a *restartResourcePoolComp) Metadata() opent.ActionMetadata {
	return ActionRestartNextGenResourcePoolComponent
}

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

	return nil
}

func (a *restartResourcePoolComp) 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.NextGen().RestartSharedPoolComponent(ctx, req.ClusterId, req)
	if err != nil {
		return actions.FailedActionResult(err), nil
	}
	rebootInfo, _ := json.Marshal(req)
	clusterID, _ := strconv.ParseUint(req.ClusterId, 10, 64)
	rp, err := a.comps.CoreSvc.Mgmt.GetResourcePoolString(ctx, req.ClusterId)
	if err != nil {
		a.comps.Logger.Error(err, "get resource pool failed for restart resource pool event", "resource_pool_id", req.ClusterId)
	}
	tenantID, ok := resourcePoolDisplayEventTenantIDFromResourcePool(rp)
	if !ok {
		tenantID, ok = resourcePoolDisplayEventTenantIDFromParams(instance)
	}
	if !ok {
		if err != nil {
			a.comps.Logger.Error(err, "skip restart resource pool event because tenant attribution is unavailable", "resource_pool_id", req.ClusterId)
		} else {
			a.comps.Logger.Info("skip restart resource pool event because tenant attribution is unavailable", "resource_pool_id", req.ClusterId)
		}
		return actions.RunningActionResult(fmt.Sprintf("sent request: %s", resp.Message), nil), nil
	}
	// 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:         tenantID,
		ProjectID:        0,
		ClusterID:        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: %s", resp.Message), nil), nil
}

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

func (a *restartResourcePoolComp) getParam(instance *opent.Instance) (*apiv1.RestartReq, error) {
	rpID := instance.Params[opent.ParamResourcePoolID]
	if rpID == "" {
		return nil, fmt.Errorf("resource_pool_id is required")
	}

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

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

	targetName := instance.Params[opent.ParamRestartTarget]

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