package br

import (
	"context"

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

func lockResourcePoolForRestore(
	ctx context.Context,
	resourcePoolRepo resourcepoolrepo.Repo,
	clusterRepo clusterrepo.Repo,
	sharedPoolID uint64,
	targetClusterID uint64,
) bizerr.BizError {
	lifecycle, berr := resourcePoolRepo.LoadLifecycleForUpdate(ctx, sharedPoolID, false)
	if berr != nil {
		return berr.Chain("failed to lock shared pool: %d", sharedPoolID)
	}
	decision := resourcepoolpolicy.Decide(
		resourcepoolpolicy.OperationRestoreInstance,
		lifecycle.State,
	)
	if decision == resourcepoolpolicy.DecisionAllowed ||
		(decision == resourcepoolpolicy.DecisionTriggerOrReuseResume &&
			lifecycle.State == globalngv1.ResourcePool_RESUMING) {
		if _, berr := clusterRepo.Get(ctx, targetClusterID, false); berr != nil {
			return berr.Chain("failed to reload target cluster: %d", targetClusterID)
		}
		return nil
	}
	return bizerr.New(
		bizerr.ErrCodeParams,
		"restore is not allowed while resource pool lifecycle state is %s",
		lifecycle.State.String(),
	)
}

func (s *serviceImpl) checkResourcePoolForBRMutation(
	ctx context.Context,
	sharedPoolID uint64,
	operation resourcepoolpolicy.Operation,
) bizerr.BizError {
	resourcePool, berr := s.resourcePoolRepo.Load(ctx, sharedPoolID, false)
	if berr != nil {
		return berr.Chain("failed to load shared pool: %d", sharedPoolID)
	}
	return servicecommon.RequireResourcePoolLifecycle(
		resourcePool,
		operation,
	)
}

// lockResourcePoolForBRMutation serializes BR mutations with Global
// Pause/Resume admission, which locks the same ResourcePool row before changing
// its lifecycle state.
func (s *serviceImpl) lockResourcePoolForBRMutation(ctx context.Context, sharedPoolID uint64) bizerr.BizError {
	return s.lockResourcePoolForBRMutationWithOperation(
		ctx,
		sharedPoolID,
		resourcepoolpolicy.OperationManualBackup,
	)
}

func (s *serviceImpl) lockResourcePoolForBRMutationWithOperation(
	ctx context.Context,
	sharedPoolID uint64,
	operation resourcepoolpolicy.Operation,
) bizerr.BizError {
	lifecycle, berr := s.resourcePoolRepo.LoadLifecycleForUpdate(ctx, sharedPoolID, false)
	if berr != nil {
		return berr.Chain("failed to lock shared pool: %d", sharedPoolID)
	}

	return servicecommon.RequireResourcePoolLifecycleState(
		lifecycle.State,
		operation,
	)
}

func (s *serviceImpl) lockResourcePoolForScheduledBackupMutation(
	ctx context.Context,
	sharedPoolID uint64,
) bizerr.BizError {
	lifecycle, berr := s.resourcePoolRepo.LoadLifecycleForUpdate(ctx, sharedPoolID, false)
	if berr != nil {
		return berr.Chain("failed to lock shared pool: %d", sharedPoolID)
	}
	return servicecommon.RequireResourcePoolLifecycleState(
		lifecycle.State,
		resourcepoolpolicy.OperationScheduledBackup,
	)
}

// lockResourcePoolForManualBackupDeletion serializes deletion with Pause
// admission while allowing archived backups to be cleaned up after their
// ResourcePool has been soft-deleted.
func (s *serviceImpl) lockResourcePoolForManualBackupDeletion(
	ctx context.Context,
	sharedPoolID uint64,
) bizerr.BizError {
	lifecycle, berr := s.resourcePoolRepo.LoadLifecycleForUpdate(ctx, sharedPoolID, true)
	if berr != nil {
		return berr.Chain("failed to lock shared pool for manual backup deletion: %d", sharedPoolID)
	}
	if lifecycle.IsDeleted {
		return nil
	}

	return servicecommon.RequireResourcePoolLifecycleState(
		lifecycle.State,
		resourcepoolpolicy.OperationManualBackup,
	)
}
