package cluster

import (
	"context"
	"errors"
	"fmt"

	"braces.dev/errtrace"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
	"gorm.io/gorm"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	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/grpc/metadata"
)

func (svc *service) ResumeCluster(
	ctx context.Context,
	clusterId uint64,
) (*entity.Cluster, *entity.ClusterCustomStatus, error) {
	spanName := "ClusterService.ResumeCluster"
	ctx, span := tracer.Start(ctx, spanName)
	defer span.End()

	var (
		cluster *entity.Cluster
		status  *entity.ClusterCustomStatus
	)

	if clusterId == 0 {
		return nil, nil, errtrace.Wrap(errors.New("cluster id is invalid"))
	}
	if err := svc.transaction.Transaction(ctx, func(ctx context.Context) error {
		var (
			err     error
			isMutex bool
		)
		// First make sure cluster exists in database
		cluster, err = svc.clusterRepo.GetCluster(ctx, clusterId, false)
		if err != nil {
			return fmt.Errorf("failed to find cluster in database: %w", err)
		}

		if cluster.DeployType != entity.DeployTypeDedicated {
			return fmt.Errorf("cluster is not a dedicated cluster")
		}

		// check ClusterAction_CLUSTER_ACTION_resume_CLUSTER mutex
		isMutex, status, err = svc.customStatusRepo.IsMutexClusterAction(
			ctx,
			clusterId,
			tidbmgmtservicev1.ClusterAction_CLUSTER_ACTION_RESUME_CLUSTER,
		)
		if err != nil {
			if !errors.Is(err, gorm.ErrRecordNotFound) {
				return fmt.Errorf("failed to check mutex cluster action: %w", err)
			}
		}

		if isMutex {
			tilog.Error(ctx, "mutex custom status is exist", zap.Uint64("cluster_id", clusterId), zap.Any("custom status", status))
			return errtrace.Wrap(constant.ErrClusterCustomStatusForbidden)
		}

		reqMeta, err := metadata.ExtractRequestMeta(ctx)
		if err != nil {
			return fmt.Errorf("failed to extract request metadata: %w", err)
		}

		request := &globaldedicatedv1.ResumeClusterRequest{
			ClusterId:    clusterId,
			ValidateOnly: false,
		}
		dedicatedCluster, err := svc.dedicatedCli.ResumeCluster(metadata.AppendToOutgoingMeta(ctx, reqMeta), request)
		if err != nil {
			return errtrace.Wrap(err)
		}

		if dedicatedCluster == nil {
			return errtrace.Wrap(errors.New("global cluster object should not be nil"))
		}

		// Update cluster record in schema.
		err = svc.clusterRepo.TouchCluster(ctx, clusterId)
		if err != nil {
			return fmt.Errorf("failed to update cluster record in database: %w", err)
		}

		cluster = cvtDedicatedCluster(cluster, dedicatedCluster)

		return nil
	}); err != nil {
		return cluster, status, errtrace.Wrap(err)
	}

	return cluster, nil, nil
}
