package cluster

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

	openapicommonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/constant"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
	"github.com/tidbcloud/tidb-management-service/utilities/workflow"
)

var restoreClusterSpanName = "ClusterService.RestoreCluster"

func (svc *service) RestoreCluster(ctx context.Context, restoreClusterEntity *entity.RestoreCluster) error {
	ctx, span := tracer.Start(ctx, restoreClusterSpanName)
	defer span.End()

	err := svc.setRestoreCustomStatus(ctx, restoreClusterEntity)
	if err != nil {
		return err
	}
	err = svc.createRestoreDagInstance(ctx, restoreClusterEntity)
	if err != nil {
		return err
	}

	return nil
}

func (svc *service) setRestoreCustomStatus(
	ctx context.Context,
	restoreClusterEntity *entity.RestoreCluster,
) bizerr.BizError {
	clusterId, err := strconv.ParseUint(restoreClusterEntity.TargetClusterId, 10, 64)
	if err != nil {
		return bizerr.WrapErrorWithCode(err, bizerr.ErrCodeParams, "invalid cluster id")
	}
	// Then set restore custom status
	status := &entity.ClusterCustomStatus{
		StatusId:      entity.GenStatusId(),
		StatusName:    fmt.Sprintf("restore-%s", restoreClusterEntity.TargetClusterId),
		ClusterId:     clusterId,
		StatusType:    tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_RESTORE,
		OverrideState: openapicommonv1beta1.ClusterState_RESTORING.Enum(),
		CustomMutex: &tidbmgmtservicev1.ClusterCustomStatus_CustomMutex{
			StatusType: []tidbmgmtservicev1.CustomStatusType{
				tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_IMPORT,
				tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_RESTORE,
				tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_APPLY,
				tidbmgmtservicev1.CustomStatusType_CUSTOM_STATUS_TYPE_CDC,
			},
			ClusterAction: []tidbmgmtservicev1.ClusterAction{
				tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_MODIFY_CLUSTER,
				tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_PAUSE_CLUSTER,
				tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_SCALE_CLUSTER,
				tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_SET_ROOT_PASSWORD,
			},
		},
	}
	if _, err = svc.customStatusRepo.NewClusterCustomStatus(ctx, status); err != nil {
		return bizerr.WrapErrorWithCode(err, bizerr.GeneralInternalErrorCode, "failed to set cluster custom status")
	}
	return nil
}

func (svc *service) createRestoreDagInstance(
	ctx context.Context,
	restoreClusterEntity *entity.RestoreCluster,
) bizerr.BizError {
	requestJson, err := json.Marshal(restoreClusterEntity)
	if err != nil {
		return bizerr.WrapError(err, "failed to marshal restore cluster entity")
	}

	metadataJson, err := metadata.MarshalRequestMetaFromCtx(ctx)
	if err != nil {
		return bizerr.WrapError(err, "failed to marshal metadata from context")
	}

	err = svc.workflow.RunDagWithBizId(
		ctx,
		constant.DagNextGenRestoreCluster,
		restoreClusterEntity.OrgId,
		"0",
		restoreClusterEntity.TargetClusterId,
		map[string]string{
			// dag specific parameters
			constant.KeyRequest: string(requestJson),
			// common parameters
			workflow.KeyTraceId:     workflow.GetTraceId(ctx),
			workflow.KeySpanName:    restoreClusterSpanName,
			workflow.KeyRequestMeta: string(metadataJson),
		},
	)
	if err != nil {
		return bizerr.WrapError(err, "failed to run dag for restore cluster")
	}
	return nil
}
