package br

import (
	"testing"

	"github.com/stretchr/testify/require"
	"go.uber.org/mock/gomock"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	mockclusterrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/cluster/mock_cluster"
	mockresourcepoolrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/resourcepool/mock_resourcepool"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

func TestRestoreRejectsInvalidModeBeforeResume(t *testing.T) {
	ctrl := gomock.NewController(t)
	clusterRepo := mockclusterrepo.NewMockRepo(ctrl)
	resourcePoolRepo := mockresourcepoolrepo.NewMockRepo(ctrl)
	clusterRepo.EXPECT().Get(gomock.Any(), uint64(456), false).Return(&entity.Cluster{
		Cluster: &globalngv1.Cluster{
			ClusterId:    456,
			SharedPoolId: "123",
		},
	}, nil)
	resourcePoolRepo.EXPECT().Load(gomock.Any(), uint64(123), false).Return(&entity.ResourcePool{
		ResourcePool: &globalngv1.ResourcePool{
			Id:             123,
			LifecycleState: globalngv1.ResourcePool_PAUSED,
		},
	}, nil)

	service := &serviceImpl{clusterRepo: clusterRepo, resourcePoolRepo: resourcePoolRepo}
	err := service.Restore(t.Context(), &globalngv1.RestoreClusterRequest{
		TargetClusterId: "456",
		RestoreMode:     globalngv1.RestoreMode_RESTORE_MODE_UNSPECIFIED,
	})

	require.ErrorContains(t, err, "invalid restore mode")
}

func TestRestoreValidatesRequestBeforeResume(t *testing.T) {
	ctrl := gomock.NewController(t)
	clusterRepo := mockclusterrepo.NewMockRepo(ctrl)
	resourcePoolRepo := mockresourcepoolrepo.NewMockRepo(ctrl)
	clusterRepo.EXPECT().Get(gomock.Any(), uint64(456), false).Times(2).Return(&entity.Cluster{
		Cluster: &globalngv1.Cluster{
			ClusterId:    456,
			SharedPoolId: "123",
		},
	}, nil)
	resourcePoolRepo.EXPECT().Load(gomock.Any(), uint64(123), false).Return(&entity.ResourcePool{
		ResourcePool: &globalngv1.ResourcePool{
			Id:             123,
			LifecycleState: globalngv1.ResourcePool_PAUSED,
		},
	}, nil)

	dedicatedAdapter := &restoreFromDedicatedAdapter{
		restoreAdapterClassicBase: restoreAdapterClassicBase{clusterRepo: clusterRepo},
	}
	service := &serviceImpl{
		clusterRepo:             clusterRepo,
		resourcePoolRepo:        resourcePoolRepo,
		restoreAdapterDedicated: dedicatedAdapter,
	}
	ctx := metadata.AppendToIncomingMeta(t.Context(), &metadata.RequestMeta{OrgID: 1, UserID: "test"})
	err := service.Restore(ctx, &globalngv1.RestoreClusterRequest{
		TargetClusterId: "456",
		BackupId:        "invalid",
		RestoreMode:     globalngv1.RestoreMode_RESTORE_MODE_DEDICATED_SNAPSHOT,
	})

	require.ErrorContains(t, err, "failed to parse backup id")
}

func TestRestoreRejectsBlockedLifecycleBeforePreparation(t *testing.T) {
	ctrl := gomock.NewController(t)
	clusterRepo := mockclusterrepo.NewMockRepo(ctrl)
	resourcePoolRepo := mockresourcepoolrepo.NewMockRepo(ctrl)
	clusterRepo.EXPECT().Get(gomock.Any(), uint64(456), false).Return(&entity.Cluster{
		Cluster: &globalngv1.Cluster{
			ClusterId:    456,
			SharedPoolId: "123",
		},
	}, nil)
	resourcePoolRepo.EXPECT().Load(gomock.Any(), uint64(123), false).Return(&entity.ResourcePool{
		ResourcePool: &globalngv1.ResourcePool{
			Id:             123,
			LifecycleState: globalngv1.ResourcePool_PAUSING,
		},
	}, nil)

	dedicatedAdapter := &restoreFromDedicatedAdapter{
		restoreAdapterClassicBase: restoreAdapterClassicBase{clusterRepo: clusterRepo},
	}
	service := &serviceImpl{
		clusterRepo:             clusterRepo,
		resourcePoolRepo:        resourcePoolRepo,
		restoreAdapterDedicated: dedicatedAdapter,
	}
	err := service.Restore(t.Context(), &globalngv1.RestoreClusterRequest{
		TargetClusterId: "456",
		BackupId:        "invalid",
		RestoreMode:     globalngv1.RestoreMode_RESTORE_MODE_DEDICATED_SNAPSHOT,
	})

	require.ErrorContains(t, err, "PAUSING")
}
