package tidbxregion

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	_ "google.golang.org/protobuf/reflect/protoreflect"

	bffv1beta2 "github.com/tidbcloud/tidb-management-service/api/spec/bff/cluster_management/v1beta2"
	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	openapicommonv1beta1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/common/v1beta1"
	openapingv1beta2 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_cloud_open_api/nextgen/v1beta2"
)

type fakeNextgenRegionServer struct {
	openapingv1beta2.UnimplementedRegionServiceServer

	called bool
}

func (f *fakeNextgenRegionServer) ListRegions(
	context.Context,
	*openapingv1beta2.ListRegionsRequest,
) (*openapingv1beta2.ListRegionsResponse, error) {
	f.called = true
	return &openapingv1beta2.ListRegionsResponse{
		Regions: []*openapicommonv1beta1.Region{
			{
				Name:          "regions/aws-us-west-2",
				RegionId:      "aws-us-west-2",
				CloudProvider: openapicommonv1beta1.Region_aws,
				ServicePlans:  []openapicommonv1beta1.ServicePlan{openapicommonv1beta1.ServicePlan_Essential_V2},
			},
		},
	}, nil
}

func TestFlatViewFillsSupportedServicePlans(t *testing.T) {
	server := &RegionServer{}

	view := server.flatView(t.Context(), []*openapicommonv1beta1.Region{
		{
			Name:          "regions/aws-us-west-2",
			RegionId:      "aws-us-west-2",
			CloudProvider: openapicommonv1beta1.Region_aws,
			ServicePlans:  []openapicommonv1beta1.ServicePlan{openapicommonv1beta1.ServicePlan_Starter},
		},
		{
			Name:          "regions/aws-us-west-2",
			RegionId:      "aws-us-west-2",
			CloudProvider: openapicommonv1beta1.Region_aws,
			ServicePlans:  []openapicommonv1beta1.ServicePlan{openapicommonv1beta1.ServicePlan_Essential},
		},
	})

	assert.Len(t, view.GetRegions(), 1)
	region := view.GetRegions()[0]
	assert.ElementsMatch(t, []openapicommonv1beta1.ServicePlan{
		openapicommonv1beta1.ServicePlan_Starter,
		openapicommonv1beta1.ServicePlan_Essential,
	}, region.GetServicePlans())
	assert.ElementsMatch(t, []*openapicommonv1beta1.ServicePlanInfo{
		{
			ServicePlan:          openapicommonv1beta1.ServicePlan_Starter,
			DisplayName:          "Starter",
			SupportedEngineTypes: []openapicommonv1beta1.EngineType{openapicommonv1beta1.EngineType_MYSQL_COMPATIBLE},
		},
		{
			ServicePlan:          openapicommonv1beta1.ServicePlan_Essential,
			DisplayName:          "Essential",
			SupportedEngineTypes: []openapicommonv1beta1.EngineType{openapicommonv1beta1.EngineType_MYSQL_COMPATIBLE},
		},
	}, region.GetSupportedServicePlans())
}

func TestSetSupportedServicePlansPreservesRegionEngineTypes(t *testing.T) {
	region := &openapicommonv1beta1.Region{
		ServicePlans: []openapicommonv1beta1.ServicePlan{openapicommonv1beta1.ServicePlan_Starter},
		SupportedServicePlans: []*openapicommonv1beta1.ServicePlanInfo{
			{
				ServicePlan: openapicommonv1beta1.ServicePlan_Starter,
				SupportedEngineTypes: []openapicommonv1beta1.EngineType{
					openapicommonv1beta1.EngineType_MYSQL_COMPATIBLE,
					openapicommonv1beta1.EngineType_POSTGRESQL_COMPATIBLE,
				},
			},
		},
	}

	setSupportedServicePlans(region)

	assert.Len(t, region.GetSupportedServicePlans(), 1)
	assert.ElementsMatch(
		t,
		[]openapicommonv1beta1.EngineType{
			openapicommonv1beta1.EngineType_MYSQL_COMPATIBLE,
			openapicommonv1beta1.EngineType_POSTGRESQL_COMPATIBLE,
		},
		region.GetSupportedServicePlans()[0].GetSupportedEngineTypes(),
	)
}

func TestCvtOpenAPIEngineTypeDefaultsToMySQL(t *testing.T) {
	assert.Equal(
		t,
		openapicommonv1beta1.EngineType_MYSQL_COMPATIBLE,
		cvtOpenAPIEngineType(globalcommonv1.Region_ENGINE_TYPE_UNSPECIFIED),
	)
	assert.Equal(
		t,
		openapicommonv1beta1.EngineType_MYSQL_COMPATIBLE,
		cvtOpenAPIEngineType(globalcommonv1.Region_MYSQL_COMPATIBLE),
	)
	assert.Equal(
		t,
		openapicommonv1beta1.EngineType_POSTGRESQL_COMPATIBLE,
		cvtOpenAPIEngineType(globalcommonv1.Region_POSTGRESQL_COMPATIBLE),
	)
}

func TestFlatViewMergesSupportedEngineTypes(t *testing.T) {
	server := &RegionServer{}

	view := server.flatView(t.Context(), []*openapicommonv1beta1.Region{
		{
			Name:          "regions/aws-us-east-2",
			RegionId:      "aws-us-east-2",
			CloudProvider: openapicommonv1beta1.Region_aws,
			ServicePlans:  []openapicommonv1beta1.ServicePlan{openapicommonv1beta1.ServicePlan_Starter},
			SupportedServicePlans: []*openapicommonv1beta1.ServicePlanInfo{
				{
					ServicePlan:          openapicommonv1beta1.ServicePlan_Starter,
					SupportedEngineTypes: []openapicommonv1beta1.EngineType{openapicommonv1beta1.EngineType_POSTGRESQL_COMPATIBLE},
				},
			},
		},
		{
			Name:          "regions/aws-us-east-2",
			RegionId:      "aws-us-east-2",
			CloudProvider: openapicommonv1beta1.Region_aws,
			ServicePlans:  []openapicommonv1beta1.ServicePlan{openapicommonv1beta1.ServicePlan_Starter},
			SupportedServicePlans: []*openapicommonv1beta1.ServicePlanInfo{
				{
					ServicePlan:          openapicommonv1beta1.ServicePlan_Starter,
					SupportedEngineTypes: []openapicommonv1beta1.EngineType{openapicommonv1beta1.EngineType_MYSQL_COMPATIBLE},
				},
			},
		},
	})

	assert.Len(t, view.GetRegions(), 1)
	assert.Len(t, view.GetRegions()[0].GetSupportedServicePlans(), 1)
	assert.ElementsMatch(
		t,
		[]openapicommonv1beta1.EngineType{
			openapicommonv1beta1.EngineType_MYSQL_COMPATIBLE,
			openapicommonv1beta1.EngineType_POSTGRESQL_COMPATIBLE,
		},
		view.GetRegions()[0].GetSupportedServicePlans()[0].GetSupportedEngineTypes(),
	)
}

func TestServicePlanDisplayName(t *testing.T) {
	assert.Equal(t, "Starter", GetDisplayName(openapicommonv1beta1.ServicePlan_Starter))
	assert.Equal(t, "Essential", GetDisplayName(openapicommonv1beta1.ServicePlan_Essential))
	assert.Equal(t, "Premium", GetDisplayName(openapicommonv1beta1.ServicePlan_Premium))
	assert.Equal(t, "Premium Reserved", GetDisplayName(openapicommonv1beta1.ServicePlan_Premium_Reserved))
	assert.Equal(t, "Dedicated", GetDisplayName(openapicommonv1beta1.ServicePlan_Dedicated))
	assert.Equal(t, "BYOC", GetDisplayName(openapicommonv1beta1.ServicePlan_BYOC))
}

func TestListNextgenRegionsSupportsEssentialV2(t *testing.T) {
	nextgenRegionServer := &fakeNextgenRegionServer{}
	server := &RegionServer{
		openAPINextgenRegionServer: nextgenRegionServer,
	}

	regions, err := server.listNextgenRegions(t.Context(), &bffv1beta2.ListRegionsRequest{
		ServicePlans: []openapicommonv1beta1.ServicePlan{openapicommonv1beta1.ServicePlan_Essential_V2},
	})

	require.NoError(t, err)
	assert.True(t, nextgenRegionServer.called)
	assert.Len(t, regions, 1)
	assert.Equal(t, []openapicommonv1beta1.ServicePlan{
		openapicommonv1beta1.ServicePlan_Essential_V2,
	}, regions[0].GetServicePlans())
}
