package grpcli

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	global_commonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	tidb_mgmt_servicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"google.golang.org/grpc"
)

func TestRegionName2ID(t *testing.T) {
	testcases := []struct {
		regionName       string
		expectedRegionID global_commonv1.Region_Name
	}{
		{
			regionName:       "",
			expectedRegionID: global_commonv1.Region_NAME_UNSPECIFIED,
		},
		{
			regionName:       "abc",
			expectedRegionID: global_commonv1.Region_NAME_UNSPECIFIED,
		},
		{
			regionName:       "us-west-2",
			expectedRegionID: global_commonv1.Region_AWS_US_WEST_2,
		},
		{
			regionName:       "us-east-1",
			expectedRegionID: global_commonv1.Region_AWS_US_EAST_1,
		},
		{
			regionName:       "asia-east1",
			expectedRegionID: global_commonv1.Region_GCP_ASIA_EAST1,
		},
		{
			regionName:       "us-central1",
			expectedRegionID: global_commonv1.Region_GCP_US_CENTRAL1,
		},
		{
			regionName:       "eastus",
			expectedRegionID: global_commonv1.Region_AZURE_EASTUS,
		},
		{
			regionName:       "asia",
			expectedRegionID: global_commonv1.Region_AZURE_ASIA,
		},
	}

	for _, tc := range testcases {
		regionID := RegionName2ID(tc.regionName)
		assert.Equal(t, tc.expectedRegionID, regionID)
	}
}

func TestRegionFromString(t *testing.T) {
	testcases := map[string]struct {
		cloudProvider    string
		regionName       string
		expectedRegionID global_commonv1.Region_Name
		wantErr          bool
	}{
		"aws provider matches enum prefix": {
			cloudProvider:    "aws",
			regionName:       "us-west-2",
			expectedRegionID: global_commonv1.Region_AWS_US_WEST_2,
		},
		"aliyun provider maps to alicloud enum prefix": {
			cloudProvider:    "aliyun",
			regionName:       "ap-southeast-1",
			expectedRegionID: global_commonv1.Region_ALICLOUD_AP_SOUTHEAST_1,
		},
		"alicloud provider remains supported": {
			cloudProvider:    "alicloud",
			regionName:       "ap-southeast-1",
			expectedRegionID: global_commonv1.Region_ALICLOUD_AP_SOUTHEAST_1,
		},
		"ack provider maps to alicloud enum prefix": {
			cloudProvider:    "ack",
			regionName:       "ap-southeast-1",
			expectedRegionID: global_commonv1.Region_ALICLOUD_AP_SOUTHEAST_1,
		},
		"invalid provider returns error": {
			cloudProvider:    "invalid",
			regionName:       "ap-southeast-1",
			expectedRegionID: global_commonv1.Region_NAME_UNSPECIFIED,
			wantErr:          true,
		},
	}

	for name, tc := range testcases {
		t.Run(name, func(t *testing.T) {
			regionID, err := RegionFromString(tc.cloudProvider, tc.regionName)
			if tc.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
			assert.Equal(t, tc.expectedRegionID, regionID)
		})
	}
}

func TestCloudProviderFromString(t *testing.T) {
	testcases := map[string]struct {
		cloudProvider           string
		expectedCloudProviderID global_commonv1.CloudProvider_Name
	}{
		"aws provider": {
			cloudProvider:           "aws",
			expectedCloudProviderID: global_commonv1.CloudProvider_AWS,
		},
		"eks provider": {
			cloudProvider:           "eks",
			expectedCloudProviderID: global_commonv1.CloudProvider_AWS,
		},
		"aliyun provider": {
			cloudProvider:           "aliyun",
			expectedCloudProviderID: global_commonv1.CloudProvider_ALICLOUD,
		},
		"ack provider": {
			cloudProvider:           "ack",
			expectedCloudProviderID: global_commonv1.CloudProvider_ALICLOUD,
		},
		"invalid provider": {
			cloudProvider:           "invalid",
			expectedCloudProviderID: global_commonv1.CloudProvider_NAME_UNSPECIFIED,
		},
	}

	for name, tc := range testcases {
		t.Run(name, func(t *testing.T) {
			assert.Equal(t, tc.expectedCloudProviderID, CloudProviderFromString(tc.cloudProvider))
		})
	}
}

func TestGetRegionByTenantIDWithOptionsBuildsNextGenPremiumRequest(t *testing.T) {
	regionSvc := &recordingRegionServiceClient{}
	regionCli := &regionGrpcClientImpl{client: regionSvc}

	_, err := regionCli.GetRegionByTenantIDWithOptions(
		context.Background(),
		42,
		"ack",
		"ap-southeast-1",
		RegionLookupOptions{
			DeployType:  tidb_mgmt_servicev1.DeployType_DEPLOY_TYPE_NEXTGEN,
			ServicePlan: global_commonv1.ServicePlan_Premium,
		},
	)
	assert.NoError(t, err)
	assert.NotNil(t, regionSvc.selectRegionReq)
	assert.Equal(t, tidb_mgmt_servicev1.DeployType_DEPLOY_TYPE_NEXTGEN, regionSvc.selectRegionReq.GetDeployType())
	assert.Equal(t, global_commonv1.CloudProvider_ALICLOUD, regionSvc.selectRegionReq.GetCloudProviderId())
	assert.Equal(t, global_commonv1.Region_ALICLOUD_AP_SOUTHEAST_1, regionSvc.selectRegionReq.GetRegionId())
	assert.Equal(t, uint64(42), regionSvc.selectRegionReq.GetOrganizationId())
	assert.Equal(t, global_commonv1.ServicePlan_Premium, regionSvc.selectRegionReq.GetServicePlan())
}

func TestGetRegionByTenantIDBuildsDedicatedRequestByDefault(t *testing.T) {
	regionSvc := &recordingRegionServiceClient{}
	regionCli := &regionGrpcClientImpl{client: regionSvc}

	_, err := regionCli.GetRegionByTenantID(context.Background(), 42, "aws", "us-west-2")
	assert.NoError(t, err)
	assert.NotNil(t, regionSvc.selectRegionReq)
	assert.Equal(t, tidb_mgmt_servicev1.DeployType_DEPLOY_TYPE_DEDICATED, regionSvc.selectRegionReq.GetDeployType())
	assert.Equal(t, global_commonv1.CloudProvider_NAME_UNSPECIFIED, regionSvc.selectRegionReq.GetCloudProviderId())
	assert.Equal(t, global_commonv1.Region_AWS_US_WEST_2, regionSvc.selectRegionReq.GetRegionId())
	assert.Equal(t, global_commonv1.ServicePlan_SERVICE_PLAN_UNSPECIFIED, regionSvc.selectRegionReq.GetServicePlan())
}

type recordingRegionServiceClient struct {
	tidb_mgmt_servicev1.RegionServiceClient

	selectRegionReq *tidb_mgmt_servicev1.SelectRegionByOrganizationIdRequest
}

func (c *recordingRegionServiceClient) SelectRegionByOrganizationId(
	_ context.Context,
	req *tidb_mgmt_servicev1.SelectRegionByOrganizationIdRequest,
	_ ...grpc.CallOption,
) (*tidb_mgmt_servicev1.Region, error) {
	c.selectRegionReq = req
	return &tidb_mgmt_servicev1.Region{
		Region: &global_commonv1.Region{
			State: global_commonv1.Region_ACTIVE,
		},
	}, nil
}
