package aks

import (
	"context"
	"fmt"
	"strings"
	"testing"
	"time"

	v1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	"github.com/crossplane/crossplane-runtime/pkg/meta"
	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/cidr"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers"
	"github.com/tidbcloud/infra-provider/regional-server/test/kubemock"
	"github.com/upbound/provider-azure/apis/network/v1beta1"
	"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"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

func fakeSubnetTask(t *testing.T) *Subnet {
	return NewSubnet(fakeTaskContext(t))
}

func TestSubnet_getPodSubnetName(t *testing.T) {
	subnetTask := fakeSubnetTask(t)
	assert.True(t, strings.HasSuffix(subnetTask.getPodSubnetName(), "-pod"))
}

func TestSubnet_getNodeSubnetName(t *testing.T) {
	subnetTask := fakeSubnetTask(t)
	assert.True(t, strings.HasSuffix(subnetTask.getNodeSubnetName(), "-node"))
}

func TestSubnet_getPLSubnetName(t *testing.T) {
	subnetTask := fakeSubnetTask(t)
	assert.True(t, strings.HasSuffix(subnetTask.getPLSSubnetName(), "-pls"))
}

func TestSubnet_getAPIServerSubnetName(t *testing.T) {
	subnetTask := fakeSubnetTask(t)
	assert.True(t, strings.HasSuffix(subnetTask.getAPIServerSubnetName(), "-apiserver"))
}

func TestSubnet_createSubnet(t *testing.T) {
	subnetTask := fakeSubnetTask(t)
	t.Run("create subnet success", func(t *testing.T) {
		subnetName := "test-subnet"
		cidr := "10.0.0.0/24"
		assert.Nil(t, subnetTask.createSubnet(context.Background(), subnetName, cidr, v1alpha1.NodeSubnetType, true))
		subnet := &v1beta1.Subnet{}
		assert.Nil(t, subnetTask.Local.Get(context.Background(), types.NamespacedName{Name: subnetName}, subnet))
		labels := map[string]string{
			v1alpha1.VPCNameKey:    subnetTask.VPC.Name,
			v1alpha1.SubnetTypeKey: v1alpha1.NodeSubnetType,
		}
		assert.Equal(t, labels, subnet.Labels)
		assert.Equal(t, v1beta1.SubnetSpec{
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{
					Name: subnetTask.ProviderConfigName(),
				},
			},
			ForProvider: v1beta1.SubnetParameters{
				AddressPrefixes:                          []*string{ptr.To(cidr)},
				ResourceGroupNameRef:                     &xpv1.Reference{Name: controllers.GetResourceGroupName(subnetTask.VPC)},
				VirtualNetworkNameRef:                    &xpv1.Reference{Name: getVNetName(subnetTask.VPC)},
				PrivateLinkServiceNetworkPoliciesEnabled: ptr.To(true),
			},
		}, subnet.Spec)
	})
	t.Run("create subnet failed", func(t *testing.T) {
		ctl := gomock.NewController(t)
		mockcli := kubemock.NewMockClient(ctl)
		subnetTask.Local = mockcli
		mockcli.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(schema.GroupResource{}, "not found")).Times(1)
		mockcli.EXPECT().Create(gomock.Any(), gomock.Any()).Return(assert.AnError).Times(1)
		result := subnetTask.Create(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.NotNil(t, retriableErr)
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
	})
}

func TestSubnet_deleteSubnet(t *testing.T) {
	subnetTask := fakeSubnetTask(t)
	t.Run("delete subnet success", func(t *testing.T) {
		subnetName := "test-subnet"
		subnet := &v1beta1.Subnet{
			ObjectMeta: metav1.ObjectMeta{
				Name: subnetName,
			},
		}
		assert.Nil(t, subnetTask.Local.Create(context.Background(), subnet))
		assert.Nil(t, subnetTask.deleteSubnet(context.Background(), subnetName))
		err := subnetTask.Local.Get(context.Background(), types.NamespacedName{Name: subnetName}, subnet)
		assert.Contains(t, err.Error(), "not found")
	})
	t.Run("delete subnet failed", func(t *testing.T) {
		ctl := gomock.NewController(t)
		mockcli := kubemock.NewMockClient(ctl)
		subnetTask.Local = mockcli
		subnetName := "test-subnet"
		mockcli.EXPECT().Delete(gomock.Any(), &v1beta1.Subnet{
			ObjectMeta: metav1.ObjectMeta{
				Name: subnetName,
			},
		}).Return(assert.AnError).Times(1)
		assert.NotNil(t, subnetTask.deleteSubnet(context.Background(), subnetName))
	})
}

func TestSubnet_Create(t *testing.T) {
	subnetTask := fakeSubnetTask(t)
	t.Run("Create subnet success", func(t *testing.T) {
		result := subnetTask.Create(context.Background())
		assert.True(t, result.IsDoneState())
		podSubnet := &v1beta1.Subnet{}
		err := subnetTask.Local.Get(context.Background(), types.NamespacedName{Name: subnetTask.getPodSubnetName()}, podSubnet)
		assert.Nil(t, err)
		labels := map[string]string{
			v1alpha1.VPCNameKey:    subnetTask.VPC.Name,
			v1alpha1.SubnetTypeKey: v1alpha1.PodSubnetType,
		}
		assert.Equal(t, labels, podSubnet.Labels)
		cidrDispatcher, err := cidr.NewAKSCIDRDispatcher(subnetTask.VPC.Spec.CIDR)
		assert.Nil(t, err)
		assert.Nil(t, cidrDispatcher.Allocate())
		assert.Equal(t, v1beta1.SubnetSpec{
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{
					Name: subnetTask.ProviderConfigName(),
				},
			},
			ForProvider: v1beta1.SubnetParameters{
				AddressPrefixes:                          []*string{ptr.To(cidrDispatcher.PodIPv4CIDRBlock())},
				ResourceGroupNameRef:                     &xpv1.Reference{Name: controllers.GetResourceGroupName(subnetTask.VPC)},
				VirtualNetworkNameRef:                    &xpv1.Reference{Name: getVNetName(subnetTask.VPC)},
				PrivateLinkServiceNetworkPoliciesEnabled: ptr.To(true),
			},
		}, podSubnet.Spec)

		nodeSubnet := &v1beta1.Subnet{}
		err = subnetTask.Local.Get(context.Background(), types.NamespacedName{Name: subnetTask.getNodeSubnetName()}, nodeSubnet)
		assert.Nil(t, err)
		labels[v1alpha1.SubnetTypeKey] = v1alpha1.NodeSubnetType
		assert.Equal(t, labels, nodeSubnet.Labels)
		assert.Equal(t, v1beta1.SubnetSpec{
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{
					Name: subnetTask.ProviderConfigName(),
				},
			},
			ForProvider: v1beta1.SubnetParameters{
				AddressPrefixes:                          []*string{ptr.To(cidrDispatcher.NodeIPv4CIDRBlock())},
				ResourceGroupNameRef:                     &xpv1.Reference{Name: controllers.GetResourceGroupName(subnetTask.VPC)},
				VirtualNetworkNameRef:                    &xpv1.Reference{Name: getVNetName(subnetTask.VPC)},
				PrivateLinkServiceNetworkPoliciesEnabled: ptr.To(true),
			},
		}, nodeSubnet.Spec)

		plsSubnet := &v1beta1.Subnet{}
		err = subnetTask.Local.Get(context.Background(), types.NamespacedName{Name: subnetTask.getPLSSubnetName()}, plsSubnet)
		assert.Nil(t, err)
		labels[v1alpha1.SubnetTypeKey] = v1alpha1.PLSNATSubnetType
		assert.Equal(t, labels, plsSubnet.Labels)
		assert.Equal(t, v1beta1.SubnetSpec{
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{
					Name: subnetTask.ProviderConfigName(),
				},
			},
			ForProvider: v1beta1.SubnetParameters{
				AddressPrefixes:                          []*string{ptr.To(cidrDispatcher.PLSNATIPv4CIDRBlock())},
				ResourceGroupNameRef:                     &xpv1.Reference{Name: controllers.GetResourceGroupName(subnetTask.VPC)},
				VirtualNetworkNameRef:                    &xpv1.Reference{Name: getVNetName(subnetTask.VPC)},
				PrivateLinkServiceNetworkPoliciesEnabled: ptr.To(false),
			},
		}, plsSubnet.Spec)

		apiServerSubnet := &v1beta1.Subnet{}
		err = subnetTask.Local.Get(context.Background(), types.NamespacedName{Name: subnetTask.getAPIServerSubnetName()}, apiServerSubnet)
		assert.Nil(t, err)
		labels[v1alpha1.SubnetTypeKey] = v1alpha1.APIServerSubnetType
		assert.Equal(t, labels, apiServerSubnet.Labels)
		assert.Equal(t, v1beta1.SubnetSpec{
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{
					Name: subnetTask.ProviderConfigName(),
				},
			},
			ForProvider: v1beta1.SubnetParameters{
				AddressPrefixes:                          []*string{ptr.To(cidrDispatcher.APIServerIPv4CIDRBlock())},
				ResourceGroupNameRef:                     &xpv1.Reference{Name: controllers.GetResourceGroupName(subnetTask.VPC)},
				VirtualNetworkNameRef:                    &xpv1.Reference{Name: getVNetName(subnetTask.VPC)},
				PrivateLinkServiceNetworkPoliciesEnabled: ptr.To(true),
				Delegation: []v1beta1.DelegationParameters{
					{
						Name: ptr.To("aks-apiserver"),
						ServiceDelegation: []v1beta1.ServiceDelegationParameters{
							{
								Name: ptr.To("Microsoft.ContainerService/managedClusters"),
							},
						},
					},
				},
			},
		}, apiServerSubnet.Spec)
	})

	t.Run("create pod subnet failed", func(t *testing.T) {
		ctl := gomock.NewController(t)
		mockcli := kubemock.NewMockClient(ctl)
		subnetTask.Local = mockcli
		mockcli.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(schema.GroupResource{}, "not found")).Times(1)
		mockcli.EXPECT().Create(gomock.Any(), gomock.Any()).Return(assert.AnError).Times(1)
		result := subnetTask.Create(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.NotNil(t, retriableErr)
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
	})
	t.Run("create node subnet failed", func(t *testing.T) {
		ctl := gomock.NewController(t)
		mockcli := kubemock.NewMockClient(ctl)
		subnetTask.Local = mockcli
		mockcli.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(schema.GroupResource{}, "not found")).Times(2)
		mockcli.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil).Times(1)
		mockcli.EXPECT().Create(gomock.Any(), gomock.Any()).Return(assert.AnError).Times(1)
		result := subnetTask.Create(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.NotNil(t, retriableErr)
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
	})
	t.Run("create pls subnet failed", func(t *testing.T) {
		ctl := gomock.NewController(t)
		mockcli := kubemock.NewMockClient(ctl)
		subnetTask.Local = mockcli
		mockcli.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(schema.GroupResource{}, "not found")).Times(3)
		mockcli.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil).Times(2)
		mockcli.EXPECT().Create(gomock.Any(), gomock.Any()).Return(assert.AnError).Times(1)
		result := subnetTask.Create(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.NotNil(t, retriableErr)
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
	})
	t.Run("create api server subnet failed", func(t *testing.T) {
		ctl := gomock.NewController(t)
		mockcli := kubemock.NewMockClient(ctl)
		subnetTask.Local = mockcli
		mockcli.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(schema.GroupResource{}, "not found")).Times(4)
		mockcli.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil).Times(3)
		mockcli.EXPECT().Create(gomock.Any(), gomock.Any()).Return(assert.AnError).Times(1)
		result := subnetTask.Create(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.NotNil(t, retriableErr)
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
	})
}

func TestSubnet_checkSubnet(t *testing.T) {
	t.Run("subnet is not found", func(t *testing.T) {
		subnetTask := fakeSubnetTask(t)
		ctl := gomock.NewController(t)
		mockcli := kubemock.NewMockClient(ctl)
		subnetTask.Local = mockcli
		mockcli.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(apierrors.NewNotFound(schema.GroupResource{}, "not found")).Times(1)
		_, subnetReady, err := subnetTask.checkSubnet("test-subnet")
		assert.False(t, subnetReady)
		assert.Contains(t, err.Error(), "not found")
	})
	t.Run("subnet is not ready", func(t *testing.T) {
		subnetTask := fakeSubnetTask(t)
		subnetName := "test-subnet"
		subnet := &v1beta1.Subnet{
			ObjectMeta: metav1.ObjectMeta{
				Name: subnetName,
			},
		}
		assert.Nil(t, subnetTask.Local.Create(context.Background(), subnet))
		_, subnetReady, err := subnetTask.checkSubnet(subnetName)
		assert.False(t, subnetReady)
		assert.Contains(t, err.Error(), fmt.Sprintf("subnet %s is not ready", subnetName))
	})

	t.Run("subnet has no address prefixes", func(t *testing.T) {
		subnetTask := fakeSubnetTask(t)
		subnetName := "test-subnet"
		subnet := &v1beta1.Subnet{
			ObjectMeta: metav1.ObjectMeta{
				Name: subnetName,
			},
			Status: v1beta1.SubnetStatus{
				ResourceStatus: v1.ResourceStatus{
					ConditionedStatus: xpv1.ConditionedStatus{
						Conditions: []xpv1.Condition{
							{
								Type:   xpv1.TypeReady,
								Status: corev1.ConditionTrue,
							},
						},
					},
				},
			},
		}
		assert.Nil(t, subnetTask.Local.Create(context.Background(), subnet))
		_, subnetReady, err := subnetTask.checkSubnet(subnetName)
		assert.False(t, subnetReady)
		assert.Contains(t, err.Error(), fmt.Sprintf("subnet %s has no address prefixes", subnetName))
	})
	t.Run("check subnet success", func(t *testing.T) {
		subnetTask := fakeSubnetTask(t)
		subnetName := "test-subnet"
		subnet := &v1beta1.Subnet{
			ObjectMeta: metav1.ObjectMeta{
				Name: subnetName,
				Annotations: map[string]string{
					meta.AnnotationKeyExternalName: "test-id",
				},
			},
			Spec: v1beta1.SubnetSpec{
				ForProvider: v1beta1.SubnetParameters{
					AddressPrefixes: []*string{ptr.To("10.0.0.0/23")},
				},
			},
			Status: v1beta1.SubnetStatus{
				AtProvider: v1beta1.SubnetObservation{
					ID: ptr.To("test-subnet-id"),
				},
				ResourceStatus: v1.ResourceStatus{
					ConditionedStatus: xpv1.ConditionedStatus{
						Conditions: []xpv1.Condition{
							{
								Type:   xpv1.TypeReady,
								Status: corev1.ConditionTrue,
							},
						},
					},
				},
			},
		}
		assert.Nil(t, subnetTask.Local.Create(context.Background(), subnet))
		subnetStatus, subnetReady, err := subnetTask.checkSubnet(subnetName)
		assert.True(t, subnetReady)
		assert.Nil(t, err)
		assert.Equal(t, v1alpha1.Subnet{
			Name:      subnetName,
			SubnetID:  "test-subnet-id",
			CIDRBlock: "10.0.0.0/23",
		}, subnetStatus)
	})
}

func TestSubnet_PostCreate(t *testing.T) {
	createSubnetF := func(cli client.Client, name string, cidr string, isReady corev1.ConditionStatus) {
		assert.Nil(t, cli.Create(context.Background(), &v1beta1.Subnet{
			ObjectMeta: metav1.ObjectMeta{
				Name: name,
				Annotations: map[string]string{
					meta.AnnotationKeyExternalName: fmt.Sprintf("%s-id", name),
				},
			},
			Spec: v1beta1.SubnetSpec{
				ForProvider: v1beta1.SubnetParameters{
					AddressPrefixes: []*string{ptr.To(cidr)},
				},
			},
			Status: v1beta1.SubnetStatus{
				AtProvider: v1beta1.SubnetObservation{
					ID: ptr.To(fmt.Sprintf("%s-id", name)),
				},
				ResourceStatus: v1.ResourceStatus{
					ConditionedStatus: xpv1.ConditionedStatus{
						Conditions: []xpv1.Condition{
							{
								Type:   xpv1.TypeReady,
								Status: isReady,
							},
						},
					},
				},
			},
		}))
	}
	t.Run("PostCreate success", func(t *testing.T) {
		subnetTask := fakeSubnetTask(t)
		cidrDispatcher, err := cidr.NewAKSCIDRDispatcher(subnetTask.VPC.Spec.CIDR)
		assert.Nil(t, err)
		assert.Nil(t, cidrDispatcher.Allocate())
		createSubnetF(subnetTask.Local, subnetTask.getPodSubnetName(), cidrDispatcher.PodIPv4CIDRBlock(), corev1.ConditionTrue)
		createSubnetF(subnetTask.Local, subnetTask.getNodeSubnetName(), cidrDispatcher.NodeIPv4CIDRBlock(), corev1.ConditionTrue)
		createSubnetF(subnetTask.Local, subnetTask.getPLSSubnetName(), cidrDispatcher.PLSNATIPv4CIDRBlock(), corev1.ConditionTrue)
		createSubnetF(subnetTask.Local, subnetTask.getAPIServerSubnetName(), cidrDispatcher.APIServerIPv4CIDRBlock(), corev1.ConditionTrue)

		result := subnetTask.PostCreate(context.Background())
		assert.True(t, result.IsDoneState())
		vpc := &v1alpha1.VPC{}
		assert.Nil(t, subnetTask.Local.Get(context.Background(), types.NamespacedName{Name: subnetTask.VPC.Name, Namespace: subnetTask.VPC.Namespace}, vpc))
		assert.Equal(t, []v1alpha1.Subnet{
			{
				Name:      subnetTask.getPodSubnetName(),
				SubnetID:  fmt.Sprintf("%s-id", subnetTask.getPodSubnetName()),
				CIDRBlock: cidrDispatcher.PodIPv4CIDRBlock(),
			},
		}, vpc.Status.PodSubnets)
		assert.Equal(t, []v1alpha1.Subnet{
			{
				Name:      subnetTask.getNodeSubnetName(),
				SubnetID:  fmt.Sprintf("%s-id", subnetTask.getNodeSubnetName()),
				CIDRBlock: cidrDispatcher.NodeIPv4CIDRBlock(),
			},
		}, vpc.Status.NodeSubnets)
		assert.Equal(t, []v1alpha1.Subnet{
			{
				Name:      subnetTask.getPLSSubnetName(),
				SubnetID:  fmt.Sprintf("%s-id", subnetTask.getPLSSubnetName()),
				CIDRBlock: cidrDispatcher.PLSNATIPv4CIDRBlock(),
			},
		}, vpc.Status.PLSNATSubnets)
		assert.Equal(t, []v1alpha1.Subnet{
			{
				Name:      subnetTask.getAPIServerSubnetName(),
				SubnetID:  fmt.Sprintf("%s-id", subnetTask.getAPIServerSubnetName()),
				CIDRBlock: cidrDispatcher.APIServerIPv4CIDRBlock(),
			},
		}, vpc.Status.ControlPlaneSubnets)
	})
	t.Run("Pod subnet is not ready", func(t *testing.T) {
		subnetTask := fakeSubnetTask(t)
		cidrDispatcher, err := cidr.NewAKSCIDRDispatcher(subnetTask.VPC.Spec.CIDR)
		assert.Nil(t, err)
		assert.Nil(t, cidrDispatcher.Allocate())
		createSubnetF(subnetTask.Local, subnetTask.getPodSubnetName(), cidrDispatcher.PodIPv4CIDRBlock(), corev1.ConditionFalse)
		createSubnetF(subnetTask.Local, subnetTask.getNodeSubnetName(), cidrDispatcher.NodeIPv4CIDRBlock(), corev1.ConditionTrue)
		createSubnetF(subnetTask.Local, subnetTask.getPLSSubnetName(), cidrDispatcher.PLSNATIPv4CIDRBlock(), corev1.ConditionTrue)

		result := subnetTask.PostCreate(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.Contains(t, retriableErr.Error(), fmt.Sprintf("pod subnet %s is not ready", subnetTask.getPodSubnetName()))
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
	})
	t.Run("Node subnet is not ready", func(t *testing.T) {
		subnetTask := fakeSubnetTask(t)
		cidrDispatcher, err := cidr.NewAKSCIDRDispatcher(subnetTask.VPC.Spec.CIDR)
		assert.Nil(t, err)
		assert.Nil(t, cidrDispatcher.Allocate())
		createSubnetF(subnetTask.Local, subnetTask.getPodSubnetName(), cidrDispatcher.PodIPv4CIDRBlock(), corev1.ConditionTrue)
		createSubnetF(subnetTask.Local, subnetTask.getNodeSubnetName(), cidrDispatcher.NodeIPv4CIDRBlock(), corev1.ConditionFalse)
		createSubnetF(subnetTask.Local, subnetTask.getPLSSubnetName(), cidrDispatcher.PLSNATIPv4CIDRBlock(), corev1.ConditionTrue)

		result := subnetTask.PostCreate(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.Contains(t, retriableErr.Error(), fmt.Sprintf("node subnet %s is not ready", subnetTask.getNodeSubnetName()))
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
	})
	t.Run("PLS subnet is not ready", func(t *testing.T) {
		subnetTask := fakeSubnetTask(t)
		cidrDispatcher, err := cidr.NewAKSCIDRDispatcher(subnetTask.VPC.Spec.CIDR)
		assert.Nil(t, err)
		assert.Nil(t, cidrDispatcher.Allocate())
		createSubnetF(subnetTask.Local, subnetTask.getPodSubnetName(), cidrDispatcher.PodIPv4CIDRBlock(), corev1.ConditionTrue)
		createSubnetF(subnetTask.Local, subnetTask.getNodeSubnetName(), cidrDispatcher.NodeIPv4CIDRBlock(), corev1.ConditionTrue)
		createSubnetF(subnetTask.Local, subnetTask.getPLSSubnetName(), cidrDispatcher.PLSNATIPv4CIDRBlock(), corev1.ConditionFalse)

		result := subnetTask.PostCreate(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.Contains(t, retriableErr.Error(), fmt.Sprintf("PLS subnet %s is not ready", subnetTask.getPLSSubnetName()))
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
	})
	t.Run("API server subnet is not ready", func(t *testing.T) {
		subnetTask := fakeSubnetTask(t)
		cidrDispatcher, err := cidr.NewAKSCIDRDispatcher(subnetTask.VPC.Spec.CIDR)
		assert.Nil(t, err)
		assert.Nil(t, cidrDispatcher.Allocate())
		createSubnetF(subnetTask.Local, subnetTask.getPodSubnetName(), cidrDispatcher.PodIPv4CIDRBlock(), corev1.ConditionTrue)
		createSubnetF(subnetTask.Local, subnetTask.getNodeSubnetName(), cidrDispatcher.NodeIPv4CIDRBlock(), corev1.ConditionTrue)
		createSubnetF(subnetTask.Local, subnetTask.getPLSSubnetName(), cidrDispatcher.PLSNATIPv4CIDRBlock(), corev1.ConditionTrue)
		createSubnetF(subnetTask.Local, subnetTask.getAPIServerSubnetName(), cidrDispatcher.APIServerIPv4CIDRBlock(), corev1.ConditionFalse)

		result := subnetTask.PostCreate(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.Contains(t, retriableErr.Error(), fmt.Sprintf("api server subnet %s is not ready", subnetTask.getAPIServerSubnetName()))
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
	})
}

func TestSubnet_Delete(t *testing.T) {
	subnetTask := fakeSubnetTask(t)
	t.Run("delete subnet success", func(t *testing.T) {
		result := subnetTask.Create(context.Background())
		assert.True(t, result.IsDoneState())

		result = subnetTask.Delete(context.Background())
		assert.True(t, result.IsDoneState())
		podSubnet := &v1beta1.Subnet{}
		err := subnetTask.Local.Get(context.Background(), types.NamespacedName{Name: subnetTask.getPodSubnetName()}, podSubnet)
		assert.True(t, apierrors.IsNotFound(err))
		nodeSubnet := &v1beta1.Subnet{}
		err = subnetTask.Local.Get(context.Background(), types.NamespacedName{Name: subnetTask.getNodeSubnetName()}, nodeSubnet)
		assert.True(t, apierrors.IsNotFound(err))
		plsSubnet := &v1beta1.Subnet{}
		err = subnetTask.Local.Get(context.Background(), types.NamespacedName{Name: subnetTask.getPLSSubnetName()}, plsSubnet)
		assert.True(t, apierrors.IsNotFound(err))
		apiServerSubnet := &v1beta1.Subnet{}
		err = subnetTask.Local.Get(context.Background(), types.NamespacedName{Name: subnetTask.getAPIServerSubnetName()}, apiServerSubnet)
		assert.True(t, apierrors.IsNotFound(err))

		// delete again
		result = subnetTask.Delete(context.Background())
		assert.True(t, result.IsDoneState())
	})
	t.Run("delete subnet failed", func(t *testing.T) {
		ctl := gomock.NewController(t)
		mockcli := kubemock.NewMockClient(ctl)
		subnetTask.Local = mockcli
		mockcli.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(assert.AnError).Times(1)
		result := subnetTask.Delete(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.NotNil(t, err)
		assert.Nil(t, retriableErr)
		assert.Equal(t, time.Second*0, res.RequeueAfter)
	})
}
