package br

import (
	"testing"

	"github.com/stretchr/testify/require"
	"go.uber.org/mock/gomock"
	"google.golang.org/grpc/codes"

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

func TestLockResourcePoolForBRMutation(t *testing.T) {
	tests := []struct {
		name    string
		state   globalngv1.ResourcePool_LifecycleState
		blocked bool
	}{
		{name: "active", state: globalngv1.ResourcePool_ACTIVE},
		{name: "modifying", state: globalngv1.ResourcePool_MODIFYING},
		{name: "pausing", state: globalngv1.ResourcePool_PAUSING, blocked: true},
		{name: "paused", state: globalngv1.ResourcePool_PAUSED, blocked: true},
		{name: "resuming", state: globalngv1.ResourcePool_RESUMING, blocked: true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctrl := gomock.NewController(t)
			repo := mockresourcepool.NewMockRepo(ctrl)
			repo.EXPECT().LoadLifecycleForUpdate(gomock.Any(), uint64(42), false).Return(&resourcepoolrepo.Lifecycle{
				State: tt.state,
			}, nil)
			svc := &serviceImpl{resourcePoolRepo: repo}

			err := svc.lockResourcePoolForBRMutation(t.Context(), 42)
			if !tt.blocked {
				require.NoError(t, err)
				return
			}
			require.Error(t, err)
			require.Equal(t, bizerr.ErrCode(codes.FailedPrecondition), err.ErrCode())
			require.ErrorContains(t, err, tt.state.String())
		})
	}
}

func TestLockResourcePoolForScheduledBackupMutation(t *testing.T) {
	ctrl := gomock.NewController(t)
	repo := mockresourcepool.NewMockRepo(ctrl)
	repo.EXPECT().LoadLifecycleForUpdate(gomock.Any(), uint64(42), false).Return(&resourcepoolrepo.Lifecycle{
		State: globalngv1.ResourcePool_ACTIVE,
	}, nil)

	err := (&serviceImpl{resourcePoolRepo: repo}).lockResourcePoolForScheduledBackupMutation(t.Context(), 42)
	require.NoError(t, err)
}

func TestLockResourcePoolForManualBackupDeletion(t *testing.T) {
	t.Run("living paused pool is blocked", func(t *testing.T) {
		ctrl := gomock.NewController(t)
		repo := mockresourcepool.NewMockRepo(ctrl)
		repo.EXPECT().LoadLifecycleForUpdate(gomock.Any(), uint64(42), true).Return(&resourcepoolrepo.Lifecycle{
			State: globalngv1.ResourcePool_PAUSED,
		}, nil)

		err := (&serviceImpl{resourcePoolRepo: repo}).lockResourcePoolForManualBackupDeletion(t.Context(), 42)
		require.Error(t, err)
		require.Equal(t, bizerr.ErrCode(codes.FailedPrecondition), err.ErrCode())
	})

	t.Run("soft-deleted pool is allowed regardless of stale pause state", func(t *testing.T) {
		ctrl := gomock.NewController(t)
		repo := mockresourcepool.NewMockRepo(ctrl)
		repo.EXPECT().LoadLifecycleForUpdate(gomock.Any(), uint64(42), true).Return(&resourcepoolrepo.Lifecycle{
			State:     globalngv1.ResourcePool_PAUSED,
			IsDeleted: true,
		}, nil)

		err := (&serviceImpl{resourcePoolRepo: repo}).lockResourcePoolForManualBackupDeletion(t.Context(), 42)
		require.NoError(t, err)
	})
}
