package aks

import (
	"context"
	"fmt"
	"testing"

	pcav1beta1 "github.com/cert-manager/aws-privateca-issuer/pkg/api/v1beta1"
	. "github.com/onsi/gomega"
	iamv1beta1 "github.com/upbound/provider-aws/apis/iam/v1beta1"
	"github.com/upbound/provider-azure/apis/containerservice/v1beta1"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/intstr"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	"k8s.io/utils/ptr"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"

	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"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"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/namer"
)

func TestPCAIssuerCreate(t *testing.T) {
	t.Run("create resources", func(t *testing.T) {
		g := NewWithT(t)
		remoteScheme := runtime.NewScheme()
		utilruntime.Must(clientgoscheme.AddToScheme(remoteScheme))
		utilruntime.Must(pcav1beta1.AddToScheme(remoteScheme))

		localScheme := runtime.NewScheme()
		utilruntime.Must(iamv1beta1.SchemeBuilder.AddToScheme(localScheme))

		task := NewPCAIssuer(&task.Context{
			Config: config.RootConfig{
				ControllerConfig: config.ControllerConfig{
					AWSCrossplaneProviderConfig: "385595570414",
					APPBoxConfig: config.APPBoxConfig{
						PCAConfig: &config.PCAConfig{
							CAIdentifier: "arn:aws:acm-pca:us-west-2:385595570414:certificate-authority/4668d5cc-1042-4371-9ff4-72ab9493c545",
							Region:       "us-west-2",
						},
					},
				},
			},
			InfraTmplConfig: &config.InfraTemplateConfig{
				PCAIssuer: config.InfraComponentConfig{
					ReleaseName: "aws-pca-issuer",
					Namespace:   "cert-manager",
					HelmValues: map[string]interface{}{
						"image": map[string]interface{}{
							"repository": "385595570414.dkr.ecr.us-west-2.amazonaws.com/tidbcloud/aws-pca-issuer",
							"tag":        "latest",
						},
					},
				},
			},
			VPC: &v1alpha1.VPC{
				Spec: v1alpha1.VPCSpec{
					AccountRef: &v1alpha1.AccountReference{
						AccountID: "aks-dev",
					},
				},
			},
			Cluster: task.NewAKSClusterIdentifier(&v1beta1.KubernetesCluster{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "aks",
					Namespace: "default",
				},
				Status: v1beta1.KubernetesClusterStatus{
					AtProvider: v1beta1.KubernetesClusterObservation{
						OidcIssuerURL: ptr.To("https://eastus.oic.prod-aks.azure.com/6c865c34-8ff8-4792-bbca-8c9d15efb29d/7d2ae9c9-7ced-4612-938f-b2c107013dd0/"),
					},
				},
			}),
		})
		remote := fake.NewClientBuilder().
			WithScheme(remoteScheme).
			WithStatusSubresource(&pcav1beta1.AWSPCAClusterIssuer{}).
			Build()
		task.Remote = &controllers.MockClientGetter{Client: remote}
		task.Local = fake.NewClientBuilder().WithScheme(localScheme).Build()

		task.APPBox = &v1alpha1.APPBox{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "appbox",
				Namespace: "default",
			},
			Spec: v1alpha1.APPBoxSpec{
				Region: "us-west-2",
			},
		}
		res := task.Create(context.Background())
		g.Expect(res.Error()).ToNot(HaveOccurred())

		// service account
		var sa corev1.ServiceAccount
		g.Expect(remote.Get(context.Background(), types.NamespacedName{
			Namespace: "cert-manager",
			Name:      "aws-pca-issuer",
		}, &sa)).To(Succeed())

		// cluster role
		var cr rbacv1.ClusterRole
		g.Expect(remote.Get(context.Background(), types.NamespacedName{
			Name: "aws-pca-issuer",
		}, &cr)).To(Succeed())
		g.Expect(cr.Rules).To(Equal(task.NewClusterRole().Rules))

		// cluster role binding
		var crb rbacv1.ClusterRoleBinding
		g.Expect(remote.Get(context.Background(), types.NamespacedName{
			Name: "aws-pca-issuer",
		}, &crb)).To(Succeed())
		g.Expect(crb.RoleRef).To(Equal(task.NewClusterRoleBinding().RoleRef))
		g.Expect(crb.Subjects).To(Equal(task.NewClusterRoleBinding().Subjects))

		// deployment
		appBoxIdentifier := namer.UniqueIdentifier(task.APPBox.ObjectMeta)
		var deploy appsv1.Deployment
		g.Expect(remote.Get(context.Background(), types.NamespacedName{
			Namespace: "cert-manager",
			Name:      "aws-pca-issuer",
		}, &deploy)).To(Succeed())
		g.Expect(deploy.Spec.Template.Labels).To(HaveKeyWithValue("app.kubernetes.io/instance", "aws-pca-issuer"))
		g.Expect(deploy.Spec.Template.Labels).To(HaveKeyWithValue("app.kubernetes.io/name", "issuer"))
		g.Expect(deploy.Spec.Template.Spec.Containers).To(HaveLen(1))
		g.Expect(deploy.Spec.Template.Spec.Containers[0].Image).To(Equal("385595570414.dkr.ecr.us-west-2.amazonaws.com/tidbcloud/aws-pca-issuer:latest"))
		g.Expect(deploy.Spec.Template.Spec.ServiceAccountName).To(Equal("aws-pca-issuer"))
		g.Expect(deploy.Spec.Template.Spec.Containers[0].Env).To(ConsistOf(NewAWSEnv(
			fmt.Sprintf("arn:aws:iam::385595570414:role/%s", appBoxIdentifier),
		)))
		g.Expect(deploy.Spec.Template.Spec.Volumes).To(ConsistOf(
			corev1.Volume{
				Name: "aws-token",
				VolumeSource: corev1.VolumeSource{
					Projected: &corev1.ProjectedVolumeSource{
						Sources: []corev1.VolumeProjection{
							{
								ServiceAccountToken: &corev1.ServiceAccountTokenProjection{
									Audience:          "sts.amazonaws.com",
									ExpirationSeconds: ptr.To(int64(86400)),
									Path:              "token",
								},
							},
						},
					},
				},
			},
		))
		g.Expect(deploy.Spec.Template.Spec.Containers[0].VolumeMounts).To(ConsistOf(
			corev1.VolumeMount{
				Name:      "aws-token",
				MountPath: "/var/run/secrets/eks.amazonaws.com/serviceaccount/",
			},
		))

		// service
		var svc corev1.Service
		g.Expect(remote.Get(context.Background(), types.NamespacedName{
			Namespace: "cert-manager",
			Name:      "aws-pca-issuer",
		}, &svc)).To(Succeed())
		g.Expect(svc.Spec.Ports).To(ConsistOf(
			corev1.ServicePort{
				Port:       8080,
				TargetPort: intstr.FromString("http"),
				Protocol:   corev1.ProtocolTCP,
				Name:       "http",
			},
		))

		// AWS IAM role
		var role iamv1beta1.Role
		g.Expect(task.Local.Get(context.Background(), types.NamespacedName{
			Name: appBoxIdentifier,
		}, &role)).To(Succeed())
		g.Expect(role.Spec.ResourceSpec.ProviderConfigReference.Name).To(Equal("385595570414"))
		g.Expect(role.Spec.ForProvider.AssumeRolePolicy).To(Equal(ptr.To(`{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::385595570414:oidc-provider/eastus.oic.prod-aks.azure.com/6c865c34-8ff8-4792-bbca-8c9d15efb29d/7d2ae9c9-7ced-4612-938f-b2c107013dd0/"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "eastus.oic.prod-aks.azure.com/6c865c34-8ff8-4792-bbca-8c9d15efb29d/7d2ae9c9-7ced-4612-938f-b2c107013dd0/:sub": "system:serviceaccount:cert-manager:aws-pca-issuer"
        }
      }
    }
  ]
}`)))
		// AWS IAM role policy
		var policy iamv1beta1.Policy
		g.Expect(task.Local.Get(context.Background(), types.NamespacedName{
			Name: appBoxIdentifier,
		}, &policy)).To(Succeed())
		g.Expect(policy.Spec.ResourceSpec.ProviderConfigReference.Name).To(Equal("385595570414"))
		g.Expect(policy.Spec.ForProvider.Policy).To(Equal(ptr.To(`{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "acm-pca:IssueCertificate",
        "acm-pca:GetCertificate",
        "acm-pca:GetCertificateAuthorityCertificate",
        "acm-pca:DescribeCertificateAuthority"
      ],
      "Resource": "*"
    }
  ]
}`)))

		// IAM role policy attachment
		var policyAttachment iamv1beta1.RolePolicyAttachment
		g.Expect(task.Local.Get(context.Background(), types.NamespacedName{
			Name: appBoxIdentifier,
		}, &policyAttachment)).To(Succeed())
		g.Expect(policyAttachment.Spec.ResourceSpec.ProviderConfigReference.Name).To(Equal("385595570414"))
		g.Expect(policyAttachment.Spec.ForProvider.RoleRef.Name).To(Equal(appBoxIdentifier))
		g.Expect(policyAttachment.Spec.ForProvider.PolicyArnRef.Name).To(Equal(appBoxIdentifier))

		// AWS PCA ClusterIssuer CR
		var issuer pcav1beta1.AWSPCAClusterIssuer
		g.Expect(remote.Get(context.Background(), types.NamespacedName{
			Name: clusterIssuerName,
		}, &issuer)).To(Succeed())
		g.Expect(issuer.Spec.Arn).To(Equal("arn:aws:acm-pca:us-west-2:385595570414:certificate-authority/4668d5cc-1042-4371-9ff4-72ab9493c545"))
		g.Expect(issuer.Spec.Region).To(Equal("us-west-2"))

		res = task.PostCreate(context.Background())
		g.Expect(res.Error()).To(HaveOccurred())
		g.Expect(res.Error().Error()).To(ContainSubstring("AWS PCA ClusterIssuer is not ready yet"))

		issuer.Status.Conditions = []metav1.Condition{
			{
				Type:   "Ready",
				Status: metav1.ConditionTrue,
			},
		}
		g.Expect(remote.Status().Update(context.Background(), &issuer)).To(Succeed())

		res = task.PostCreate(context.Background())
		g.Expect(res.Error()).ToNot(HaveOccurred())
	})
}
