package region

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"
	"go.uber.org/mock/gomock"
	"google.golang.org/protobuf/proto"

	openapicommonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	openapifsv1beta2 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/fs/v1beta2"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/repository/mock_repository"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

func testCtx(t *testing.T) context.Context {
	t.Helper()
	return metadata.AppendToIncomingMeta(t.Context(), &metadata.RequestMeta{
		OrgID:             1001,
		UserID:            "user-1",
		DefaultProjectID:  2001,
		ValidProjectList:  []uint64{2001},
		IsCheckPermission: proto.Bool(true),
	})
}

func TestListRegions(t *testing.T) {
	t.Parallel()

	ctrl := gomock.NewController(t)
	fsRegionRepo := mock_repository.NewMockFsRegionRepository(ctrl)
	server := NewServer(fsRegionRepo)

	fsRegionRepo.EXPECT().List(gomock.Any(), "", entity.FsRegionStatusActive).DoAndReturn(
		func(_ context.Context, cloudProvider, status string) ([]*entity.FsRegion, error) {
			require.Empty(t, cloudProvider)
			require.Equal(t, entity.FsRegionStatusActive, status)
			return []*entity.FsRegion{
				{
					CloudProvider:  "aws",
					Region:         "us-west-2",
					DisplayName:    "Oregon (us-west-2)",
					ServiceAddress: "https://fs-us-west-2.tidbcloud.com",
					Status:         "ACTIVE",
				},
				{
					CloudProvider:  "aws",
					Region:         "us-east-1",
					DisplayName:    "N. Virginia (us-east-1)",
					ServiceAddress: "https://fs-us-east-1.tidbcloud.com",
					Status:         "ACTIVE",
				},
			}, nil
		},
	)

	resp, err := server.ListRegions(testCtx(t), &openapifsv1beta2.ListRegionsRequest{})
	require.NoError(t, err)
	require.Equal(t, int32(2), resp.GetTotalSize())
	require.Len(t, resp.GetRegions(), 2)

	first := resp.GetRegions()[0]
	require.Equal(t, "regions/aws-us-east-1", first.GetName())
	require.Equal(t, "aws-us-east-1", first.GetRegionId())
	require.Equal(t, openapicommonv1beta1.Region_aws, first.GetCloudProvider())
	require.Equal(t, "N. Virginia (us-east-1)", first.GetDisplayName())
	require.Equal(t, "https://fs-us-east-1.tidbcloud.com", first.GetServiceAddress())
	require.Equal(t, openapifsv1beta2.Region_ACTIVE, first.GetState())
	require.Equal(t, openapifsv1beta2.Region_ACTIVE, resp.GetRegions()[1].GetState())
}

func TestListRegionsWithCloudProviderFilter(t *testing.T) {
	t.Parallel()

	ctrl := gomock.NewController(t)
	fsRegionRepo := mock_repository.NewMockFsRegionRepository(ctrl)
	server := NewServer(fsRegionRepo)

	fsRegionRepo.EXPECT().List(gomock.Any(), "aws", entity.FsRegionStatusActive).DoAndReturn(
		func(_ context.Context, cloudProvider, status string) ([]*entity.FsRegion, error) {
			require.Equal(t, "aws", cloudProvider)
			require.Equal(t, entity.FsRegionStatusActive, status)
			return []*entity.FsRegion{
				{
					CloudProvider:  "aws",
					Region:         "us-west-2",
					DisplayName:    "Oregon (us-west-2)",
					ServiceAddress: "https://fs-us-west-2.tidbcloud.com",
				},
			}, nil
		},
	)

	resp, err := server.ListRegions(testCtx(t), &openapifsv1beta2.ListRegionsRequest{CloudProvider: "aws"})
	require.NoError(t, err)
	require.Equal(t, int32(1), resp.GetTotalSize())
	require.Len(t, resp.GetRegions(), 1)
	require.Equal(t, "aws-us-west-2", resp.GetRegions()[0].GetRegionId())
}

func TestListRegionsWithInvalidCloudProvider(t *testing.T) {
	t.Parallel()

	ctrl := gomock.NewController(t)
	fsRegionRepo := mock_repository.NewMockFsRegionRepository(ctrl)
	server := NewServer(fsRegionRepo)

	_, err := server.ListRegions(testCtx(t), &openapifsv1beta2.ListRegionsRequest{CloudProvider: "invalid"})
	require.Error(t, err)
}

func TestGetRegion(t *testing.T) {
	t.Parallel()

	ctrl := gomock.NewController(t)
	fsRegionRepo := mock_repository.NewMockFsRegionRepository(ctrl)
	server := NewServer(fsRegionRepo)

	fsRegionRepo.EXPECT().Get(gomock.Any(), "aws", "us-west-2").DoAndReturn(
		func(_ context.Context, cloudProvider, region string) (*entity.FsRegion, error) {
			require.Equal(t, "aws", cloudProvider)
			require.Equal(t, "us-west-2", region)
			return &entity.FsRegion{
				CloudProvider:  "aws",
				Region:         "us-west-2",
				DisplayName:    "Oregon (us-west-2)",
				ServiceAddress: "https://fs-us-west-2.tidbcloud.com",
				Status:         "ACTIVE",
			}, nil
		},
	)

	resp, err := server.GetRegion(testCtx(t), &openapifsv1beta2.GetRegionRequest{RegionId: "aws-us-west-2"})
	require.NoError(t, err)
	require.Equal(t, "regions/aws-us-west-2", resp.GetName())
	require.Equal(t, "aws-us-west-2", resp.GetRegionId())
	require.Equal(t, openapicommonv1beta1.Region_aws, resp.GetCloudProvider())
	require.Equal(t, "Oregon (us-west-2)", resp.GetDisplayName())
	require.Equal(t, "https://fs-us-west-2.tidbcloud.com", resp.GetServiceAddress())
	require.Equal(t, openapifsv1beta2.Region_ACTIVE, resp.GetState())
}

func TestGetRegionInactive(t *testing.T) {
	t.Parallel()

	ctrl := gomock.NewController(t)
	fsRegionRepo := mock_repository.NewMockFsRegionRepository(ctrl)
	server := NewServer(fsRegionRepo)

	fsRegionRepo.EXPECT().Get(gomock.Any(), "aws", "us-west-2").DoAndReturn(
		func(_ context.Context, _, _ string) (*entity.FsRegion, error) {
			return &entity.FsRegion{
				CloudProvider:  "aws",
				Region:         "us-west-2",
				DisplayName:    "Oregon (us-west-2)",
				ServiceAddress: "https://fs-us-west-2.tidbcloud.com",
				Status:         "INACTIVE",
			}, nil
		},
	)

	_, err := server.GetRegion(testCtx(t), &openapifsv1beta2.GetRegionRequest{RegionId: "aws-us-west-2"})
	require.Error(t, err)
}

func TestGetRegionWithInvalidRegionId(t *testing.T) {
	t.Parallel()

	ctrl := gomock.NewController(t)
	fsRegionRepo := mock_repository.NewMockFsRegionRepository(ctrl)
	server := NewServer(fsRegionRepo)

	_, err := server.GetRegion(testCtx(t), &openapifsv1beta2.GetRegionRequest{RegionId: "invalid"})
	require.Error(t, err)
}
