package vpc

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	regionalapiv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/regional-server/v1"
	"github.com/tidbcloud/infra-provider/pkg/consts"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/models"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/utils"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mockappbox"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mockapproute"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mockinternet"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mocknamespace"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mockvpc"
	"github.com/tidbcloud/infra-provider/regional-server/test/resourcemanager/mockvpcpeering"
	"github.com/tidbcloud/infra-provider/regional-server/test/service/mockaccount"
	"go.uber.org/mock/gomock"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

func TestCreateVPC(t *testing.T) {
	ctl := gomock.NewController(t)
	accountsvc := mockaccount.NewMockServiceInterface(ctl)
	vpcrw := mockvpc.NewMockVPCReaderWriter(ctl)
	namespacerw := mocknamespace.NewMockNamespaceReaderWriter(ctl)
	vpcsvc := NewVPCService(vpcrw, nil, nil, nil, nil, accountsvc, namespacerw, config.RootConfig{
		ControllerConfig: config.ControllerConfig{
			Provider: consts.ProviderTypeEKS,
			Env:      "test",
			RegionConfig: config.RegionConfig{
				Name: "us-west-1",
			},
		},
	})
	req := &regionalapiv1.CreateVpcRequest{
		RequestId: "request1",
		Vpc: &regionalapiv1.Vpc{
			TenantIds:  []string{"tenant1"},
			ProjectIds: []string{"project1"},
			CidrBlock:  "10.15.0.0/21",
		},
	}
	namer := utils.NewNamer(req.Vpc.TenantIds[0], req.Vpc.ProjectIds[0], "", vpcsvc.config.RegionConfig.Name, "test")
	t.Run("assign account failed", func(t *testing.T) {
		namespacerw.EXPECT().Get(context.Background(), client.ObjectKey{Name: namer.GetNamespace()}, &corev1.Namespace{}).Return(nil).Times(1)
		accountsvc.EXPECT().AssignAccount(
			context.Background(),
			namer.GetUniqueNameByInput(req.RequestId), req.Vpc.TenantIds[0], req.Vpc.ProjectIds[0]).Return(models.DataPlaneAccountInfo{}, "", assert.AnError).Times(1)
		_, err := vpcsvc.CreateVPC(context.Background(), req)
		assert.ErrorContains(t, err, "failed to assign account")
	})
	t.Run("create vpc failed", func(t *testing.T) {
		namespacerw.EXPECT().Get(context.Background(), client.ObjectKey{Name: namer.GetNamespace()}, &corev1.Namespace{}).Return(nil).Times(1)
		accountsvc.EXPECT().AssignAccount(
			gomock.Any(),
			namer.GetUniqueNameByInput(req.RequestId),
			req.Vpc.TenantIds[0],
			req.Vpc.ProjectIds[0],
		).Return(models.DataPlaneAccountInfo{
			AWSAccountInfo: &models.AWSAccountInfo{
				AWSAccount: "account1",
			},
		}, "provider1", nil).Times(1)
		vpcrw.EXPECT().Create(context.Background(), gomock.Any()).Return(assert.AnError).Times(1)
		_, err := vpcsvc.CreateVPC(context.Background(), req)
		assert.ErrorContains(t, err, "failed to create vpc")
	})
	t.Run("CreateVPC succeed", func(t *testing.T) {
		namespacerw.EXPECT().Get(context.Background(), client.ObjectKey{Name: namer.GetNamespace()}, &corev1.Namespace{}).Return(nil).Times(1)
		accountsvc.EXPECT().AssignAccount(
			gomock.Any(),
			namer.GetUniqueNameByInput(req.RequestId),
			req.Vpc.TenantIds[0],
			req.Vpc.ProjectIds[0],
		).Return(models.DataPlaneAccountInfo{
			AWSAccountInfo: &models.AWSAccountInfo{
				AWSAccount: "account1",
			},
		}, "provider1", nil).Times(1)
		vpcrw.EXPECT().Create(gomock.Any(), gomock.Any()).DoAndReturn(
			func(ctx context.Context, vpc *v1alpha1.VPC) error {
				assert.Equal(t, vpc.Name, namer.GetUniqueNameByInput(req.RequestId))
				assert.Equal(t, vpc.Namespace, namer.GetNamespace())
				assert.Equal(t, vpc.ObjectMeta.Labels[v1alpha1.RegionKey], vpcsvc.config.RegionConfig.Name)
				assert.Equal(t, vpc.ObjectMeta.Labels[namer.GetProjectLabelKey()], "true")
				assert.Equal(t, vpc.ObjectMeta.Labels[namer.GetTenantLabelKey()], "true")
				assert.Equal(t, vpc.ObjectMeta.Labels[v1alpha1.AccountIDKey], "account1")
				assert.Equal(t, vpc.Spec, v1alpha1.VPCSpec{
					CIDR:     req.Vpc.CidrBlock,
					Region:   vpcsvc.config.RegionConfig.Name,
					Provider: vpcsvc.config.Provider,
					AccountRef: &v1alpha1.AccountReference{
						AccountID:          "account1",
						ProviderConfigName: "provider1",
					},
				})
				return nil
			},
		).Times(1)
		res, err := vpcsvc.CreateVPC(context.Background(), req)
		assert.Nil(t, err)
		assert.Equal(t, req.Vpc.TenantIds, res.TenantIds)
		assert.Equal(t, req.Vpc.ProjectIds, res.ProjectIds)
		assert.Equal(t, req.Vpc.CidrBlock, res.CidrBlock)
	})
	t.Run("VPC already exists", func(t *testing.T) {
		namespacerw.EXPECT().Get(context.Background(), client.ObjectKey{Name: namer.GetNamespace()}, &corev1.Namespace{}).Return(nil).Times(1)
		accountsvc.EXPECT().AssignAccount(
			gomock.Any(),
			namer.GetUniqueNameByInput(req.RequestId), req.Vpc.TenantIds[0], req.Vpc.ProjectIds[0]).
			Return(models.DataPlaneAccountInfo{
				AWSAccountInfo: &models.AWSAccountInfo{
					AWSAccount: "account1",
				},
			}, "provider1", nil).Times(1)
		vpcrw.EXPECT().Create(gomock.Any(), gomock.Any()).Return(apierrors.NewAlreadyExists(schema.GroupResource{}, "vpc")).Times(1)
		_, err := vpcsvc.CreateVPC(context.Background(), req)
		assert.ErrorContains(t, err, "vpc already exists")
	})
	t.Run("namespace is not exist", func(t *testing.T) {
		namespacerw.EXPECT().Get(context.Background(), client.ObjectKey{Name: namer.GetNamespace()}, &corev1.Namespace{}).Return(apierrors.NewNotFound(schema.GroupResource{}, "namespace")).Times(1)
		namespacerw.EXPECT().Create(context.Background(), &corev1.Namespace{
			ObjectMeta: metav1.ObjectMeta{
				Name: namer.GetNamespace(),
			},
		}).Return(nil).Times(1)
		accountsvc.EXPECT().AssignAccount(
			gomock.Any(),
			namer.GetUniqueNameByInput(req.RequestId), req.Vpc.TenantIds[0], req.Vpc.ProjectIds[0]).
			Return(models.DataPlaneAccountInfo{
				AWSAccountInfo: &models.AWSAccountInfo{
					AWSAccount: "account1",
				},
			}, "provider1", nil).Times(1)
		vpcrw.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil).Times(1)
		_, err := vpcsvc.CreateVPC(context.Background(), req)
		assert.Nil(t, err)
	})
}

func TestDeleteVPC(t *testing.T) {
	ctl := gomock.NewController(t)
	accountsvc := mockaccount.NewMockServiceInterface(ctl)
	vpcrw := mockvpc.NewMockVPCReaderWriter(ctl)
	approuter := mockapproute.NewMockAPPRouteReader(ctl)
	appboxrw := mockappbox.NewMockAPPBoxReaderWriter(ctl)
	internetrw := mockinternet.NewMockInternetReaderWriter(ctl)
	vpcpeeringr := mockvpcpeering.NewMockVPCPeeringReaderWriter(ctl)
	namespacerw := mocknamespace.NewMockNamespaceReaderWriter(ctl)
	vpcsvc := NewVPCService(vpcrw, approuter, appboxrw, vpcpeeringr, internetrw, accountsvc, namespacerw, config.RootConfig{
		ControllerConfig: config.ControllerConfig{
			Provider: consts.ProviderTypeEKS,
			Env:      "test",
			RegionConfig: config.RegionConfig{
				Name: "us-west-1",
			},
		},
	})
	req := &regionalapiv1.DeleteVpcRequest{
		VpcId: "vpc1",
	}
	t.Run("list vpc error", func(t *testing.T) {
		vpcrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCIDKey: req.VpcId,
		}).Return(assert.AnError).Times(1)
		err := vpcsvc.DeleteVPC(context.Background(), req)
		assert.ErrorContains(t, err, "failed to list vpcs")
	})
	t.Run("delete vpc succeed", func(t *testing.T) {
		vpcrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCIDKey: req.VpcId,
		}).DoAndReturn(
			func(ctx context.Context, vpcs *v1alpha1.VPCList, opts ...client.ListOption) error {
				vpcs.Items = []v1alpha1.VPC{
					{
						ObjectMeta: metav1.ObjectMeta{
							Name: "vpc1",
							Labels: map[string]string{
								v1alpha1.VPCIDKey: "vpc1",
							},
						},
					},
				}
				return nil
			},
		).Times(1)
		approuter.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
		appboxrw.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
		internetrw.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
		vpcpeeringr.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
		vpcrw.EXPECT().Delete(context.Background(), &v1alpha1.VPC{
			ObjectMeta: metav1.ObjectMeta{
				Name: "vpc1",
				Labels: map[string]string{
					v1alpha1.VPCIDKey: "vpc1",
				},
			},
		}).Return(nil).Times(1)
		err := vpcsvc.DeleteVPC(context.Background(), req)
		assert.Nil(t, err)
	})
	t.Run("vpc is used", func(t *testing.T) {
		vpcrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCIDKey: req.VpcId,
		}).DoAndReturn(
			func(ctx context.Context, vpcs *v1alpha1.VPCList, opts ...client.ListOption) error {
				vpcs.Items = []v1alpha1.VPC{
					{
						ObjectMeta: metav1.ObjectMeta{
							Name: "vpc1",
							Labels: map[string]string{
								v1alpha1.VPCIDKey: "vpc1",
							},
						},
					},
				}
				return nil
			},
		).Times(6)
		approuter.EXPECT().List(context.Background(), &v1alpha1.APPRouteList{}, client.MatchingLabels{
			v1alpha1.VPCNameKey: "vpc1",
		}).DoAndReturn(func(ctx context.Context, routes *v1alpha1.APPRouteList, opts ...client.ListOption) error {
			routes.Items = []v1alpha1.APPRoute{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name: "route1",
						Labels: map[string]string{
							v1alpha1.VPCNameKey: "vpc1",
						},
					},
				},
			}
			return nil
		}).Times(1)
		err := vpcsvc.DeleteVPC(context.Background(), req)
		assert.ErrorContains(t, err, "used by approute")

		// list approute error
		approuter.EXPECT().List(context.Background(), &v1alpha1.APPRouteList{}, client.MatchingLabels{
			v1alpha1.VPCNameKey: "vpc1",
		}).Return(assert.AnError).Times(1)
		err = vpcsvc.DeleteVPC(context.Background(), req)
		assert.ErrorContains(t, err, "failed to list approutes")

		// used by appbox
		approuter.EXPECT().List(context.Background(), &v1alpha1.APPRouteList{}, client.MatchingLabels{
			v1alpha1.VPCNameKey: "vpc1",
		}).Return(nil).Times(1)
		vpcpeeringr.EXPECT().List(context.Background(), &v1alpha1.VPCPeeringList{}, client.MatchingLabels{
			v1alpha1.VPCIDKey: "vpc1",
		}).Return(nil).Times(1)
		appboxrw.EXPECT().List(context.Background(), &v1alpha1.APPBoxList{}, client.MatchingLabels{
			v1alpha1.VPCNameKey: "vpc1",
		}).DoAndReturn(func(ctx context.Context, boxes *v1alpha1.APPBoxList, opts ...client.ListOption) error {
			boxes.Items = []v1alpha1.APPBox{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name: "box1",
						Labels: map[string]string{
							v1alpha1.VPCNameKey: "vpc1",
						},
					},
				},
			}
			return nil
		}).Times(1)
		appboxrw.EXPECT().Delete(context.Background(), &v1alpha1.APPBox{
			ObjectMeta: metav1.ObjectMeta{
				Name: "box1",
				Labels: map[string]string{
					v1alpha1.VPCNameKey: "vpc1",
				},
			},
		}).Return(nil).Times(1)
		internetrw.EXPECT().List(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(1)
		vpcrw.EXPECT().Delete(context.Background(), &v1alpha1.VPC{
			ObjectMeta: metav1.ObjectMeta{
				Name: "vpc1",
				Labels: map[string]string{
					v1alpha1.VPCIDKey: "vpc1",
				},
			},
		}).Return(nil).Times(1)
		err = vpcsvc.DeleteVPC(context.Background(), req)
		assert.Nil(t, err)

		// list appbox error
		approuter.EXPECT().List(context.Background(), &v1alpha1.APPRouteList{}, client.MatchingLabels{
			v1alpha1.VPCNameKey: "vpc1",
		}).Return(nil).Times(1)
		vpcpeeringr.EXPECT().List(context.Background(), &v1alpha1.VPCPeeringList{}, client.MatchingLabels{
			v1alpha1.VPCIDKey: "vpc1",
		}).Return(nil).Times(1)
		appboxrw.EXPECT().List(context.Background(), &v1alpha1.APPBoxList{}, client.MatchingLabels{
			v1alpha1.VPCNameKey: "vpc1",
		}).Return(assert.AnError).Times(1)
		err = vpcsvc.DeleteVPC(context.Background(), req)
		assert.ErrorContains(t, err, "failed to list appboxe")

		// used by vpc peering
		approuter.EXPECT().List(context.Background(), &v1alpha1.APPRouteList{}, client.MatchingLabels{
			v1alpha1.VPCNameKey: "vpc1",
		}).Return(nil).Times(1)
		vpcpeeringr.EXPECT().List(context.Background(), &v1alpha1.VPCPeeringList{}, client.MatchingLabels{
			v1alpha1.VPCIDKey: "vpc1",
		}).DoAndReturn(func(ctx context.Context, peerings *v1alpha1.VPCPeeringList, opts ...client.ListOption) error {
			peerings.Items = []v1alpha1.VPCPeering{
				{
					ObjectMeta: metav1.ObjectMeta{
						Name: "peering1",
						Labels: map[string]string{
							v1alpha1.VPCIDKey: "vpc1",
						},
					},
				},
			}
			return nil
		}).Times(1)
		err = vpcsvc.DeleteVPC(context.Background(), req)
		assert.ErrorContains(t, err, "used by vpcpeering")

		// list vpc peering error
		approuter.EXPECT().List(context.Background(), &v1alpha1.APPRouteList{}, client.MatchingLabels{
			v1alpha1.VPCNameKey: "vpc1",
		}).Return(nil).Times(1)
		vpcpeeringr.EXPECT().List(context.Background(), &v1alpha1.VPCPeeringList{}, client.MatchingLabels{
			v1alpha1.VPCIDKey: "vpc1",
		}).Return(assert.AnError).Times(1)
		err = vpcsvc.DeleteVPC(context.Background(), req)
		assert.ErrorContains(t, err, "failed to list vpcpeering")
	})
}

func TestGetVPC(t *testing.T) {
	ctl := gomock.NewController(t)
	accountsvc := mockaccount.NewMockServiceInterface(ctl)
	vpcrw := mockvpc.NewMockVPCReaderWriter(ctl)
	internetrw := mockinternet.NewMockInternetReaderWriter(ctl)
	vpcsvc := NewVPCService(vpcrw, nil, nil, nil, internetrw, accountsvc, nil, config.RootConfig{
		ControllerConfig: config.ControllerConfig{
			Provider: consts.ProviderTypeEKS,
			Env:      "test",
			RegionConfig: config.RegionConfig{
				Name: "us-west-1",
			},
		},
	})
	req := &regionalapiv1.GetVpcRequest{
		VpcId: "vpc1",
	}
	t.Run("list vpc error", func(t *testing.T) {
		vpcrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCIDKey: req.VpcId,
		}).Return(assert.AnError).Times(1)
		_, err := vpcsvc.GetVPC(context.Background(), req)
		assert.ErrorContains(t, err, "failed to list vpcs")
	})
	t.Run("get vpc succeed", func(t *testing.T) {
		targetVPC := v1alpha1.VPC{
			ObjectMeta: metav1.ObjectMeta{
				Name: "vpc1",
				Labels: map[string]string{
					v1alpha1.VPCIDKey: "vpc1",
				},
			},
		}
		targetInternet := v1alpha1.Internet{
			ObjectMeta: metav1.ObjectMeta{
				Name: "internet-1",
				Labels: map[string]string{
					v1alpha1.VPCNameKey: "vpc1",
				},
			},
			Status: v1alpha1.InternetStatus{
				ExternalIPs: []string{"1.1.1.1"},
				Phase:       v1alpha1.InternetPhaseActive,
			},
		}
		vpcrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCIDKey: req.VpcId,
		}).DoAndReturn(
			func(ctx context.Context, vpcs *v1alpha1.VPCList, opts ...client.ListOption) error {
				vpcs.Items = []v1alpha1.VPC{targetVPC}
				return nil
			},
		).Times(1)
		internetrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCNameKey: targetVPC.Name,
		}).DoAndReturn(
			func(ctx context.Context, internets *v1alpha1.InternetList, opts ...client.ListOption) error {
				internets.Items = []v1alpha1.Internet{targetInternet}
				return nil
			},
		).Times(1)
		res, err := vpcsvc.GetVPC(context.Background(), req)
		assert.Nil(t, err)
		info, err := getVPCInfo(&targetVPC, &targetInternet, vpcsvc.config.Provider)
		assert.Nil(t, err)
		assert.Equal(t, info, res)
	})
	t.Run("vpc not found", func(t *testing.T) {
		vpcrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCIDKey: req.VpcId,
		}).DoAndReturn(
			func(ctx context.Context, vpcs *v1alpha1.VPCList, opts ...client.ListOption) error {
				vpcs.Items = []v1alpha1.VPC{}
				return nil
			},
		).Times(1)
		_, err := vpcsvc.GetVPC(context.Background(), req)
		assert.ErrorContains(t, err, "vpc not found")
	})
}

func TestListVPC(t *testing.T) {
	ctl := gomock.NewController(t)
	accountsvc := mockaccount.NewMockServiceInterface(ctl)
	vpcrw := mockvpc.NewMockVPCReaderWriter(ctl)
	internetrw := mockinternet.NewMockInternetReaderWriter(ctl)
	approuter := mockapproute.NewMockAPPRouteReader(ctl)
	vpcsvc := NewVPCService(vpcrw, approuter, nil, nil, internetrw, accountsvc, nil, config.RootConfig{
		ControllerConfig: config.ControllerConfig{
			Provider: consts.ProviderTypeEKS,
			Env:      "test",
			RegionConfig: config.RegionConfig{
				Name: "us-west-1",
			},
		},
	})
	req := &regionalapiv1.ListVpcsRequest{
		TenantId:  "tenant1",
		ProjectId: "project1",
	}
	t.Run("list vpc error", func(t *testing.T) {
		vpcrw.EXPECT().List(gomock.Any(), &v1alpha1.VPCList{}, client.MatchingLabels{
			utils.GenTenantLabelKey(req.TenantId):   "true",
			utils.GenProjectLabelKey(req.ProjectId): "true",
		}).Return(assert.AnError).Times(1)
		_, err := vpcsvc.ListVPC(context.Background(), req)
		assert.ErrorContains(t, err, "failed to list vpcs")
	})
	t.Run("list vpc succeed with tenantid and projectid", func(t *testing.T) {
		targetVPC := &v1alpha1.VPC{
			ObjectMeta: metav1.ObjectMeta{
				Name: "vpc1",
				Labels: map[string]string{
					utils.GenTenantLabelKey(req.TenantId):   "true",
					utils.GenProjectLabelKey(req.ProjectId): "true",
				},
			},
		}
		targetInternet := v1alpha1.Internet{
			ObjectMeta: metav1.ObjectMeta{
				Name: "internet-1",
				Labels: map[string]string{
					v1alpha1.VPCNameKey: "vpc1",
				},
			},
			Status: v1alpha1.InternetStatus{
				ExternalIPs: []string{"1.1.1.1"},
				Phase:       v1alpha1.InternetPhaseActive,
			},
		}
		vpcrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			utils.GenTenantLabelKey(req.TenantId):   "true",
			utils.GenProjectLabelKey(req.ProjectId): "true",
		}).DoAndReturn(
			func(ctx context.Context, vpcs *v1alpha1.VPCList, opts ...client.ListOption) error {
				vpcs.Items = []v1alpha1.VPC{*targetVPC}
				return nil
			},
		).Times(1)
		internetrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCNameKey: targetVPC.Name,
		}).DoAndReturn(
			func(ctx context.Context, internets *v1alpha1.InternetList, opts ...client.ListOption) error {
				internets.Items = []v1alpha1.Internet{targetInternet}
				return nil
			},
		).Times(1)
		res, err := vpcsvc.ListVPC(context.Background(), req)
		assert.Nil(t, err)
		info, err := getVPCInfo(targetVPC, &targetInternet, vpcsvc.config.Provider)
		assert.Nil(t, err)
		info.TenantIds = []string{req.TenantId}
		info.ProjectIds = []string{req.ProjectId}
		assert.Equal(t, &regionalapiv1.ListVpcsResponse{Vpcs: []*regionalapiv1.Vpc{
			info,
		}}, res)
	})
	t.Run("list vpc succeed with tenantid", func(t *testing.T) {
		req := &regionalapiv1.ListVpcsRequest{
			TenantId: "tenant1",
		}
		targetVPC := &v1alpha1.VPC{
			ObjectMeta: metav1.ObjectMeta{
				Name: "vpc1",
				Labels: map[string]string{
					utils.GenTenantLabelKey(req.TenantId): "true",
				},
			},
		}
		targetVPC2 := &v1alpha1.VPC{
			ObjectMeta: metav1.ObjectMeta{
				Name: "vpc2",
				Labels: map[string]string{
					utils.GenTenantLabelKey(req.TenantId): "true",
				},
			},
		}
		targetInternet := v1alpha1.Internet{
			ObjectMeta: metav1.ObjectMeta{
				Name: "internet-1",
				Labels: map[string]string{
					v1alpha1.VPCNameKey: "vpc1",
				},
			},
			Status: v1alpha1.InternetStatus{
				ExternalIPs: []string{"1.1.1.1"},
				Phase:       v1alpha1.InternetPhaseActive,
			},
		}
		targetInternet2 := v1alpha1.Internet{
			ObjectMeta: metav1.ObjectMeta{
				Name: "internet-2",
				Labels: map[string]string{
					v1alpha1.VPCNameKey: "vpc2",
				},
			},
			Status: v1alpha1.InternetStatus{
				ExternalIPs: []string{"2.2.2.2"},
				Phase:       v1alpha1.InternetPhaseActive,
			},
		}
		vpcrw.EXPECT().List(gomock.Any(), &v1alpha1.VPCList{}, client.MatchingLabels{
			utils.GenTenantLabelKey(req.TenantId): "true",
		}).DoAndReturn(
			func(ctx context.Context, vpcs *v1alpha1.VPCList, opts ...client.ListOption) error {
				vpcs.Items = []v1alpha1.VPC{*targetVPC, *targetVPC2}
				return nil
			},
		).Times(1)
		internetrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCNameKey: targetVPC.Name,
		}).DoAndReturn(
			func(ctx context.Context, internets *v1alpha1.InternetList, opts ...client.ListOption) error {
				internets.Items = []v1alpha1.Internet{targetInternet}
				return nil
			},
		).Times(1)
		internetrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCNameKey: targetVPC2.Name,
		}).DoAndReturn(
			func(ctx context.Context, internets *v1alpha1.InternetList, opts ...client.ListOption) error {
				internets.Items = []v1alpha1.Internet{targetInternet2}
				return nil
			},
		).Times(1)
		res, err := vpcsvc.ListVPC(context.Background(), req)
		assert.Nil(t, err)
		info, err := getVPCInfo(targetVPC, &targetInternet, vpcsvc.config.Provider)
		info.TenantIds = []string{req.TenantId}
		assert.Nil(t, err)
		info2, err := getVPCInfo(targetVPC2, &targetInternet2, vpcsvc.config.Provider)
		assert.Nil(t, err)
		info2.TenantIds = []string{req.TenantId}
		assert.Equal(t, &regionalapiv1.ListVpcsResponse{Vpcs: []*regionalapiv1.Vpc{
			info, info2,
		}}, res)
	})
	t.Run("list vpc succeed with clusterid", func(t *testing.T) {
		req := &regionalapiv1.ListVpcsRequest{
			TenantId:  "tenant1",
			ProjectId: "project1",
			ClusterId: "cluster1",
		}
		targetVPC := &v1alpha1.VPC{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "vpc1",
				Namespace: "default",
			},
		}
		targetInternet := v1alpha1.Internet{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "internet-1",
				Namespace: "default",
				Labels: map[string]string{
					v1alpha1.VPCNameKey: "vpc1",
				},
			},
			Status: v1alpha1.InternetStatus{
				ExternalIPs: []string{"1.1.1.1"},
				Phase:       v1alpha1.InternetPhaseActive,
			},
		}
		targetRoute := &v1alpha1.APPRoute{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "route1",
				Namespace: "default",
				Labels: map[string]string{
					utils.GenTenantLabelKey(req.TenantId):   "true",
					utils.GenProjectLabelKey(req.ProjectId): "true",
					utils.GenClusterLabelKey(req.ClusterId): "true",
					v1alpha1.VPCNameKey:                     "vpc1",
				},
			},
		}
		approuter.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			utils.GenTenantLabelKey(req.TenantId):   "true",
			utils.GenProjectLabelKey(req.ProjectId): "true",
			utils.GenClusterLabelKey(req.ClusterId): "true",
		}).DoAndReturn(
			func(ctx context.Context, routes *v1alpha1.APPRouteList, opts ...client.ListOption) error {
				routes.Items = []v1alpha1.APPRoute{*targetRoute}
				return nil
			},
		).Times(2)
		vpcrw.EXPECT().Get(gomock.Any(), client.ObjectKey{
			Namespace: targetRoute.Namespace,
			Name:      targetRoute.Labels[v1alpha1.VPCNameKey],
		}, &v1alpha1.VPC{}).DoAndReturn(
			func(ctx context.Context, key client.ObjectKey, vpc *v1alpha1.VPC) error {
				vpc.ObjectMeta = targetVPC.ObjectMeta
				return nil
			},
		).Times(1)
		internetrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCNameKey: targetVPC.Name,
		}).DoAndReturn(
			func(ctx context.Context, internets *v1alpha1.InternetList, opts ...client.ListOption) error {
				internets.Items = []v1alpha1.Internet{targetInternet}
				return nil
			},
		).Times(1)
		resp, err := vpcsvc.ListVPC(context.Background(), req)
		assert.Nil(t, err)
		info, err := getVPCInfo(targetVPC, &targetInternet, vpcsvc.config.Provider)
		assert.Nil(t, err)
		assert.Equal(t, &regionalapiv1.ListVpcsResponse{Vpcs: []*regionalapiv1.Vpc{
			info,
		}}, resp)

		targetVPC.Status.Phase = v1alpha1.VPCPhaseCreating
		req.Statuses = []regionalapiv1.Vpc_Status{regionalapiv1.Vpc_ACTIVE}
		vpcrw.EXPECT().Get(gomock.Any(), client.ObjectKey{
			Namespace: targetRoute.Namespace,
			Name:      targetRoute.Labels[v1alpha1.VPCNameKey],
		}, &v1alpha1.VPC{}).DoAndReturn(
			func(ctx context.Context, key client.ObjectKey, vpc *v1alpha1.VPC) error {
				vpc.ObjectMeta = targetVPC.ObjectMeta
				return nil
			},
		).Times(1)
		resp, err = vpcsvc.ListVPC(context.Background(), req)
		assert.Nil(t, err)
		assert.True(t, len(resp.Vpcs) == 0)
	})

	t.Run("list active active vpc", func(t *testing.T) {
		req := &regionalapiv1.ListVpcsRequest{
			TenantId:  "tenant1",
			ProjectId: "project1",
			Statuses:  []regionalapiv1.Vpc_Status{regionalapiv1.Vpc_ACTIVE},
		}
		targetVPC_createing := &v1alpha1.VPC{
			ObjectMeta: metav1.ObjectMeta{
				Name: "vpc1",
				Labels: map[string]string{
					utils.GenTenantLabelKey(req.TenantId):   "true",
					utils.GenProjectLabelKey(req.ProjectId): "true",
				},
			},
			Status: v1alpha1.VPCStatus{
				Phase: v1alpha1.VPCPhaseCreating,
			},
		}
		targetVPC_active := &v1alpha1.VPC{
			ObjectMeta: metav1.ObjectMeta{
				Name: "vpc2",
				Labels: map[string]string{
					utils.GenTenantLabelKey(req.TenantId):   "true",
					utils.GenProjectLabelKey(req.ProjectId): "true",
				},
			},
			Status: v1alpha1.VPCStatus{
				Phase: v1alpha1.VPCPhaseActive,
			},
		}
		targetInternet2 := v1alpha1.Internet{
			ObjectMeta: metav1.ObjectMeta{
				Name: "internet-2",
				Labels: map[string]string{
					v1alpha1.VPCNameKey: "vpc2",
				},
			},
			Status: v1alpha1.InternetStatus{
				ExternalIPs: []string{"2.2.2.2"},
				Phase:       v1alpha1.InternetPhaseActive,
			},
		}
		vpcrw.EXPECT().List(gomock.Any(), &v1alpha1.VPCList{}, client.MatchingLabels{
			utils.GenTenantLabelKey(req.TenantId):   "true",
			utils.GenProjectLabelKey(req.ProjectId): "true",
		}).DoAndReturn(
			func(ctx context.Context, vpcs *v1alpha1.VPCList, opts ...client.ListOption) error {
				vpcs.Items = []v1alpha1.VPC{*targetVPC_createing, *targetVPC_active}
				return nil
			},
		).Times(1)
		internetrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCNameKey: targetVPC_active.Name,
		}).DoAndReturn(
			func(ctx context.Context, internets *v1alpha1.InternetList, opts ...client.ListOption) error {
				internets.Items = []v1alpha1.Internet{targetInternet2}
				return nil
			},
		).Times(1)
		res, err := vpcsvc.ListVPC(context.Background(), req)
		assert.Nil(t, err)
		assert.True(t, len(res.Vpcs) == 1)
		assert.True(t, res.Vpcs[0].Status == regionalapiv1.Vpc_ACTIVE)
	})
}

func TestQueryClusterIds(t *testing.T) {
	ctl := gomock.NewController(t)
	accountsvc := mockaccount.NewMockServiceInterface(ctl)
	vpcrw := mockvpc.NewMockVPCReaderWriter(ctl)
	approuter := mockapproute.NewMockAPPRouteReader(ctl)
	vpcsvc := NewVPCService(vpcrw, approuter, nil, nil, nil, accountsvc, nil, config.RootConfig{
		ControllerConfig: config.ControllerConfig{
			Provider: consts.ProviderTypeEKS,
			Env:      "test",
			RegionConfig: config.RegionConfig{
				Name: "us-west-1",
			},
		},
	})
	t.Run("query cluster ids succeed", func(t *testing.T) {
		req := &regionalapiv1.QueryVpcClusterIdsRequest{
			TenantId:  "tenant1",
			ProjectId: "project1",
			Info: &regionalapiv1.QueryVpcClusterIdsRequest_Aws{
				Aws: &regionalapiv1.Vpc_AwsVpcInfo{
					AccountId: "account1",
					VpcId:     "vpc1",
				},
			},
		}
		targetRoute := &v1alpha1.APPRoute{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "route1",
				Namespace: "default",
				Labels: map[string]string{
					v1alpha1.VPCNameKey:                     "vpc1-name",
					utils.GenTenantLabelKey(req.TenantId):   "true",
					utils.GenProjectLabelKey(req.ProjectId): "true",
					v1alpha1.ClusterIDKey:                   "route1",
				},
			},
		}
		targetVPC := &v1alpha1.VPC{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "vpc1-name",
				Namespace: "default",
				Labels: map[string]string{
					v1alpha1.VPCIDKey:     "vpc1",
					v1alpha1.AccountIDKey: "account1",
				},
			},
		}
		vpcrw.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCIDKey:     req.GetAws().VpcId,
			v1alpha1.AccountIDKey: req.GetAws().AccountId,
		}).DoAndReturn(
			func(ctx context.Context, vpcs *v1alpha1.VPCList, opts ...client.ListOption) error {
				vpcs.Items = []v1alpha1.VPC{*targetVPC}
				return nil
			},
		).Times(1)
		approuter.EXPECT().List(gomock.Any(), gomock.Any(), client.MatchingLabels{
			v1alpha1.VPCNameKey:                     targetVPC.Name,
			utils.GenTenantLabelKey(req.TenantId):   "true",
			utils.GenProjectLabelKey(req.ProjectId): "true",
		}).DoAndReturn(
			func(ctx context.Context, routes *v1alpha1.APPRouteList, opts ...client.ListOption) error {
				routes.Items = []v1alpha1.APPRoute{*targetRoute}
				return nil
			},
		).Times(1)

		resp, err := vpcsvc.QueryClusterIds(context.Background(), req)
		assert.Nil(t, err)
		assert.Equal(t, &regionalapiv1.QueryVpcClusterIdsResponse{
			ClusterIds: []string{"route1"},
		}, resp)
	})
}

func Test_setVPCStatus(t *testing.T) {
	cases := []struct {
		name     string
		phase    v1alpha1.VPCPhase
		expected regionalapiv1.Vpc_Status
	}{
		{
			name:     "phase is empty",
			phase:    "",
			expected: regionalapiv1.Vpc_CREATING,
		},
		{
			name:     "phase is pending",
			phase:    v1alpha1.VPCPhasePending,
			expected: regionalapiv1.Vpc_CREATING,
		},
		{
			name:     "phase is creating",
			phase:    v1alpha1.VPCPhaseCreating,
			expected: regionalapiv1.Vpc_CREATING,
		},
		{
			name:     "phase is readyforcreate",
			phase:    v1alpha1.VPCPhaseReadyForCreate,
			expected: regionalapiv1.Vpc_CREATING,
		},
		{
			name:     "phase is active",
			phase:    v1alpha1.VPCPhaseActive,
			expected: regionalapiv1.Vpc_ACTIVE,
		},
		{
			name:     "phase is deleting",
			phase:    v1alpha1.VPCPhaseDeleting,
			expected: regionalapiv1.Vpc_DELETING,
		},
		{
			name:     "phase is deleted",
			phase:    v1alpha1.VPCPhaseDeleted,
			expected: regionalapiv1.Vpc_DELETING,
		},
		{
			name:     "phase is readyfordelete",
			phase:    v1alpha1.VPCPhaseReadyForDelete,
			expected: regionalapiv1.Vpc_DELETING,
		},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			out := getVPCStatus(c.phase)
			assert.Equal(t, c.expected, out)
		})
	}
}

func Test_meetVPCStatus(t *testing.T) {
	cases := []struct {
		name     string
		statuses []regionalapiv1.Vpc_Status
		phase    v1alpha1.VPCPhase
		expected bool
	}{
		{
			name:     "empty statuses",
			statuses: []regionalapiv1.Vpc_Status{},
			phase:    v1alpha1.VPCPhaseActive,
			expected: false,
		},
		{
			name:     "meet status",
			statuses: []regionalapiv1.Vpc_Status{regionalapiv1.Vpc_ACTIVE},
			phase:    v1alpha1.VPCPhaseActive,
			expected: true,
		},
		{
			name:     "not meet status",
			statuses: []regionalapiv1.Vpc_Status{regionalapiv1.Vpc_ACTIVE},
			phase:    v1alpha1.VPCPhaseCreating,
			expected: false,
		},
		{
			name:     "meet multiple statuses",
			statuses: []regionalapiv1.Vpc_Status{regionalapiv1.Vpc_ACTIVE, regionalapiv1.Vpc_CREATING},
			phase:    v1alpha1.VPCPhaseActive,
			expected: true,
		},
	}

	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			out := meetVPCStatus(c.phase, c.statuses)
			assert.Equal(t, c.expected, out)
		})
	}
}
