package common

import (
	"context"
	"strconv"

	"google.golang.org/grpc/codes"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	resourcepoolrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/resourcepool"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/resourcepoolpolicy"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func CheckClusterResourcePoolLifecycle(
	ctx context.Context,
	repo resourcepoolrepo.Repo,
	cluster *entity.Cluster,
	operation resourcepoolpolicy.Operation,
) bizerr.BizError {
	return requireClusterResourcePoolLifecycle(ctx, repo, cluster, operation, false)
}

func LockClusterResourcePoolLifecycle(
	ctx context.Context,
	repo resourcepoolrepo.Repo,
	cluster *entity.Cluster,
	operation resourcepoolpolicy.Operation,
) bizerr.BizError {
	return requireClusterResourcePoolLifecycle(ctx, repo, cluster, operation, true)
}

func requireClusterResourcePoolLifecycle(
	ctx context.Context,
	repo resourcepoolrepo.Repo,
	cluster *entity.Cluster,
	operation resourcepoolpolicy.Operation,
	forUpdate bool,
) bizerr.BizError {
	resourcePoolID, err := strconv.ParseUint(cluster.GetSharedPoolId(), 10, 64)
	if err != nil || resourcePoolID == 0 {
		return bizerr.New(bizerr.ErrCodeUnknown, "cluster has an invalid resource pool id")
	}
	var resourcePool *entity.ResourcePool
	var berr bizerr.BizError
	if forUpdate {
		resourcePool, berr = repo.LoadForUpdate(ctx, resourcePoolID, false)
	} else {
		resourcePool, berr = repo.Load(ctx, resourcePoolID, false)
	}
	if berr != nil {
		return berr
	}
	return RequireResourcePoolLifecycle(
		resourcePool,
		operation,
	)
}

func RequireResourcePoolLifecycle(
	resourcePool *entity.ResourcePool,
	operation resourcepoolpolicy.Operation,
) bizerr.BizError {
	if resourcePool == nil {
		return bizerr.New(bizerr.ErrCodeParams, "resource pool is required")
	}
	return RequireResourcePoolLifecycleState(resourcePool.GetLifecycleState(), operation)
}

func RequireResourcePoolLifecycleState(
	state globalngv1.ResourcePool_LifecycleState,
	operation resourcepoolpolicy.Operation,
) bizerr.BizError {
	decision := resourcepoolpolicy.Decide(operation, state)
	if decision == resourcepoolpolicy.DecisionAllowed {
		return nil
	}
	return bizerr.New(
		bizerr.ErrCode(codes.FailedPrecondition),
		"operation is not allowed while resource pool lifecycle state is %s",
		state.String(),
	)
}
