package resourcepool

import (
	"testing"

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

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/cluster/mock_cluster"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/region/mock_region"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/resourcepool/mock_resourcepool"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func TestDeleteResourcePoolLocksBeforeAttachmentRecheck(t *testing.T) {
	ctrl := gomock.NewController(t)
	resourcePoolRepo := mock_resourcepool.NewMockRepo(ctrl)
	clusterRepo := mock_cluster.NewMockRepo(ctrl)
	regionRepo := mock_region.NewMockRepo(ctrl)
	pool := &entity.ResourcePool{
		ResourcePool: &globalngv1.ResourcePool{
			Id:             42,
			OwnerType:      globalngv1.ResourcePool_ORGANIZATION,
			OwnerId:        200,
			ProviderId:     globalcommonv1.CloudProvider_AWS,
			RegionId:       globalcommonv1.Region_AWS_US_EAST_1,
			ServicePlan:    globalcommonv1.ServicePlan_BYOC,
			LifecycleState: globalngv1.ResourcePool_ACTIVE,
		},
	}
	svc := &serviceImpl{
		transaction:      resourcePoolPassthroughTransactioner{},
		resourcePoolRepo: resourcePoolRepo,
		clusterRepo:      clusterRepo,
		regionRepo:       regionRepo,
	}

	resourcePoolRepo.EXPECT().Load(gomock.Any(), uint64(42), false).Return(pool, nil)
	regionRepo.EXPECT().SelectRegion(gomock.Any(), gomock.Any()).Return(&entity.Region{}, nil)
	gomock.InOrder(
		resourcePoolRepo.EXPECT().LoadForUpdate(gomock.Any(), uint64(42), false).Return(pool, nil),
		clusterRepo.EXPECT().
			Query(gomock.Any(), gomock.Any()).
			Return([]*entity.Cluster{{Cluster: &globalngv1.Cluster{ClusterId: 100}}}, nil),
	)

	deleted, err := svc.DeleteResourcePool(t.Context(), &globalngv1.DeleteResourcePoolRequest{
		ResourcePoolId: 42,
	})

	require.Nil(t, deleted)
	require.ErrorContains(t, err, "used by some clusters")
	var businessError bizerr.BizError
	require.ErrorAs(t, err, &businessError)
	require.Equal(t, bizerr.ErrCode(codes.FailedPrecondition), businessError.ErrCode())
}

func TestValidateDeleteResourcePoolWithAttachmentsReturnsFailedPrecondition(t *testing.T) {
	ctrl := gomock.NewController(t)
	resourcePoolRepo := mock_resourcepool.NewMockRepo(ctrl)
	clusterRepo := mock_cluster.NewMockRepo(ctrl)
	regionRepo := mock_region.NewMockRepo(ctrl)
	pool := &entity.ResourcePool{
		ResourcePool: &globalngv1.ResourcePool{
			Id:             42,
			OwnerType:      globalngv1.ResourcePool_ORGANIZATION,
			OwnerId:        200,
			ProviderId:     globalcommonv1.CloudProvider_AWS,
			RegionId:       globalcommonv1.Region_AWS_US_EAST_1,
			ServicePlan:    globalcommonv1.ServicePlan_BYOC,
			LifecycleState: globalngv1.ResourcePool_ACTIVE,
		},
	}
	svc := &serviceImpl{
		resourcePoolRepo: resourcePoolRepo,
		clusterRepo:      clusterRepo,
		regionRepo:       regionRepo,
	}

	resourcePoolRepo.EXPECT().Load(gomock.Any(), uint64(42), false).Return(pool, nil)
	regionRepo.EXPECT().SelectRegion(gomock.Any(), gomock.Any()).Return(&entity.Region{}, nil)
	clusterRepo.EXPECT().
		Query(gomock.Any(), gomock.Any()).
		Return([]*entity.Cluster{{Cluster: &globalngv1.Cluster{ClusterId: 100}}}, nil)

	deleted, err := svc.DeleteResourcePool(t.Context(), &globalngv1.DeleteResourcePoolRequest{
		ResourcePoolId: 42,
		ValidateOnly:   true,
	})

	require.Nil(t, deleted)
	require.ErrorContains(t, err, "used by some clusters")
	var businessError bizerr.BizError
	require.ErrorAs(t, err, &businessError)
	require.Equal(t, bizerr.ErrCode(codes.FailedPrecondition), businessError.ErrCode())
}

func TestValidateDeleteEmptyResourcePoolAllowsPauseResumeLifecycles(t *testing.T) {
	for _, lifecycle := range []globalngv1.ResourcePool_LifecycleState{
		globalngv1.ResourcePool_ACTIVE,
		globalngv1.ResourcePool_PAUSING,
		globalngv1.ResourcePool_PAUSED,
		globalngv1.ResourcePool_RESUMING,
	} {
		t.Run(lifecycle.String(), func(t *testing.T) {
			ctrl := gomock.NewController(t)
			resourcePoolRepo := mock_resourcepool.NewMockRepo(ctrl)
			clusterRepo := mock_cluster.NewMockRepo(ctrl)
			regionRepo := mock_region.NewMockRepo(ctrl)
			pool := &entity.ResourcePool{ResourcePool: &globalngv1.ResourcePool{
				Id:             42,
				LifecycleState: lifecycle,
			}}
			resourcePoolRepo.EXPECT().Load(gomock.Any(), uint64(42), false).Return(pool, nil)
			regionRepo.EXPECT().SelectRegion(gomock.Any(), gomock.Any()).Return(&entity.Region{}, nil)
			clusterRepo.EXPECT().Query(gomock.Any(), gomock.Any()).Return(nil, nil)
			svc := &serviceImpl{
				resourcePoolRepo: resourcePoolRepo,
				clusterRepo:      clusterRepo,
				regionRepo:       regionRepo,
			}

			result, err := svc.DeleteResourcePool(t.Context(), &globalngv1.DeleteResourcePoolRequest{
				ResourcePoolId: 42,
				ValidateOnly:   true,
			})

			require.NoError(t, err)
			require.Equal(t, pool, result)
		})
	}
}
