package vpcpeering_test

import (
	"bytes"
	"context"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"
	"go.uber.org/mock/gomock"
	spb "google.golang.org/genproto/googleapis/rpc/status"
	grpccodes "google.golang.org/grpc/codes"
	"google.golang.org/protobuf/encoding/protojson"
	"k8s.io/apimachinery/pkg/labels"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/yaml"

	regionalapipb "github.com/tidbcloud/infra-provider/api/tidbcloud/regional-server/v1"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/router"
	vpcpeeringhandler "github.com/tidbcloud/infra-provider/regional-server/pkg/router/vpcpeering"
	vpcpeeringsvc "github.com/tidbcloud/infra-provider/regional-server/pkg/service/vpcpeering"
	"github.com/tidbcloud/infra-provider/regional-server/test/kubemock"
	"github.com/tidbcloud/infra-provider/regional-server/test/testdata"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestVPCPeeringHandlerCommon(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	ctrl := gomock.NewController(t)
	kubeClient := kubemock.NewMockClient(ctrl)
	kubeAPIReader := kubemock.NewMockReader(ctrl)
	svc, err := vpcpeeringsvc.NewService(
		kubeClient,
		kubeAPIReader,
		vpcpeeringsvc.WithProvider("aws"),
		vpcpeeringsvc.WithRegion("us-west-2"),
	)
	require.NoError(t, err)
	handler := vpcpeeringhandler.NewVPCPeeringHandler(svc)
	mountFn := router.MountVPCPeeringHandler(handler)
	mountFn(eng.Group("/api/dedicated/v1"))
	{
		// wrong route should return 404
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodPost, "/api/dedicated/v1/vpcPeeringsNotExisted", nil)
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusNotFound, w.Result().StatusCode)
	}
	// invalid parameter when creating vpcpeering shoud return http status code 400 and grpc code 3
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		in := &regionalapipb.VpcPeering{
			TenantId:             "foo",
			ProjectId:            "foo",
			DestinationCidrBlock: "10.30.0.0/16",
			Info: &regionalapipb.VpcPeering_Aws_{
				Aws: &regionalapipb.VpcPeering_Aws{
					SourceVpcId:          "vpc-foo",
					SourceAccountId:      "foo",
					DestinationVpcId:     "vpc-bar",
					DestinationAccountId: "bar",
					DestinationRegion:    "us-west-2",
				},
			},
		}
		buf, err := protojson.Marshal(in)
		body := bytes.NewReader(buf)
		require.NoError(t, err)
		ctx.Request, _ = http.NewRequest(http.MethodPost, "/api/dedicated/v1/vpcPeerings", body)
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
		out := new(spb.Status)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, int32(grpccodes.InvalidArgument), out.GetCode())
	}
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		in := &regionalapipb.VpcPeering{
			Id:                   "foo",
			TenantId:             "foo",
			ProjectId:            "foo",
			DestinationCidrBlock: "10.30.0.0/16",
		}
		buf, err := protojson.Marshal(in)
		body := bytes.NewReader(buf)
		require.NoError(t, err)
		ctx.Request, _ = http.NewRequest(http.MethodPost, "/api/dedicated/v1/vpcPeerings", body)
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
		out := new(spb.Status)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, int32(grpccodes.InvalidArgument), out.GetCode())
	}
	{
		// missing tenantId when listing vpcpeerings should return http status code 400 and grpc code 3
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/dedicated/v1/vpcPeerings?projectId=foo", nil)
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
		out := new(spb.Status)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, int32(grpccodes.InvalidArgument), out.GetCode())
	}
}

func TestVPCPeeringHandlerCreateVPCPeeringAWS(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	ctrl := gomock.NewController(t)
	kubeClient := kubemock.NewMockClient(ctrl)
	kubeAPIReader := kubemock.NewMockReader(ctrl)
	svc, err := vpcpeeringsvc.NewService(
		kubeClient,
		kubeAPIReader,
		vpcpeeringsvc.WithProvider("aws"),
		vpcpeeringsvc.WithRegion("us-west-2"),
	)
	require.NoError(t, err)
	handler := vpcpeeringhandler.NewVPCPeeringHandler(svc)
	mountFn := router.MountVPCPeeringHandler(handler)
	mountFn(eng.Group("/api/dedicated/v1"))
	{
		// wrong provider should return http status code 400 and grpc code 3
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		in := &regionalapipb.VpcPeering{
			Id:                   "foo",
			TenantId:             "foo",
			ProjectId:            "foo",
			DestinationCidrBlock: "10.30.0.0/16",
			Info: &regionalapipb.VpcPeering_Gcp_{
				Gcp: &regionalapipb.VpcPeering_Gcp{
					SourceProjectId:        "foo",
					SourceNetworkName:      "foo",
					DestinationProjectId:   "bar",
					DestinationNetworkName: "bar",
				},
			},
		}
		buf, err := protojson.Marshal(in)
		body := bytes.NewReader(buf)
		require.NoError(t, err)
		ctx.Request, _ = http.NewRequest(http.MethodPost, "/api/dedicated/v1/vpcPeerings", body)
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
		out := new(spb.Status)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, int32(grpccodes.InvalidArgument), out.GetCode())
	}
	{
		// cannot find source vpc when creating vpcpeering should return http status code 400 and grpc code 3
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		in := &regionalapipb.VpcPeering{
			Id:                   "foo",
			TenantId:             "foo",
			ProjectId:            "foo",
			DestinationCidrBlock: "10.30.0.0/16",
			Info: &regionalapipb.VpcPeering_Aws_{
				Aws: &regionalapipb.VpcPeering_Aws{
					SourceAccountId:      "foo",
					SourceVpcId:          "vpc-foo",
					DestinationAccountId: "bar",
					DestinationVpcId:     "vpc-bar",
				},
			},
		}
		buf, err := protojson.Marshal(in)
		body := bytes.NewReader(buf)
		require.NoError(t, err)
		ctx.Request, _ = http.NewRequest(http.MethodPost, "/api/dedicated/v1/vpcPeerings", body)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 3 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/project-foo=true" {
							return false
						}
						if rs[1].String() != "cloud.pingcap.com/tenant-foo=true" {
							return false
						}
						if rs[2].String() != "cloud.pingcap.com/vpcId=vpc-foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(_ context.Context, list *v1alpha1.VPCList, _ ...client.ListOption) error {
				list.Items = []v1alpha1.VPC{}
				return nil
			})
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
		out := new(spb.Status)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, int32(grpccodes.InvalidArgument), out.GetCode())
	}
	{
		// correct request should return http status code 200
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		in := &regionalapipb.VpcPeering{
			Id:                   "foo",
			TenantId:             "foo",
			ProjectId:            "foo",
			DestinationCidrBlock: "10.30.0.0/16",
			Info: &regionalapipb.VpcPeering_Aws_{
				Aws: &regionalapipb.VpcPeering_Aws{
					SourceAccountId:      "foo",
					SourceVpcId:          "vpc-foo",
					DestinationAccountId: "bar",
					DestinationVpcId:     "vpc-bar",
				},
			},
		}
		buf, err := protojson.Marshal(in)
		require.NoError(t, err)
		body := bytes.NewReader(buf)
		ctx.Request, _ = http.NewRequest(http.MethodPost, "/api/dedicated/v1/vpcPeerings", body)
		bs, err := testdata.LoadDefaultVPCYAML()
		require.NoError(t, err)
		defaultVPC := new(v1alpha1.VPC)
		require.NoError(t, yaml.Unmarshal(bs, defaultVPC))
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 3 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/project-foo=true" {
							return false
						}
						if rs[1].String() != "cloud.pingcap.com/tenant-foo=true" {
							return false
						}
						if rs[2].String() != "cloud.pingcap.com/vpcId=vpc-foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCList, opts ...client.ListOption) error {
				item := defaultVPC.DeepCopy()
				list.Items = []v1alpha1.VPC{*item}
				return nil
			})
		kubeClient.EXPECT().
			Create(gomock.Any(), gomock.Cond(func(obj any) bool {
				ensureMeta := func(cr *v1alpha1.VPCPeering) bool {
					if cr.Name != "foo" && cr.Namespace != "foo" {
						return false
					}
					return true
				}
				ensureSpec := func(cr *v1alpha1.VPCPeering) bool {
					if cr.Spec.SourceVPCRef == nil {
						t.Log("spec.sourceVPCRef is nil")
						return false
					}
					if cr.Spec.SourceVPCRef.Name != "foo" || cr.Spec.SourceVPCRef.Namespace != "foo" {
						t.Log("spec.sourceVPCRef is incorrect")
						return false
					}
					if cr.Spec.AccountRef == nil {
						t.Log("spec.accountRef is nil")
						return false
					}
					if cr.Spec.AccountRef.AccountID != "foo" || cr.Spec.AccountRef.ProviderConfigName != "foo" {
						t.Log("spec.accountRef is incorrect")
						return false
					}
					if cr.Spec.Provider != "aws" || cr.Spec.Region != "us-west-2" {
						t.Log("spec.provider or spec.region is incorrect")
						return false
					}
					if cr.Spec.Target.AWS == nil {
						t.Log("spec.target.aws is nil")
						return false
					}
					if cr.Spec.Target.AWS.AccountID != "bar" ||
						cr.Spec.Target.AWS.VpcID != "vpc-bar" ||
						cr.Spec.Target.AWS.PeerCIDR != "10.30.0.0/16" ||
						cr.Spec.Target.AWS.PeerRegion != "us-west-2" {
						t.Logf("spec.target.aws is incorrect, got %v", cr.Spec.Target.AWS)
						return false
					}
					return true
				}
				ensureLabel := func(cr *v1alpha1.VPCPeering) bool {
					if cr.GetLabels() == nil {
						return false
					}
					if cr.Labels[v1alpha1.ProjectIDKey] != "foo" {
						return false
					}
					if cr.Labels[v1alpha1.TenantIDKey] != "foo" {
						return false
					}
					if cr.Labels[v1alpha1.LabelKeyBizID] != "foo" {
						return false
					}

					return true
				}
				ensureAnno := func(cr *v1alpha1.VPCPeering) bool {
					if cr.GetAnnotations() == nil {
						return false
					}
					if cr.Annotations[v1alpha1.AnnotationKeySourceVPCID] != "vpc-foo" {
						return false
					}
					if cr.Annotations[v1alpha1.AnnotationKeySourceAccountID] != "foo" {
						return false
					}
					if cr.Annotations[v1alpha1.AnnotationKeySourceVPCCIDR] != "10.10.0.0/16" {
						return false
					}
					return true
				}
				vpcpeering, ok := obj.(*v1alpha1.VPCPeering)
				if !ok {
					return false
				}
				if !ensureMeta(vpcpeering) {
					t.Log("meta is incorrect")
					return false
				}
				if !ensureSpec(vpcpeering) {
					t.Log("spec is incorrect")
					return false
				}
				if !ensureLabel(vpcpeering) {
					t.Log("label is incorrect")
					return false
				}
				if !ensureAnno(vpcpeering) {
					t.Log("annotation is incorrect")
					return false
				}
				return true
			})).
			Return(nil)
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusAccepted, w.Result().StatusCode)
		out := new(regionalapipb.VpcPeering)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, "foo", out.Id)
		require.Equal(t, "10.10.0.0/16", out.SourceCidrBlock)
		require.Equal(t, regionalapipb.VpcPeering_PENDING, out.Status)
	}
	{
		// cross region peering case
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		in := &regionalapipb.VpcPeering{
			Id:                   "foo",
			TenantId:             "foo",
			ProjectId:            "foo",
			DestinationCidrBlock: "10.30.0.0/16",
			Info: &regionalapipb.VpcPeering_Aws_{
				Aws: &regionalapipb.VpcPeering_Aws{
					SourceAccountId:      "foo",
					SourceVpcId:          "vpc-foo",
					DestinationAccountId: "bar",
					DestinationVpcId:     "vpc-bar",
					DestinationRegion:    "us-east-2",
				},
			},
		}
		buf, err := protojson.Marshal(in)
		require.NoError(t, err)
		body := bytes.NewReader(buf)
		ctx.Request, _ = http.NewRequest(http.MethodPost, "/api/dedicated/v1/vpcPeerings", body)
		bs, err := testdata.LoadDefaultVPCYAML()
		require.NoError(t, err)
		defaultVPC := new(v1alpha1.VPC)
		require.NoError(t, yaml.Unmarshal(bs, defaultVPC))
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 3 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/project-foo=true" {
							return false
						}
						if rs[1].String() != "cloud.pingcap.com/tenant-foo=true" {
							return false
						}
						if rs[2].String() != "cloud.pingcap.com/vpcId=vpc-foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCList, opts ...client.ListOption) error {
				item := defaultVPC.DeepCopy()
				list.Items = []v1alpha1.VPC{*item}
				return nil
			})
		kubeClient.EXPECT().
			Create(gomock.Any(), gomock.Cond(func(obj any) bool {
				ensureMeta := func(cr *v1alpha1.VPCPeering) bool {
					if cr.Name != "foo" && cr.Namespace != "foo" {
						return false
					}
					return true
				}
				ensureSpec := func(cr *v1alpha1.VPCPeering) bool {
					if cr.Spec.SourceVPCRef == nil {
						t.Log("spec.sourceVPCRef is nil")
						return false
					}
					if cr.Spec.SourceVPCRef.Name != "foo" || cr.Spec.SourceVPCRef.Namespace != "foo" {
						t.Log("spec.sourceVPCRef is incorrect")
						return false
					}
					if cr.Spec.AccountRef == nil {
						t.Log("spec.accountRef is nil")
						return false
					}
					if cr.Spec.AccountRef.AccountID != "foo" || cr.Spec.AccountRef.ProviderConfigName != "foo" {
						t.Log("spec.accountRef is incorrect")
						return false
					}
					if cr.Spec.Provider != "aws" || cr.Spec.Region != "us-west-2" {
						t.Log("spec.provider or spec.region is incorrect")
						return false
					}
					if cr.Spec.Target.AWS == nil {
						t.Log("spec.target.aws is nil")
						return false
					}
					if cr.Spec.Target.AWS.AccountID != "bar" ||
						cr.Spec.Target.AWS.VpcID != "vpc-bar" ||
						cr.Spec.Target.AWS.PeerCIDR != "10.30.0.0/16" ||
						cr.Spec.Target.AWS.PeerRegion != "us-east-2" {
						t.Logf("spec.target.aws is incorrect, got %v", cr.Spec.Target.AWS)
						return false
					}
					return true
				}
				ensureLabel := func(cr *v1alpha1.VPCPeering) bool {
					if cr.GetLabels() == nil {
						return false
					}
					if cr.Labels[v1alpha1.ProjectIDKey] != "foo" {
						return false
					}
					if cr.Labels[v1alpha1.TenantIDKey] != "foo" {
						return false
					}
					if cr.Labels[v1alpha1.LabelKeyBizID] != "foo" {
						return false
					}

					return true
				}
				ensureAnno := func(cr *v1alpha1.VPCPeering) bool {
					if cr.GetAnnotations() == nil {
						return false
					}
					if cr.Annotations[v1alpha1.AnnotationKeySourceVPCID] != "vpc-foo" {
						return false
					}
					if cr.Annotations[v1alpha1.AnnotationKeySourceAccountID] != "foo" {
						return false
					}
					if cr.Annotations[v1alpha1.AnnotationKeySourceVPCCIDR] != "10.10.0.0/16" {
						return false
					}
					return true
				}
				vpcpeering, ok := obj.(*v1alpha1.VPCPeering)
				if !ok {
					return false
				}
				if !ensureMeta(vpcpeering) {
					t.Log("meta is incorrect")
					return false
				}
				if !ensureSpec(vpcpeering) {
					t.Log("spec is incorrect")
					return false
				}
				if !ensureLabel(vpcpeering) {
					t.Log("label is incorrect")
					return false
				}
				if !ensureAnno(vpcpeering) {
					t.Log("annotation is incorrect")
					return false
				}
				return true
			})).
			Return(nil)
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusAccepted, w.Result().StatusCode)
		out := new(regionalapipb.VpcPeering)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, "foo", out.Id)
		require.Equal(t, "10.10.0.0/16", out.SourceCidrBlock)
		require.Equal(t, regionalapipb.VpcPeering_PENDING, out.Status)
	}
	require.True(t, ctrl.Satisfied())
}

func TestVPCPeeringHandlerCreateVPCPeeringGCP(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	ctrl := gomock.NewController(t)
	kubeClient := kubemock.NewMockClient(ctrl)
	kubeAPIReader := kubemock.NewMockReader(ctrl)
	svc, err := vpcpeeringsvc.NewService(
		kubeClient,
		kubeAPIReader,
		vpcpeeringsvc.WithProvider("gcp"),
		vpcpeeringsvc.WithRegion("us-west1"),
	)
	require.NoError(t, err)
	handler := vpcpeeringhandler.NewVPCPeeringHandler(svc)
	mountFn := router.MountVPCPeeringHandler(handler)
	mountFn(eng.Group("/api/dedicated/v1"))
	{
		// wrong provider should return http status code 400 and grpc code 3
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		in := &regionalapipb.VpcPeering{
			Id:                   "foo",
			TenantId:             "foo",
			ProjectId:            "foo",
			DestinationCidrBlock: "10.30.0.0/16",
			Info: &regionalapipb.VpcPeering_Aws_{
				Aws: &regionalapipb.VpcPeering_Aws{
					SourceAccountId:      "foo",
					SourceVpcId:          "vpc-foo",
					DestinationAccountId: "bar",
					DestinationVpcId:     "vpc-bar",
				},
			},
		}
		buf, err := protojson.Marshal(in)
		body := bytes.NewReader(buf)
		require.NoError(t, err)
		ctx.Request, _ = http.NewRequest(http.MethodPost, "/api/dedicated/v1/vpcPeerings", body)
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
		out := new(spb.Status)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, int32(grpccodes.InvalidArgument), out.GetCode())
	}
	{
		// cannot find source vpc when creating vpcpeering should return http status code 400 and grpc code 3
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		in := &regionalapipb.VpcPeering{
			Id:                   "foo",
			TenantId:             "foo",
			ProjectId:            "foo",
			DestinationCidrBlock: "10.30.0.0/16",
			Info: &regionalapipb.VpcPeering_Gcp_{
				Gcp: &regionalapipb.VpcPeering_Gcp{
					SourceProjectId:        "foo",
					SourceNetworkName:      "vpc-foo",
					DestinationProjectId:   "bar",
					DestinationNetworkName: "vpc-bar",
				},
			},
		}
		buf, err := protojson.Marshal(in)
		body := bytes.NewReader(buf)
		require.NoError(t, err)
		ctx.Request, _ = http.NewRequest(http.MethodPost, "/api/dedicated/v1/vpcPeerings", body)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 3 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/project-foo=true" {
							return false
						}
						if rs[1].String() != "cloud.pingcap.com/tenant-foo=true" {
							return false
						}
						if rs[2].String() != "cloud.pingcap.com/vpcId=vpc-foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(_ context.Context, list *v1alpha1.VPCList, _ ...client.ListOption) error {
				list.Items = []v1alpha1.VPC{}
				return nil
			})
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
		out := new(spb.Status)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, int32(grpccodes.InvalidArgument), out.GetCode())
	}
	{
		// correct request should return http status code 200
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		in := &regionalapipb.VpcPeering{
			Id:                   "foo",
			TenantId:             "foo",
			ProjectId:            "foo",
			DestinationCidrBlock: "10.30.0.0/16",
			Info: &regionalapipb.VpcPeering_Gcp_{
				Gcp: &regionalapipb.VpcPeering_Gcp{
					SourceProjectId:        "foo",
					SourceNetworkName:      "vpc-foo",
					DestinationProjectId:   "bar",
					DestinationNetworkName: "vpc-bar",
				},
			},
		}
		buf, err := protojson.Marshal(in)
		require.NoError(t, err)
		body := bytes.NewReader(buf)
		ctx.Request, _ = http.NewRequest(http.MethodPost, "/api/dedicated/v1/vpcPeerings", body)
		bs, err := testdata.LoadDefaultVPCYAML()
		require.NoError(t, err)
		defaultVPC := new(v1alpha1.VPC)
		require.NoError(t, yaml.Unmarshal(bs, defaultVPC))
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					if len(opts) != 1 {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 3 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/project-foo=true" {
							return false
						}
						if rs[1].String() != "cloud.pingcap.com/tenant-foo=true" {
							return false
						}
						if rs[2].String() != "cloud.pingcap.com/vpcId=vpc-foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCList, opts ...client.ListOption) error {
				item := defaultVPC.DeepCopy()
				item.Spec.Region = "us-west1"
				item.Spec.Provider = "gke"
				list.Items = []v1alpha1.VPC{*item}
				return nil
			})
		kubeClient.EXPECT().
			Create(gomock.Any(), gomock.Cond(func(obj any) bool {
				ensureMeta := func(cr *v1alpha1.VPCPeering) bool {
					if cr.Name != "foo" && cr.Namespace != "foo" {
						return false
					}
					return true
				}
				ensureSpec := func(cr *v1alpha1.VPCPeering) bool {
					if cr.Spec.SourceVPCRef == nil {
						t.Log("spec.sourceVPCRef is nil")
						return false
					}
					if cr.Spec.SourceVPCRef.Name != "foo" || cr.Spec.SourceVPCRef.Namespace != "foo" {
						t.Log("spec.sourceVPCRef is incorrect")
						return false
					}
					if cr.Spec.AccountRef == nil {
						t.Log("spec.accountRef is nil")
						return false
					}
					if cr.Spec.AccountRef.AccountID != "foo" || cr.Spec.AccountRef.ProviderConfigName != "foo" {
						t.Log("spec.accountRef is incorrect")
						return false
					}
					if cr.Spec.Provider != "gcp" || cr.Spec.Region != "us-west1" {
						t.Log("spec.provider or spec.region is incorrect")
						return false
					}
					if cr.Spec.Target.GCP == nil {
						t.Log("spec.target.gcp is nil")
						return false
					}
					if cr.Spec.Target.GCP.ProjectID != "bar" ||
						cr.Spec.Target.GCP.PeerNetwork != "vpc-bar" ||
						cr.Spec.Target.GCP.PeerCIDR != "10.30.0.0/16" {
						t.Logf("spec.target.gcp is incorrect, got %v", cr.Spec.Target.GCP)
						return false
					}
					return true
				}
				ensureLabel := func(cr *v1alpha1.VPCPeering) bool {
					if cr.GetLabels() == nil {
						return false
					}
					if cr.Labels[v1alpha1.ProjectIDKey] != "foo" {
						return false
					}
					if cr.Labels[v1alpha1.TenantIDKey] != "foo" {
						return false
					}
					if cr.Labels[v1alpha1.LabelKeyBizID] != "foo" {
						return false
					}

					return true
				}
				ensureAnno := func(cr *v1alpha1.VPCPeering) bool {
					if cr.GetAnnotations() == nil {
						return false
					}
					if cr.Annotations[v1alpha1.AnnotationKeySourceVPCID] != "vpc-foo" {
						return false
					}
					if cr.Annotations[v1alpha1.AnnotationKeySourceAccountID] != "foo" {
						return false
					}
					if cr.Annotations[v1alpha1.AnnotationKeySourceVPCCIDR] != "10.10.0.0/16" {
						return false
					}
					return true
				}
				vpcpeering, ok := obj.(*v1alpha1.VPCPeering)
				if !ok {
					return false
				}
				if !ensureMeta(vpcpeering) {
					t.Log("meta is incorrect")
					return false
				}
				if !ensureSpec(vpcpeering) {
					t.Log("spec is incorrect")
					return false
				}
				if !ensureLabel(vpcpeering) {
					t.Log("label is incorrect")
					return false
				}
				if !ensureAnno(vpcpeering) {
					t.Log("annotation is incorrect")
					return false
				}
				return true
			})).
			Return(nil)
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusAccepted, w.Result().StatusCode)
		out := new(regionalapipb.VpcPeering)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, "foo", out.Id)
		require.Equal(t, "10.10.0.0/16", out.SourceCidrBlock)
		require.Equal(t, regionalapipb.VpcPeering_PENDING, out.Status)
	}
	require.True(t, ctrl.Satisfied())
}

func TestVPCPeeringHandlerGetVPCPeeringAWS(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	ctrl := gomock.NewController(t)
	kubeClient := kubemock.NewMockClient(ctrl)
	kubeAPIReader := kubemock.NewMockReader(ctrl)
	svc, err := vpcpeeringsvc.NewService(
		kubeClient,
		kubeAPIReader,
		vpcpeeringsvc.WithProvider("aws"),
		vpcpeeringsvc.WithRegion("us-west-2"),
	)
	require.NoError(t, err)
	handler := vpcpeeringhandler.NewVPCPeeringHandler(svc)
	mountFn := router.MountVPCPeeringHandler(handler)
	mountFn(eng.Group("/api/dedicated/v1"))
	bs, err := testdata.LoadDefaultVPCPeeringYAML()
	require.NoError(t, err)
	defaultVPCPeering := new(v1alpha1.VPCPeering)
	require.NoError(t, yaml.Unmarshal(bs, defaultVPCPeering))
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/dedicated/v1/vpcPeerings/foo", nil)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCPeeringList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					if len(opts) != 1 {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 1 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/bizId=foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCPeeringList, opts ...client.ListOption) error {
				item := defaultVPCPeering.DeepCopy()
				item.Spec.Provider = "aws"
				item.Spec.Region = "us-west-2"
				list.Items = []v1alpha1.VPCPeering{*item}
				return nil
			})
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusOK, w.Result().StatusCode)
		out := new(regionalapipb.VpcPeering)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, "foo", out.Id)
		require.Equal(t, "foo", out.TenantId)
		require.Equal(t, "foo", out.ProjectId)
		require.Equal(t, "10.10.0.0/16", out.SourceCidrBlock)
		require.Equal(t, "10.30.0.0/16", out.DestinationCidrBlock)
		require.Equal(t, regionalapipb.VpcPeering_ACTIVE, out.Status)
		awsInfo, ok := out.Info.(*regionalapipb.VpcPeering_Aws_)
		require.True(t, ok)
		require.Equal(t, "vpc-foo", awsInfo.Aws.SourceVpcId)
		require.Equal(t, "foo", awsInfo.Aws.SourceAccountId)
		require.Equal(t, "vpc-bar", awsInfo.Aws.DestinationVpcId)
		require.Equal(t, "bar", awsInfo.Aws.DestinationAccountId)
		require.Equal(t, "pcx-foobar", awsInfo.Aws.VpcPeeringConnectionId)
	}
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/dedicated/v1/vpcPeerings/foo?showStatusOnly=true", nil)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCPeeringList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 1 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/bizId=foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCPeeringList, opts ...client.ListOption) error {
				item := defaultVPCPeering.DeepCopy()
				item.Spec.Provider = "aws"
				item.Spec.Region = "us-west-2"
				list.Items = []v1alpha1.VPCPeering{*item}
				return nil
			})
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusOK, w.Result().StatusCode)
		out := new(regionalapipb.VpcPeering)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, "foo", out.Id)
		require.Equal(t, regionalapipb.VpcPeering_ACTIVE, out.Status)
	}
	{

		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/dedicated/v1/vpcPeerings/foo", nil)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCPeeringList{}),
				gomock.Any()).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCPeeringList, opts ...client.ListOption) error {
				list.Items = []v1alpha1.VPCPeering{}
				return nil
			})
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusNotFound, w.Result().StatusCode)
		out := new(spb.Status)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, int32(grpccodes.NotFound), out.Code)
	}
	require.True(t, ctrl.Satisfied())
}

func TestVPCPeeringHandlerGetVPCPeeringGCP(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	ctrl := gomock.NewController(t)
	kubeClient := kubemock.NewMockClient(ctrl)
	kubeAPIReader := kubemock.NewMockReader(ctrl)
	svc, err := vpcpeeringsvc.NewService(
		kubeClient,
		kubeAPIReader,
		vpcpeeringsvc.WithProvider("gcp"),
		vpcpeeringsvc.WithRegion("us-west1"),
	)
	require.NoError(t, err)
	handler := vpcpeeringhandler.NewVPCPeeringHandler(svc)
	mountFn := router.MountVPCPeeringHandler(handler)
	mountFn(eng.Group("/api/dedicated/v1"))
	bs, err := testdata.LoadDefaultVPCPeeringYAML()
	require.NoError(t, err)
	defaultVPCPeering := new(v1alpha1.VPCPeering)
	require.NoError(t, yaml.Unmarshal(bs, defaultVPCPeering))
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/dedicated/v1/vpcPeerings/foo", nil)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCPeeringList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 1 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/bizId=foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCPeeringList, opts ...client.ListOption) error {
				item := defaultVPCPeering.DeepCopy()
				item.Spec.Provider = "gcp"
				item.Spec.Region = "us-west1"
				item.Spec.Target.AWS = nil
				item.Spec.Target.GCP = &v1alpha1.GCP{
					ProjectID:   "bar",
					PeerNetwork: "vpc-bar",
					PeerCIDR:    "10.30.0.0/16",
				}
				list.Items = []v1alpha1.VPCPeering{*item}
				return nil
			})
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusOK, w.Result().StatusCode)
		out := new(regionalapipb.VpcPeering)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, "foo", out.Id)
		require.Equal(t, "foo", out.TenantId)
		require.Equal(t, "foo", out.ProjectId)
		require.Equal(t, "10.10.0.0/16", out.SourceCidrBlock)
		require.Equal(t, "10.30.0.0/16", out.DestinationCidrBlock)
		require.Equal(t, regionalapipb.VpcPeering_ACTIVE, out.Status)
		gcpInfo, ok := out.Info.(*regionalapipb.VpcPeering_Gcp_)
		require.True(t, ok)
		require.Equal(t, "foo", gcpInfo.Gcp.SourceProjectId)
		require.Equal(t, "vpc-foo", gcpInfo.Gcp.SourceNetworkName)
		require.Equal(t, "bar", gcpInfo.Gcp.DestinationProjectId)
		require.Equal(t, "vpc-bar", gcpInfo.Gcp.DestinationNetworkName)
	}
	require.True(t, ctrl.Satisfied())
}

func TestVPCPeeringHandlerListVPCPeeringAws(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	ctrl := gomock.NewController(t)
	kubeClient := kubemock.NewMockClient(ctrl)
	kubeAPIReader := kubemock.NewMockReader(ctrl)
	svc, err := vpcpeeringsvc.NewService(
		kubeClient,
		kubeAPIReader,
		vpcpeeringsvc.WithProvider("aws"),
		vpcpeeringsvc.WithRegion("us-west-2"),
	)
	require.NoError(t, err)
	handler := vpcpeeringhandler.NewVPCPeeringHandler(svc)
	mountFn := router.MountVPCPeeringHandler(handler)
	mountFn(eng.Group("/api/dedicated/v1"))
	bs, err := testdata.LoadDefaultVPCPeeringYAML()
	require.NoError(t, err)
	defaultVPCPeering := new(v1alpha1.VPCPeering)
	require.NoError(t, yaml.Unmarshal(bs, defaultVPCPeering))
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/dedicated/v1/vpcPeerings?tenantId=foo", nil)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCPeeringList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 1 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/tenantId=foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCPeeringList, opts ...client.ListOption) error {
				item := defaultVPCPeering.DeepCopy()
				item.Spec.Provider = "aws"
				item.Spec.Region = "us-west-2"
				list.Items = []v1alpha1.VPCPeering{*item}
				return nil
			})
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusOK, w.Result().StatusCode)
		out := new(regionalapipb.ListVpcPeeringsResponse)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, 1, len(out.VpcPeerings))
		vpcpeering := out.VpcPeerings[0]
		require.Equal(t, "foo", vpcpeering.Id)
		require.Equal(t, "foo", vpcpeering.TenantId)
		require.Equal(t, "foo", vpcpeering.ProjectId)
		require.Equal(t, "10.10.0.0/16", vpcpeering.SourceCidrBlock)
		require.Equal(t, "10.30.0.0/16", vpcpeering.DestinationCidrBlock)
		require.Equal(t, regionalapipb.VpcPeering_ACTIVE, vpcpeering.Status)
		awsInfo, ok := vpcpeering.Info.(*regionalapipb.VpcPeering_Aws_)
		require.True(t, ok)
		require.Equal(t, "vpc-foo", awsInfo.Aws.SourceVpcId)
		require.Equal(t, "foo", awsInfo.Aws.SourceAccountId)
		require.Equal(t, "vpc-bar", awsInfo.Aws.DestinationVpcId)
		require.Equal(t, "bar", awsInfo.Aws.DestinationAccountId)
		require.Equal(t, "pcx-foobar", awsInfo.Aws.VpcPeeringConnectionId)
	}
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/dedicated/v1/vpcPeerings?tenantId=foo", nil)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCPeeringList{}),
				gomock.Any()).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCPeeringList, opts ...client.ListOption) error {
				list.Items = []v1alpha1.VPCPeering{}
				return nil
			})
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusOK, w.Result().StatusCode)
		out := new(regionalapipb.ListVpcPeeringsResponse)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, 0, len(out.VpcPeerings))
	}
	require.True(t, ctrl.Satisfied())
}

func TestVPCPeeringHandlerListVPCPeeringGCP(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	ctrl := gomock.NewController(t)
	kubeClient := kubemock.NewMockClient(ctrl)
	kubeAPIReader := kubemock.NewMockReader(ctrl)
	svc, err := vpcpeeringsvc.NewService(
		kubeClient,
		kubeAPIReader,
		vpcpeeringsvc.WithProvider("gcp"),
		vpcpeeringsvc.WithRegion("us-west1"),
	)
	require.NoError(t, err)
	handler := vpcpeeringhandler.NewVPCPeeringHandler(svc)
	mountFn := router.MountVPCPeeringHandler(handler)
	mountFn(eng.Group("/api/dedicated/v1"))
	bs, err := testdata.LoadDefaultVPCPeeringYAML()
	require.NoError(t, err)
	defaultVPCPeering := new(v1alpha1.VPCPeering)
	require.NoError(t, yaml.Unmarshal(bs, defaultVPCPeering))
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/dedicated/v1/vpcPeerings?tenantId=foo", nil)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCPeeringList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 1 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/tenantId=foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCPeeringList, opts ...client.ListOption) error {
				item := defaultVPCPeering.DeepCopy()
				item.Spec.Provider = "gcp"
				item.Spec.Region = "us-west1"
				item.Spec.Target.AWS = nil
				item.Spec.Target.GCP = &v1alpha1.GCP{
					ProjectID:   "bar",
					PeerNetwork: "vpc-bar",
					PeerCIDR:    "10.30.0.0/16",
				}
				list.Items = []v1alpha1.VPCPeering{*item}
				return nil
			})
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusOK, w.Result().StatusCode)
		out := new(regionalapipb.ListVpcPeeringsResponse)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, 1, len(out.VpcPeerings))
		vpcpeering := out.VpcPeerings[0]
		require.Equal(t, "foo", vpcpeering.Id)
		require.Equal(t, "foo", vpcpeering.TenantId)
		require.Equal(t, "foo", vpcpeering.ProjectId)
		require.Equal(t, "10.10.0.0/16", vpcpeering.SourceCidrBlock)
		require.Equal(t, "10.30.0.0/16", vpcpeering.DestinationCidrBlock)
		require.Equal(t, regionalapipb.VpcPeering_ACTIVE, vpcpeering.Status)
		gcpInfo, ok := vpcpeering.Info.(*regionalapipb.VpcPeering_Gcp_)
		require.True(t, ok)
		require.Equal(t, "foo", gcpInfo.Gcp.SourceProjectId)
		require.Equal(t, "vpc-foo", gcpInfo.Gcp.SourceNetworkName)
		require.Equal(t, "bar", gcpInfo.Gcp.DestinationProjectId)
		require.Equal(t, "vpc-bar", gcpInfo.Gcp.DestinationNetworkName)
	}
	require.True(t, ctrl.Satisfied())
}

func TestVPCPeeringHandlerDeleteVPCPeering(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	ctrl := gomock.NewController(t)
	kubeClient := kubemock.NewMockClient(ctrl)
	kubeAPIReader := kubemock.NewMockReader(ctrl)
	svc, err := vpcpeeringsvc.NewService(
		kubeClient,
		kubeAPIReader,
		vpcpeeringsvc.WithProvider("aws"),
		vpcpeeringsvc.WithRegion("us-west-2"),
	)
	require.NoError(t, err)
	handler := vpcpeeringhandler.NewVPCPeeringHandler(svc)
	mountFn := router.MountVPCPeeringHandler(handler)
	mountFn(eng.Group("/api/dedicated/v1"))
	bs, err := testdata.LoadDefaultVPCPeeringYAML()
	require.NoError(t, err)
	defaultVPCPeering := new(v1alpha1.VPCPeering)
	require.NoError(t, yaml.Unmarshal(bs, defaultVPCPeering))
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodDelete, "/api/dedicated/v1/vpcPeerings/foo", nil)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCPeeringList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 1 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/bizId=foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCPeeringList, opts ...client.ListOption) error {
				item := defaultVPCPeering.DeepCopy()
				list.Items = []v1alpha1.VPCPeering{*item}
				return nil
			})
		kubeClient.EXPECT().
			Delete(gomock.Any(),
				gomock.Cond(func(arg any) bool {
					cr, ok := arg.(*v1alpha1.VPCPeering)
					if !ok {
						return false
					}
					if cr.Name != "foo" || cr.Namespace != "foo" {
						return false
					}
					return true
				})).
			Return(nil)
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusOK, w.Result().StatusCode)
		out := new(regionalapipb.ListVpcPeeringsResponse)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
	}
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodDelete, "/api/dedicated/v1/vpcPeerings/foo", nil)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCPeeringList{}),
				gomock.Any()).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCPeeringList, opts ...client.ListOption) error {
				list.Items = []v1alpha1.VPCPeering{
					{
						ObjectMeta: metav1.ObjectMeta{
							Name:      "foo",
							Namespace: "foo",
						},
					},
				}
				return nil
			})
		kubeClient.EXPECT().
			Delete(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCPeering{})).
			Return(nil)
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusOK, w.Result().StatusCode)
		out := new(regionalapipb.ListVpcPeeringsResponse)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))

	}
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodDelete, "/api/dedicated/v1/vpcPeerings/foo", nil)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCPeeringList{}),
				gomock.Any()).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCPeeringList, opts ...client.ListOption) error {
				list.Items = []v1alpha1.VPCPeering{}
				return nil
			})
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusNotFound, w.Result().StatusCode)
	}
	require.True(t, ctrl.Satisfied())
}

func TestVPCPeeringHandlerGetAfterDeleteVPCPeering(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	ctrl := gomock.NewController(t)
	kubeClient := kubemock.NewMockClient(ctrl)
	kubeAPIReader := kubemock.NewMockReader(ctrl)
	svc, err := vpcpeeringsvc.NewService(
		kubeClient,
		kubeAPIReader,
		vpcpeeringsvc.WithProvider("aws"),
		vpcpeeringsvc.WithRegion("us-west-2"),
	)
	require.NoError(t, err)
	handler := vpcpeeringhandler.NewVPCPeeringHandler(svc)
	mountFn := router.MountVPCPeeringHandler(handler)
	mountFn(eng.Group("/api/dedicated/v1"))
	bs, err := testdata.LoadDefaultVPCPeeringYAML()
	require.NoError(t, err)
	defaultVPCPeering := new(v1alpha1.VPCPeering)
	require.NoError(t, yaml.Unmarshal(bs, defaultVPCPeering))
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/dedicated/v1/vpcPeerings/foo", nil)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCPeeringList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 1 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/bizId=foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCPeeringList, opts ...client.ListOption) error {
				item := defaultVPCPeering.DeepCopy()
				dtime := metav1.Now()
				item.SetDeletionTimestamp(&dtime)
				list.Items = []v1alpha1.VPCPeering{*item}
				return nil
			})
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusNotFound, w.Result().StatusCode)
	}
	require.True(t, ctrl.Satisfied())
}

func TestVPCPeeringHandlerListAfterDeleteVPCPeering(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	ctrl := gomock.NewController(t)
	kubeClient := kubemock.NewMockClient(ctrl)
	kubeAPIReader := kubemock.NewMockReader(ctrl)
	svc, err := vpcpeeringsvc.NewService(
		kubeClient,
		kubeAPIReader,
		vpcpeeringsvc.WithProvider("aws"),
		vpcpeeringsvc.WithRegion("us-west-2"),
	)
	require.NoError(t, err)
	handler := vpcpeeringhandler.NewVPCPeeringHandler(svc)
	mountFn := router.MountVPCPeeringHandler(handler)
	mountFn(eng.Group("/api/dedicated/v1"))
	bs, err := testdata.LoadDefaultVPCPeeringYAML()
	require.NoError(t, err)
	defaultVPCPeering := new(v1alpha1.VPCPeering)
	require.NoError(t, yaml.Unmarshal(bs, defaultVPCPeering))
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		ctx.Request, _ = http.NewRequest(http.MethodGet, "/api/dedicated/v1/vpcPeerings?tenantId=foo", nil)
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCPeeringList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 1 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/tenantId=foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCPeeringList, opts ...client.ListOption) error {
				item := defaultVPCPeering.DeepCopy()
				dtime := metav1.Now()
				item.SetDeletionTimestamp(&dtime)
				list.Items = []v1alpha1.VPCPeering{*item}
				return nil
			})
		eng.HandleContext(ctx)
		t.Log(w.Body.String())
		require.Equal(t, http.StatusOK, w.Result().StatusCode)
		out := new(regionalapipb.ListVpcPeeringsResponse)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, 0, len(out.VpcPeerings))
	}
	require.True(t, ctrl.Satisfied())
}

func TestVPCPeeringHandlerAcceptVPCPeering(t *testing.T) {
	gin.SetMode(gin.TestMode)
	eng := gin.Default()
	ctrl := gomock.NewController(t)
	kubeClient := kubemock.NewMockClient(ctrl)
	kubeAPIReader := kubemock.NewMockReader(ctrl)
	svc, err := vpcpeeringsvc.NewService(
		kubeClient,
		kubeAPIReader,
		vpcpeeringsvc.WithProvider("aws"),
		vpcpeeringsvc.WithRegion("us-west-2"),
	)
	require.NoError(t, err)
	handler := vpcpeeringhandler.NewVPCPeeringHandler(svc)
	mountFn := router.MountVPCPeeringHandler(handler)
	mountFn(eng.Group("/api/dedicated/v1"))
	{
		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		in := &regionalapipb.AcceptVpcPeeringRequest{
			AccepterVpcId:    "vpc-bar",
			RequesterVpcCidr: "10.2.0.0/24",
		}
		buf, err := protojson.Marshal(in)
		body := bytes.NewReader(buf)
		require.NoError(t, err)
		ctx.Request, _ = http.NewRequest(http.MethodPost, "/api/dedicated/v1/vpcPeerings/accept", body)
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
		out := new(spb.Status)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, int32(grpccodes.InvalidArgument), out.GetCode())
	}
	{
		kubeClient.EXPECT().
			List(gomock.Any(),
				gomock.AssignableToTypeOf(&v1alpha1.VPCList{}),
				gomock.Cond(func(arg any) bool {
					opts, ok := arg.([]client.ListOption)
					if !ok {
						return false
					}
					listOptions := new(client.ListOptions)
					for _, opt := range opts {
						opt.ApplyToList(listOptions)
					}
					ensureLabelSelector := func(s labels.Selector) bool {
						rs, _ := s.Requirements()
						if len(rs) != 1 {
							return false
						}
						if rs[0].String() != "cloud.pingcap.com/vpcId=vpc-foo" {
							return false
						}
						return true
					}
					if !ensureLabelSelector(listOptions.LabelSelector) {
						t.Log("label selector is unexpected")
						return false
					}
					return true
				})).
			DoAndReturn(func(ctx context.Context, list *v1alpha1.VPCList, opts ...client.ListOption) error {
				list.Items = []v1alpha1.VPC{
					{
						ObjectMeta: metav1.ObjectMeta{
							Name:      "vpc-foo",
							Namespace: "foo",
							Labels: map[string]string{
								"cloud.pingcap.com/vpcId": "vpc-foo",
								v1alpha1.TenantIDKey:      "tenant-foo",
								v1alpha1.ProjectIDKey:     "project-foo",
							},
						},
					},
				}
				return nil
			})
		kubeClient.EXPECT().
			Create(gomock.Any(), &v1alpha1.VPCPeering{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "pcx-foobar",
					Namespace: "foo",
					Labels: map[string]string{
						v1alpha1.LabelKeyBizID: "pcx-foobar",
						v1alpha1.VPCIDKey:      "vpc-foo",
						v1alpha1.TenantIDKey:   "tenant-foo",
						v1alpha1.ProjectIDKey:  "project-foo",
						v1alpha1.TagTenantKey:  "tenant-foo",
						v1alpha1.TagProjectKey: "project-foo",
					},
					Finalizers: []string{v1alpha1.FinalizerVPCPeering},
				},
				Spec: v1alpha1.VPCPeeringSpec{
					Provider: "aws",
					Region:   "us-west-2",
					SourceVPCRef: &v1alpha1.Reference{
						Name:      "vpc-foo",
						Namespace: "foo",
					},
					PeeringRole:            v1alpha1.PeeringRoleAccepter,
					VPCPeeringConnectionID: "pcx-foobar",
					Target: v1alpha1.Target{
						AWS: &v1alpha1.AWS{
							PeerCIDR: "10.2.0.0/24",
						},
					},
				},
			}).Return(nil)

		w := httptest.NewRecorder()
		ctx := gin.CreateTestContextOnly(w, eng)
		in := &regionalapipb.AcceptVpcPeeringRequest{
			AccepterVpcId:    "vpc-foo",
			VpcPeeringId:     "pcx-foobar",
			RequesterVpcCidr: "10.2.0.0/24",
		}
		buf, err := protojson.Marshal(in)
		body := bytes.NewReader(buf)
		require.NoError(t, err)
		ctx.Request, _ = http.NewRequest(http.MethodPost, "/api/dedicated/v1/vpcPeerings/accept", body)
		eng.HandleContext(ctx)
		require.Equal(t, http.StatusAccepted, w.Result().StatusCode)
		out := new(spb.Status)
		require.NoError(t, protojson.Unmarshal(w.Body.Bytes(), out))
		require.Equal(t, int32(grpccodes.OK), out.GetCode())
	}
}
