package region

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"
	"go.uber.org/mock/gomock"
	"google.golang.org/genproto/googleapis/rpc/errdetails"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/proto"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/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"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/adapter/nextgenglobal/mock_nextgenglobal"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

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

	ctrl := gomock.NewController(t)
	nextgenCli := mock_nextgenglobal.NewMockNextGenGlobal(ctrl)
	server := NewServer(nextgenCli, nil, nil)
	ctx := metadata.AppendToIncomingMeta(t.Context(), &metadata.RequestMeta{
		OrgID:             1001,
		UserID:            "user-1",
		DefaultProjectID:  2001,
		ValidProjectList:  []uint64{2001},
		IsCheckPermission: proto.Bool(true),
	})

	nextgenCli.EXPECT().PlanResourcePoolCidr(gomock.Any(), gomock.Any()).DoAndReturn(
		func(_ context.Context, request *globalngv1.PlanResourcePoolCidrRequest, _ ...grpc.CallOption) (*globalngv1.PlanResourcePoolCidrResponse, error) {
			require.Equal(t, uint64(1001), request.GetOrganizationId())
			require.Equal(t, globalcommonv1.CloudProvider_AWS, request.GetCloudProviderId())
			require.Equal(t, globalcommonv1.Region_AWS_US_WEST_2, request.GetRegionId())
			require.Equal(t, globalngv1.ResourcePool_ZONAL, request.GetHighAvailabilityType())
			return &globalngv1.PlanResourcePoolCidrResponse{
				CloudProviderId: globalcommonv1.CloudProvider_AWS,
				RegionId:        globalcommonv1.Region_AWS_US_WEST_2,
				Policy: &globalngv1.ResourcePoolCidrPolicy{
					SupportedPrivateCidrs: []string{"10.0.0.0/8"},
					AllowedPrefixRanges: []*globalngv1.ResourcePoolCidrPrefixRange{{
						MinPrefixLength: 16,
						MaxPrefixLength: 23,
					}},
				},
				Exclusions: []*globalngv1.ResourcePoolCidrExclusion{{
					Type:                    globalngv1.ResourcePoolCidrExclusion_RESOURCE_POOL,
					Cidr:                    "10.1.0.0/16",
					ResourcePoolId:          proto.Uint64(99),
					ResourcePoolDisplayName: proto.String("existing"),
					RegionId:                globalcommonv1.Region_AWS_US_EAST_1.Enum(),
					CloudProviderId:         globalcommonv1.CloudProvider_AWS.Enum(),
				}},
				RecommendationStatus: globalngv1.PlanResourcePoolCidrResponse_AVAILABLE,
				Recommendation: &globalngv1.ResourcePoolCidrRecommendation{
					Cidr:               "10.2.0.0/16",
					SupportedNodeCount: 128,
				},
			}, nil
		},
	)

	response, err := server.PlanResourcePoolCidr(ctx, &openapingv1beta2.PlanResourcePoolCidrRequest{
		RegionId:             "regions/aws-us-west-2",
		HighAvailabilityType: openapingv1beta2.ByocVcpuPool_ZONAL,
	})

	require.NoError(t, err)
	require.Equal(t, "aws-us-west-2", response.GetRegionId())
	require.Equal(t, openapicommonv1beta1.Region_aws, response.GetCloudProvider())
	require.Equal(t, "99", response.GetExclusions()[0].GetResourcePoolId())
	require.Equal(t, "aws-us-east-1", response.GetExclusions()[0].GetRegionId())
	require.Equal(t, "10.2.0.0/16", response.GetRecommendation().GetCidr())
}

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

	ctrl := gomock.NewController(t)
	nextgenCli := mock_nextgenglobal.NewMockNextGenGlobal(ctrl)
	server := NewServer(nextgenCli, nil, nil)
	ctx := metadata.AppendToIncomingMeta(t.Context(), &metadata.RequestMeta{
		OrgID:             1001,
		UserID:            "user-1",
		DefaultProjectID:  2001,
		ValidProjectList:  []uint64{2001},
		IsCheckPermission: proto.Bool(true),
	})

	nextgenCli.EXPECT().ValidateResourcePoolCidr(gomock.Any(), gomock.Any()).DoAndReturn(
		func(_ context.Context, request *globalngv1.ValidateResourcePoolCidrRequest, _ ...grpc.CallOption) (*globalngv1.ValidateResourcePoolCidrResponse, error) {
			require.Equal(t, globalngv1.ResourcePool_ZONAL, request.GetHighAvailabilityType())
			return &globalngv1.ValidateResourcePoolCidrResponse{
				CloudProviderId:    globalcommonv1.CloudProvider_AWS,
				RegionId:           globalcommonv1.Region_AWS_US_WEST_2,
				Cidr:               "10.0.0.0/16",
				SupportedNodeCount: proto.Int64(64),
			}, nil
		},
	)

	response, err := server.ValidateResourcePoolCidr(ctx, &openapingv1beta2.ValidateResourcePoolCidrRequest{
		RegionId:             "aws-us-west-2",
		Cidr:                 "10.0.0.0/16",
		HighAvailabilityType: openapingv1beta2.ByocVcpuPool_ZONAL,
	})

	require.NoError(t, err)
	require.EqualValues(t, 64, response.GetSupportedNodeCount())
}

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

	ctrl := gomock.NewController(t)
	server := NewServer(mock_nextgenglobal.NewMockNextGenGlobal(ctrl), nil, nil)
	_, err := server.ValidateResourcePoolCidr(t.Context(), &openapingv1beta2.ValidateResourcePoolCidrRequest{
		RegionId: "aws-us-west-2",
		Cidr:     "  ",
	})

	require.Error(t, err)
	grpcStatus := status.Convert(err)
	require.Equal(t, codes.InvalidArgument, grpcStatus.Code())
	require.Len(t, grpcStatus.Details(), 1)
	badRequest, ok := grpcStatus.Details()[0].(*errdetails.BadRequest)
	require.True(t, ok)
	require.Equal(t, "cidr", badRequest.GetFieldViolations()[0].GetField())
}
