package fsregion

import (
	"context"
	"testing"

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

	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/repository/mock_repository"
)

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

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

	fsRegionRepo.EXPECT().List(gomock.Any(), "", "").DoAndReturn(
		func(_ context.Context, cloudProvider, status string) ([]*entity.FsRegion, error) {
			require.Empty(t, cloudProvider)
			require.Empty(t, 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:         "ap-southeast-1",
					DisplayName:    "Singapore (ap-southeast-1)",
					ServiceAddress: "http://dev.aws-ap-southeast-1.drive9.ai",
					Status:         "INACTIVE",
				},
			}, nil
		},
	)

	resp, err := server.ListFsRegions(context.Background(), &tidbmgmtservicev1.ListFsRegionsRequest{})
	require.NoError(t, err)
	require.Len(t, resp.GetRegions(), 2)
	// The internal API returns all regions, including INACTIVE ones.
	require.Equal(t, "INACTIVE", resp.GetRegions()[1].GetStatus())
}

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

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

	fsRegionRepo.EXPECT().Get(gomock.Any(), "aws", "ap-southeast-1").DoAndReturn(
		func(_ context.Context, cloudProvider, region string) (*entity.FsRegion, error) {
			require.Equal(t, "aws", cloudProvider)
			require.Equal(t, "ap-southeast-1", region)
			return &entity.FsRegion{
				CloudProvider:  "aws",
				Region:         "ap-southeast-1",
				DisplayName:    "Singapore (ap-southeast-1)",
				ServiceAddress: "http://dev.aws-ap-southeast-1.drive9.ai",
				Status:         "INACTIVE",
			}, nil
		},
	)

	// The internal API returns the region regardless of its status.
	resp, err := server.GetFsRegion(context.Background(), &tidbmgmtservicev1.GetFsRegionRequest{RegionId: "aws-ap-southeast-1"})
	require.NoError(t, err)
	require.Equal(t, "aws-ap-southeast-1", resp.GetRegionId())
	require.Equal(t, "INACTIVE", resp.GetStatus())
}

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

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

	_, err := server.GetFsRegion(context.Background(), &tidbmgmtservicev1.GetFsRegionRequest{RegionId: "invalid"})
	require.Error(t, err)
}

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

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

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

	resp, err := server.CreateFsRegion(context.Background(), &tidbmgmtservicev1.CreateFsRegionRequest{
		Region: &tidbmgmtservicev1.FsRegion{
			RegionId:       "aws-us-west-2",
			DisplayName:    "Oregon (us-west-2)",
			ServiceAddress: "https://fs-us-west-2.tidbcloud.com",
			Status:         "active",
		},
	})
	require.NoError(t, err)
	require.Equal(t, "aws-us-west-2", resp.GetRegionId())
	require.Equal(t, "Oregon (us-west-2)", resp.GetDisplayName())
	require.Equal(t, "https://fs-us-west-2.tidbcloud.com", resp.GetServiceAddress())
	require.Equal(t, "ACTIVE", resp.GetStatus())
}

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

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

	fsRegionRepo.EXPECT().Create(gomock.Any(), gomock.Any()).DoAndReturn(
		func(_ context.Context, region *entity.FsRegion) (*entity.FsRegion, error) {
			require.Equal(t, entity.FsRegionStatusActive, region.Status)
			return region, nil
		},
	)

	resp, err := server.CreateFsRegion(context.Background(), &tidbmgmtservicev1.CreateFsRegionRequest{
		Region: &tidbmgmtservicev1.FsRegion{
			RegionId:       "aws-us-west-2",
			ServiceAddress: "https://fs-us-west-2.tidbcloud.com",
		},
	})
	require.NoError(t, err)
	require.Equal(t, "ACTIVE", resp.GetStatus())
}

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

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

	_, err := server.CreateFsRegion(context.Background(), &tidbmgmtservicev1.CreateFsRegionRequest{
		Region: &tidbmgmtservicev1.FsRegion{
			RegionId: "aws-us-west-2",
			Status:   "DELETED",
		},
	})
	require.Error(t, err)
}

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

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

	_, err := server.CreateFsRegion(context.Background(), &tidbmgmtservicev1.CreateFsRegionRequest{
		Region: &tidbmgmtservicev1.FsRegion{RegionId: "invalid"},
	})
	require.Error(t, err)
}

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

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

	_, err := server.CreateFsRegion(context.Background(), &tidbmgmtservicev1.CreateFsRegionRequest{})
	require.Error(t, err)
}

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

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

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

	resp, err := server.UpdateFsRegion(context.Background(), &tidbmgmtservicev1.UpdateFsRegionRequest{
		RegionId: "aws-us-west-2",
		Region: &tidbmgmtservicev1.FsRegion{
			DisplayName:    "Oregon",
			ServiceAddress: "https://fs-us-west-2-new.tidbcloud.com",
		},
	})
	require.NoError(t, err)
	require.Equal(t, "aws-us-west-2", resp.GetRegionId())
	require.Equal(t, "Oregon", resp.GetDisplayName())
	require.Equal(t, "https://fs-us-west-2-new.tidbcloud.com", resp.GetServiceAddress())
}

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

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

	_, err := server.UpdateFsRegion(context.Background(), &tidbmgmtservicev1.UpdateFsRegionRequest{
		RegionId: "invalid",
		Region:   &tidbmgmtservicev1.FsRegion{ServiceAddress: "https://fs.tidbcloud.com"},
	})
	require.Error(t, err)
}
