package aks

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

	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v6"
	v1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	"github.com/go-logr/logr"
	"github.com/stretchr/testify/assert"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/pkg/namer"
	"github.com/tidbcloud/infra-provider/pkg/util"
	"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/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/appbox/task"
	resappbox "github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/appbox"
	resinternet "github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/internet"
	resvpc "github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/vpc"
	"github.com/tidbcloud/infra-provider/regional-server/test/kubemock"
	"github.com/tidbcloud/infra-provider/regional-server/test/mockazure"
	"github.com/tidbcloud/infra-provider/regional-server/test/testdata"
	authv1beta1 "github.com/upbound/provider-azure/apis/authorization/v1beta1"
	azurev1beta1 "github.com/upbound/provider-azure/apis/azure/v1beta1"
	"github.com/upbound/provider-azure/apis/containerservice/v1beta1"
	containerv1beta1 "github.com/upbound/provider-azure/apis/containerservice/v1beta1"
	managedidentityv1beta1 "github.com/upbound/provider-azure/apis/managedidentity/v1beta1"
	netv1beta1 "github.com/upbound/provider-azure/apis/network/v1beta1"
	"go.uber.org/mock/gomock"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
	"sigs.k8s.io/yaml"
)

func fakeTaskContext(t *testing.T) *task.Context {
	s := runtime.NewScheme()
	v1alpha1.AddToScheme(s)
	corev1.AddToScheme(s)
	azurev1beta1.SchemeBuilder.AddToScheme(s)
	netv1beta1.SchemeBuilder.AddToScheme(s)
	containerv1beta1.SchemeBuilder.AddToScheme(s)
	authv1beta1.SchemeBuilder.AddToScheme(s)
	managedidentityv1beta1.SchemeBuilder.AddToScheme(s)
	fakeVPC := &v1alpha1.VPC{}
	bs, err := testdata.LoadAKSVPCYAML()
	assert.Nil(t, err)
	assert.Nil(t, yaml.Unmarshal(bs, fakeVPC))
	fakeAPPBox := &v1alpha1.APPBox{}
	bs, err = testdata.LoadAKSAPPBoxYAML()
	assert.Nil(t, err)
	assert.Nil(t, yaml.Unmarshal(bs, fakeAPPBox))
	cli := fake.NewClientBuilder().WithScheme(s).WithObjects(fakeVPC, fakeAPPBox).WithStatusSubresource(
		fakeVPC,
		fakeAPPBox,
		&azurev1beta1.ResourceGroup{},
		&netv1beta1.VirtualNetwork{},
		&containerv1beta1.KubernetesCluster{},
		&authv1beta1.RoleAssignment{},
		&netv1beta1.Subnet{},
		&managedidentityv1beta1.UserAssignedIdentity{}).Build()
	c := &task.Context{
		Local:     k8s.NewMutClientWithOwner(cli, fakeAPPBox.DeepCopy(), namer.NewNamer()).Client(),
		Logger:    logr.Discard(),
		VPC:       fakeVPC.DeepCopy(),
		VPCR:      resvpc.NewVPCReader(cli),
		APPBox:    fakeAPPBox.DeepCopy(),
		APPBoxRW:  resappbox.NewAPPBoxReaderWriter(cli),
		InternetR: resinternet.NewInternetReader(cli),
		Config:    config.RootConfig{},
	}
	c, err = c.Default()
	assert.Nil(t, err)
	return c
}

func fakeAKS(t *testing.T) *AKS {
	return NewAKS(fakeTaskContext(t))
}

func createAKSSubnetForTest(t *testing.T, aksTask *AKS, name, subnetType string) {
	t.Helper()
	assert.Nil(t, aksTask.Local.Create(context.Background(), &netv1beta1.Subnet{
		ObjectMeta: metav1.ObjectMeta{
			Namespace: "default",
			Name:      name,
			Labels: map[string]string{
				v1alpha1.VPCNameKey:    aksTask.VPC.Name,
				v1alpha1.SubnetTypeKey: subnetType,
			},
		},
	}))
}

func createAKSRequiredSubnetsForTest(t *testing.T, aksTask *AKS, includeAPIServer bool) {
	t.Helper()
	createAKSSubnetForTest(t, aksTask, "subnet-node", v1alpha1.NodeSubnetType)
	createAKSSubnetForTest(t, aksTask, "subnet-pod", v1alpha1.PodSubnetType)
	if includeAPIServer {
		createAKSSubnetForTest(t, aksTask, "subnet-apiserver", v1alpha1.APIServerSubnetType)
	}
}

func expectedAKSControlPlaneIdentityForTest(t *testing.T, aksTask *AKS) []containerv1beta1.IdentityParameters {
	t.Helper()
	controlPlaneIdentityID, err := aksControlPlaneManagedIdentityResourceID(aksTask.Context)
	assert.NoError(t, err)
	return []containerv1beta1.IdentityParameters{
		{
			Type:        ptr.To(UserAssignedIdentityType),
			IdentityIds: []*string{ptr.To(controlPlaneIdentityID)},
		},
	}
}

func TestAKS_getAKSName(t *testing.T) {
	aksTask := fakeAKS(t)
	assert.Equal(t, namer.UniqueIdentifier(aksTask.APPBox.ObjectMeta, namer.WithLimit(AKSNameLimit)), aksTask.getAKSName())
}

func TestGetNodeResourceGroupName(t *testing.T) {
	aksTask := fakeAKS(t)
	assert.Equal(t, fmt.Sprintf("%s%s", aksTask.getAKSName(), nodeResourceGroupSuffix), aksTask.getNodeResourceGroupName())
}

func TestAKS_Create(t *testing.T) {
	t.Run("no node subnet in vpc", func(t *testing.T) {
		aksTask := fakeAKS(t)
		result := aksTask.Create(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.ErrorContains(t, retriableErr, "no subnet found")
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
	})
	t.Run("no pod subnet in vpc", func(t *testing.T) {
		aksTask := fakeAKS(t)
		assert.Nil(t, aksTask.Local.Create(context.Background(), &netv1beta1.Subnet{
			ObjectMeta: metav1.ObjectMeta{
				Namespace: "default",
				Name:      "subnet-node",
				Labels: map[string]string{
					v1alpha1.VPCNameKey:    aksTask.VPC.Name,
					v1alpha1.SubnetTypeKey: v1alpha1.NodeSubnetType,
				},
			},
		}))
		result := aksTask.Create(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.ErrorContains(t, retriableErr, "no subnet found")
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
	})
	t.Run("create success", func(t *testing.T) {
		aksTask := fakeAKS(t)
		createAKSRequiredSubnetsForTest(t, aksTask, true)
		aksTask.Config.APPBoxConfig.AdminGroupObjectIDs = []string{"admin-group-object-id-1", "admin-group-object-id-2"}
		result := aksTask.Create(context.Background())
		assert.Equal(t, deploy.Done(), result)
		cluster := &containerv1beta1.KubernetesCluster{}
		assert.Nil(t, aksTask.Local.Get(context.Background(), types.NamespacedName{
			Name: aksTask.getAKSName(),
		}, cluster))
		cidrDispatcher, err := cidr.NewAKSCIDRDispatcher(aksTask.VPC.Spec.CIDR)
		assert.Nil(t, err)
		err = cidrDispatcher.Allocate()
		assert.Nil(t, err)
		tags := cloudv1.TagsFromLabels(aksTask.APPBox.Labels)
		tags = util.AddResourceName(tags, aksTask.getAKSName())
		assert.Equal(t, cluster.Spec, containerv1beta1.KubernetesClusterSpec{
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{
					Name: aksTask.ProviderConfigName(),
				},
			},
			ForProvider: containerv1beta1.KubernetesClusterParameters{
				KubernetesVersion:       ptr.To(aksTask.APPBox.Spec.KubernetesConfig.Version),
				Location:                ptr.To(aksTask.APPBox.Spec.Region),
				OidcIssuerEnabled:       ptr.To(true),
				WorkloadIdentityEnabled: ptr.To(true),
				ResourceGroupName:       ptr.To(controllers.GetResourceGroupName(aksTask.VPC)),
				NodeResourceGroup:       ptr.To(aksTask.getNodeResourceGroupName()),
				NodeOsChannelUpgrade:    ptr.To("None"),
				Identity:                expectedAKSControlPlaneIdentityForTest(t, aksTask),
				NetworkProfile: []containerv1beta1.NetworkProfileParameters{
					{
						NetworkPlugin: ptr.To(NetworkPlugin),
						ServiceCidr:   ptr.To(cidrDispatcher.ServiceIPv4CIDRBlock()),
						DNSServiceIP:  ptr.To(cidrDispatcher.DNSServiceIP()),
						OutboundType:  ptr.To(userAssignedNATGateway),
					},
				},
				DNSPrefix: ptr.To(aksTask.getAKSName()),
				AutoScalerProfile: []v1beta1.AutoScalerProfileParameters{
					{
						BalanceSimilarNodeGroups: ptr.To(true),
					},
				},
				DefaultNodePool: []containerv1beta1.DefaultNodePoolParameters{
					{
						EnableAutoScaling: ptr.To(true),
						MaxCount:          ptr.To(float64(DefaultNodeMaxSize)),
						MinCount:          ptr.To(float64(DefaultNodeMinSize)),
						// use the aks name as the admin node pool name
						Name:            ptr.To(adminNodePoolName),
						VMSize:          ptr.To(DefaultInstanceType),
						OsSku:           ptr.To("AzureLinux"),
						VnetSubnetIDRef: &xpv1.Reference{Name: "subnet-node"},
						PodSubnetIDRef:  &xpv1.Reference{Name: "subnet-pod"},
						UpgradeSettings: []containerv1beta1.UpgradeSettingsParameters{
							{
								MaxSurge: ptr.To(string("10%")),
							},
						},
						Tags: util.ToMapPointer(tags),
					},
				},
				Tags: util.ToMapPointer(tags),
				AzureActiveDirectoryRoleBasedAccessControl: []v1beta1.AzureActiveDirectoryRoleBasedAccessControlParameters{
					{
						Managed: ptr.To(true),
						AdminGroupObjectIds: []*string{
							ptr.To("admin-group-object-id-1"),
							ptr.To("admin-group-object-id-2"),
						},
					},
				},
				APIServerAccessProfile: []v1beta1.APIServerAccessProfileParameters{
					{
						SubnetIDRef:            &xpv1.Reference{Name: "subnet-apiserver"},
						VnetIntegrationEnabled: ptr.To(true),
					},
				},
				SkuTier: ptr.To("Standard"),
			},
		})
		assert.Equal(t, "true", cluster.Annotations[v1alpha1.AnnotationKeyAKSAPIServerVNetIntegrationRequested])
	})
	t.Run("with allowed ip range and internet not found", func(t *testing.T) {
		aksTask := fakeAKS(t)
		createAKSRequiredSubnetsForTest(t, aksTask, true)
		aksTask.Config.APPBoxConfig.AdminGroupObjectIDs = []string{"admin-group-object-id-1", "admin-group-object-id-2"}
		aksTask.Config.APPBoxConfig.ApiserverPublicAccessCidrs = []string{"1.2.3.4/32"}
		result := aksTask.Create(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.ErrorContains(t, retriableErr, "no internet found")
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
	})
	t.Run("with allowed ip range and active internet", func(t *testing.T) {
		aksTask := fakeAKS(t)
		createAKSRequiredSubnetsForTest(t, aksTask, true)
		assert.Nil(t, aksTask.Local.Create(context.Background(), &v1alpha1.Internet{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "internet-test",
				Namespace: aksTask.VPC.Namespace,
				Labels: map[string]string{
					v1alpha1.VPCNameKey: aksTask.VPC.Name,
				},
			},
			Status: v1alpha1.InternetStatus{
				Phase: v1alpha1.InternetPhaseActive,
				ExternalIPs: []string{
					"2.3.3.3",
					"3.2.2.2",
				},
			},
		}))
		aksTask.Config.APPBoxConfig.AdminGroupObjectIDs = []string{"admin-group-object-id-1", "admin-group-object-id-2"}
		aksTask.Config.APPBoxConfig.ApiserverPublicAccessCidrs = []string{"1.2.3.4/32"}
		result := aksTask.Create(context.Background())
		assert.Equal(t, deploy.Done(), result)
		cluster := &containerv1beta1.KubernetesCluster{}
		assert.Nil(t, aksTask.Local.Get(context.Background(), types.NamespacedName{
			Name: aksTask.getAKSName(),
		}, cluster))
		assert.Equal(t, []*string{
			ptr.To("1.2.3.4/32"),
			ptr.To("2.3.3.3/32"),
			ptr.To("3.2.2.2/32"),
		}, cluster.Spec.ForProvider.APIServerAccessProfile[0].AuthorizedIPRanges)
		assert.Equal(t, &xpv1.Reference{Name: "subnet-apiserver"}, cluster.Spec.ForProvider.APIServerAccessProfile[0].SubnetIDRef)
		assert.Equal(t, ptr.To(true), cluster.Spec.ForProvider.APIServerAccessProfile[0].VnetIntegrationEnabled)
	})
	t.Run("existing provisioned cluster without annotation keeps old public mode", func(t *testing.T) {
		aksTask := fakeAKS(t)
		createAKSRequiredSubnetsForTest(t, aksTask, false)
		existingAKS := &containerv1beta1.KubernetesCluster{
			ObjectMeta: metav1.ObjectMeta{
				Name: aksTask.getAKSName(),
			},
			Spec: containerv1beta1.KubernetesClusterSpec{
				ForProvider: containerv1beta1.KubernetesClusterParameters{
					Identity: []containerv1beta1.IdentityParameters{
						{Type: ptr.To(IdentityType)},
					},
				},
			},
			Status: containerv1beta1.KubernetesClusterStatus{
				AtProvider: containerv1beta1.KubernetesClusterObservation{
					ID: ptr.To("/subscriptions/subscription-id-1/resourceGroups/foo/providers/Microsoft.ContainerService/managedClusters/test-aks"),
				},
			},
		}
		assert.Nil(t, aksTask.Local.Create(context.Background(), existingAKS))

		result := aksTask.Create(context.Background())
		assert.Equal(t, deploy.Done(), result)
		cluster := &containerv1beta1.KubernetesCluster{}
		assert.Nil(t, aksTask.Local.Get(context.Background(), types.NamespacedName{Name: aksTask.getAKSName()}, cluster))
		assert.Equal(t, []containerv1beta1.IdentityParameters{{Type: ptr.To(IdentityType)}}, cluster.Spec.ForProvider.Identity)
		assert.Empty(t, cluster.Spec.ForProvider.APIServerAccessProfile)
		assert.Empty(t, cluster.Annotations[v1alpha1.AnnotationKeyAKSAPIServerVNetIntegrationRequested])
	})
	t.Run("existing provisioned cluster with annotation enables vnet integration", func(t *testing.T) {
		aksTask := fakeAKS(t)
		aksTask.APPBox.Annotations = map[string]string{
			v1alpha1.AnnotationKeyEnableAKSAPIServerVNetIntegration: "true",
		}
		createAKSRequiredSubnetsForTest(t, aksTask, true)
		existingAKS := &containerv1beta1.KubernetesCluster{
			ObjectMeta: metav1.ObjectMeta{
				Name: aksTask.getAKSName(),
			},
			Spec: containerv1beta1.KubernetesClusterSpec{
				ForProvider: containerv1beta1.KubernetesClusterParameters{
					Identity: []containerv1beta1.IdentityParameters{
						{Type: ptr.To(IdentityType)},
					},
				},
			},
			Status: containerv1beta1.KubernetesClusterStatus{
				AtProvider: containerv1beta1.KubernetesClusterObservation{
					ID: ptr.To("/subscriptions/subscription-id-1/resourceGroups/foo/providers/Microsoft.ContainerService/managedClusters/test-aks"),
				},
			},
		}
		assert.Nil(t, aksTask.Local.Create(context.Background(), existingAKS))

		result := aksTask.Create(context.Background())
		assert.Equal(t, deploy.Done(), result)
		cluster := &containerv1beta1.KubernetesCluster{}
		assert.Nil(t, aksTask.Local.Get(context.Background(), types.NamespacedName{Name: aksTask.getAKSName()}, cluster))
		assert.Equal(t, expectedAKSControlPlaneIdentityForTest(t, aksTask), cluster.Spec.ForProvider.Identity)
		assert.Equal(t, &xpv1.Reference{Name: "subnet-apiserver"}, cluster.Spec.ForProvider.APIServerAccessProfile[0].SubnetIDRef)
		assert.Equal(t, ptr.To(true), cluster.Spec.ForProvider.APIServerAccessProfile[0].VnetIntegrationEnabled)
	})
}

func TestAKS_getSubnet(t *testing.T) {
	t.Run("no subnet found", func(t *testing.T) {
		aksTask := fakeAKS(t)
		subnets, err := aksTask.getSubnet(context.Background(), v1alpha1.NodeSubnetType)
		assert.ErrorContains(t, err, "no subnet found")
		assert.Nil(t, subnets)
	})
	t.Run("subnet found", func(t *testing.T) {
		aksTask := fakeAKS(t)
		assert.Nil(t, aksTask.Local.Create(context.Background(), &netv1beta1.Subnet{
			ObjectMeta: metav1.ObjectMeta{
				Namespace: "default",
				Name:      "subnet1",
				Labels: map[string]string{
					v1alpha1.VPCNameKey:    aksTask.VPC.Name,
					v1alpha1.SubnetTypeKey: v1alpha1.NodeSubnetType,
				},
			},
		}))
		subnets, err := aksTask.getSubnet(context.Background(), v1alpha1.NodeSubnetType)
		assert.Nil(t, err)
		assert.Equal(t, 1, len(subnets.Items))

		subnets, err = aksTask.getSubnet(context.Background(), v1alpha1.PodSubnetType)
		assert.ErrorContains(t, err, "no subnet found")
		assert.Nil(t, subnets)
	})
}

func TestAKS_PostCreate(t *testing.T) {
	t.Run("aks not found", func(t *testing.T) {
		aksTask := fakeAKS(t)
		result := aksTask.PostCreate(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.ErrorContains(t, retriableErr, "not found")
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
	})
	t.Run("aks not ready", func(t *testing.T) {
		aksTask := fakeAKS(t)
		assert.Nil(t, aksTask.Local.Create(context.Background(), &containerv1beta1.KubernetesCluster{
			ObjectMeta: metav1.ObjectMeta{
				Name: aksTask.getAKSName(),
				Labels: map[string]string{
					v1alpha1.APPBoxNameKey: aksTask.APPBox.Name,
				},
			},
		}))
		result := aksTask.PostCreate(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.ErrorContains(t, retriableErr, "AKS Cluster is creating")
		assert.Equal(t, defaultRequeuInterval, res.RequeueAfter)
		appbox := &v1alpha1.APPBox{}
		assert.Nil(t, aksTask.Local.Get(context.Background(), types.NamespacedName{
			Name:      aksTask.APPBox.Name,
			Namespace: aksTask.APPBox.Namespace,
		}, appbox))
		condition, idx := appbox.Status.Conditions.GetCondition(v1alpha1.APPBoxConditionK8SReady)
		assert.NotNil(t, idx)
		assert.Equal(t, metav1.ConditionFalse, condition.Status)
		assert.Equal(t, "AKSCreating", condition.Reason)
	})
	t.Run("create success", func(t *testing.T) {
		aksTask := fakeAKS(t)
		assert.Nil(t, aksTask.Local.Create(context.Background(), &containerv1beta1.KubernetesCluster{
			ObjectMeta: metav1.ObjectMeta{
				Name: aksTask.getAKSName(),
				Labels: map[string]string{
					v1alpha1.APPBoxNameKey: aksTask.APPBox.Name,
				},
			},
			Status: containerv1beta1.KubernetesClusterStatus{
				ResourceStatus: v1.ResourceStatus{
					ConditionedStatus: xpv1.ConditionedStatus{
						Conditions: []xpv1.Condition{
							{
								Type:   xpv1.TypeReady,
								Status: corev1.ConditionTrue,
							},
						},
					},
				},
			},
		}))
		assert.Nil(t, aksTask.Local.Create(context.Background(), &corev1.Secret{
			ObjectMeta: metav1.ObjectMeta{
				Name:      aksTask.getAKSName(),
				Namespace: aksTask.APPBox.Namespace,
				Labels: map[string]string{
					v1alpha1.APPBoxNameKey: aksTask.APPBox.Name,
				},
			},
		}))
		result := aksTask.PostCreate(context.Background())
		assert.Equal(t, deploy.Done(), result)
		appbox := &v1alpha1.APPBox{}
		assert.Nil(t, aksTask.Local.Get(context.Background(), types.NamespacedName{
			Name:      aksTask.APPBox.Name,
			Namespace: aksTask.APPBox.Namespace,
		}, appbox))
		condition, idx := appbox.Status.Conditions.GetCondition(v1alpha1.APPBoxConditionK8SReady)
		assert.NotNil(t, idx)
		assert.Equal(t, "AKSReady", condition.Reason)
	})
}

func TestAKS_Delete(t *testing.T) {
	t.Run("delete success", func(t *testing.T) {
		aksTask := fakeAKS(t)
		assert.Nil(t, aksTask.Local.Create(context.Background(), &containerv1beta1.KubernetesCluster{
			ObjectMeta: metav1.ObjectMeta{
				Name: aksTask.getAKSName(),
			},
		}))
		result := aksTask.Delete(context.Background())
		assert.Equal(t, deploy.Done(), result)
		cluster := &containerv1beta1.KubernetesCluster{}
		assert.ErrorContains(t, aksTask.Local.Get(context.Background(), types.NamespacedName{
			Name: aksTask.getAKSName(),
		}, cluster), "not found")

		// delete again
		result = aksTask.Delete(context.Background())
		assert.Equal(t, deploy.Done(), result)
	})
}

func TestAKS_PostDelete(t *testing.T) {
	t.Run("delete success", func(t *testing.T) {
		aksTask := fakeAKS(t)
		result := aksTask.PostDelete(context.Background())
		assert.Equal(t, deploy.Done(), result)
	})
	t.Run("aks is still present", func(t *testing.T) {
		aksTask := fakeAKS(t)
		assert.Nil(t, aksTask.Local.Create(context.Background(), &containerv1beta1.KubernetesCluster{
			ObjectMeta: metav1.ObjectMeta{
				Name: aksTask.getAKSName(),
				Labels: map[string]string{
					v1alpha1.APPBoxNameKey: aksTask.APPBox.Name,
				},
			},
		}))
		result := aksTask.PostDelete(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.ErrorContains(t, retriableErr, "still present")
		assert.Equal(t, 30*time.Second, res.RequeueAfter)
	})
	t.Run("kubeconfig secret is still present", func(t *testing.T) {
		aksTask := fakeAKS(t)
		assert.Nil(t, aksTask.Local.Create(context.Background(), &corev1.Secret{
			ObjectMeta: metav1.ObjectMeta{
				Name:      aksTask.getAKSName(),
				Namespace: aksTask.APPBox.Namespace,
				Labels: map[string]string{
					v1alpha1.APPBoxNameKey: aksTask.APPBox.Name,
				},
			},
		}))
		result := aksTask.PostDelete(context.Background())
		res, err, retriableErr := deploy.MinRequeue([]deploy.Result{result})
		assert.Nil(t, err)
		assert.ErrorContains(t, retriableErr, "still present")
		assert.Equal(t, 30*time.Second, res.RequeueAfter)
	})
}

func TestAKS_createKubeconfigSecret(t *testing.T) {
	t.Run("fqdn is empty", func(t *testing.T) {
		aksTask := fakeAKS(t)
		secret := &corev1.Secret{}
		cluster := &v1beta1.KubernetesCluster{}
		err := aksTask.createKubeconfigSecret(context.Background(), secret, cluster)
		assert.ErrorContains(t, err, "fqdn is empty")
	})
	t.Run("no kubeconfig found", func(t *testing.T) {
		aksTask := fakeAKS(t)
		ctl := gomock.NewController(t)
		akscli := mockazure.NewMockAKSClient(ctl)
		aksTask.AKSCli = akscli
		akscli.EXPECT().ListClusterUserCredentials(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(armcontainerservice.ManagedClustersClientListClusterUserCredentialsResponse{}, nil)
		err := aksTask.createKubeconfigSecret(context.Background(), &corev1.Secret{}, &v1beta1.KubernetesCluster{
			Status: v1beta1.KubernetesClusterStatus{
				AtProvider: v1beta1.KubernetesClusterObservation{
					Fqdn: ptr.To("fqdn"),
				},
			},
		})
		assert.ErrorContains(t, err, "no kubeconfig found")
	})
	t.Run("success", func(t *testing.T) {
		aksTask := fakeAKS(t)
		ctl := gomock.NewController(t)
		akscli := mockazure.NewMockAKSClient(ctl)
		aksTask.AKSCli = akscli
		akscli.EXPECT().ListClusterUserCredentials(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(armcontainerservice.ManagedClustersClientListClusterUserCredentialsResponse{
			CredentialResults: armcontainerservice.CredentialResults{
				Kubeconfigs: []*armcontainerservice.CredentialResult{
					{
						Value: []byte(testdata.OriginKubeConfig),
					},
				},
			},
		}, nil)
		err := aksTask.createKubeconfigSecret(context.Background(), &corev1.Secret{
			ObjectMeta: metav1.ObjectMeta{
				Name:      aksTask.getAKSName(),
				Namespace: aksTask.APPBox.Namespace,
				Labels: map[string]string{
					v1alpha1.APPBoxNameKey: aksTask.APPBox.Name,
				},
			},
		}, &v1beta1.KubernetesCluster{
			Status: v1beta1.KubernetesClusterStatus{
				AtProvider: v1beta1.KubernetesClusterObservation{
					Fqdn: ptr.To("fqdn"),
				},
			},
		})
		assert.Nil(t, err)
		secret := &corev1.Secret{}
		assert.Nil(t, aksTask.Local.Get(context.Background(), types.NamespacedName{
			Name:      aksTask.getAKSName(),
			Namespace: aksTask.APPBox.Namespace,
		}, secret))
		assert.Equal(t, map[string][]byte{
			"kubeconfig": []byte(testdata.ExpectedKubeConfig),
		}, secret.Data)
	})
}

func TestAKS_ensureKubeconfigSecret(t *testing.T) {
	t.Run("get secret error", func(t *testing.T) {
		aksTask := fakeAKS(t)
		ctl := gomock.NewController(t)
		kubecli := kubemock.NewMockClient(ctl)
		aksTask.Local = kubecli
		kubecli.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("get error"))
		err := aksTask.ensureKubeconfigSecret(context.Background(), &corev1.Secret{}, &v1beta1.KubernetesCluster{})
		assert.ErrorContains(t, err, "failed to get secret")
	})
	t.Run("secret is exist", func(t *testing.T) {
		aksTask := fakeAKS(t)
		ctl := gomock.NewController(t)
		kubecli := kubemock.NewMockClient(ctl)
		aksTask.Local = kubecli
		kubecli.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
		err := aksTask.ensureKubeconfigSecret(context.Background(), &corev1.Secret{}, &v1beta1.KubernetesCluster{})
		assert.Nil(t, err)
	})
	t.Run("create secret", func(t *testing.T) {
		aksTask := fakeAKS(t)
		ctl := gomock.NewController(t)
		akscli := mockazure.NewMockAKSClient(ctl)
		aksTask.AKSCli = akscli
		akscli.EXPECT().ListClusterUserCredentials(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(armcontainerservice.ManagedClustersClientListClusterUserCredentialsResponse{
			CredentialResults: armcontainerservice.CredentialResults{
				Kubeconfigs: []*armcontainerservice.CredentialResult{
					{
						Value: []byte(testdata.OriginKubeConfig),
					},
				},
			},
		}, nil)
		secret := &corev1.Secret{
			ObjectMeta: metav1.ObjectMeta{
				Name:      aksTask.getAKSName(),
				Namespace: aksTask.APPBox.Namespace,
				Labels: map[string]string{
					v1alpha1.APPBoxNameKey: aksTask.APPBox.Name,
				},
			},
		}
		cluster := &v1beta1.KubernetesCluster{
			Status: v1beta1.KubernetesClusterStatus{
				AtProvider: v1beta1.KubernetesClusterObservation{
					Fqdn: ptr.To("fqdn"),
				},
			},
		}
		err := aksTask.ensureKubeconfigSecret(context.Background(), secret, cluster)
		assert.Nil(t, err)
		get := &corev1.Secret{}
		assert.Nil(t, aksTask.Local.Get(context.Background(), types.NamespacedName{
			Name:      aksTask.getAKSName(),
			Namespace: aksTask.APPBox.Namespace,
		}, get))
		assert.Equal(t, map[string][]byte{
			"kubeconfig": []byte(testdata.ExpectedKubeConfig),
		}, get.Data)
	})
}
