//go:build unit || all
// +build unit all

package procedure

import (
	"context"
	"testing"

	lambdav1alpha1 "github.com/crossplane-contrib/provider-aws/apis/lambda/v1alpha1"
	. "github.com/onsi/gomega"
	eksv1alpha1 "github.com/tidbcloud/eks-provider/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/pkg/namer"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
)

var (
	fakeFrpsPrivatelinkInfo ProcedureInfo
	fakeFrpsPrivatelinkName string

	fakeFrpsPrivatelinkSvc       *corev1.Service
	fakeFrpsPrivatelinkFrpServer *eksv1alpha1.Frpserver
)

func init() {
	fakeFrpsPrivatelinkInfo.Region = "us-west-2"
	fakeFrpsPrivatelinkInfo.Cluster = "eks1"
	fakeFrpsPrivatelinkInfo.ProviderConfigRef = "123456789012"
	fakeFrpsPrivatelinkInfo.SubnetIDs = []string{"subnet1", "subnet2"}
	fakeFrpsPrivatelinkInfo.eksConnection = &eksv1alpha1.EKSConnection{}
	fakeFrpsPrivatelinkInfo.namespace = ProcedureNamespace
	fakeFrpsPrivatelinkName = namer.UniqueIdentifier(fakeFrpsPrivatelinkInfo.eksConnection.ObjectMeta)
	fakeFrpsPrivatelinkSvc = &corev1.Service{}
	fakeFrpsPrivatelinkSvc.Name = "fake-frps-svc"
	fakeFrpsPrivatelinkSvc.Namespace = "fake-frps-svc-ns"
	fakeFrpsPrivatelinkSvc.Spec.ClusterIP = fakeFrpsClusterIP
	fakeFrpsPrivatelinkFrpServer = &eksv1alpha1.Frpserver{}
	fakeFrpsPrivatelinkFrpServer.Name = namer.UniqueIdentifier(fakeFrpsPrivatelinkInfo.eksConnection.ObjectMeta)
	fakeFrpsPrivatelinkFrpServer.Annotations = map[string]string{
		AnnotationKeyFrpsSvcName: fakeFrpsPrivatelinkSvc.Name,
		AnnotationKeyFrpsSvcNs:   fakeFrpsPrivatelinkSvc.Namespace,
	}
}

func TestFrpsPrivatelinkDeploy(t *testing.T) {
	cases := map[string]struct {
		info      ProcedureInfo
		objs      []client.Object
		expectErr bool
	}{
		"create-success": {
			info: fakeFrpsPrivatelinkInfo,
			objs: []client.Object{
				fakeFrpsPrivatelinkFrpServer,
			},
			expectErr: false,
		},
		"already-exist": {
			info: fakeFrpsPrivatelinkInfo,
			objs: []client.Object{
				fakeFrpsPrivatelinkFrpServer,
				&eksv1alpha1.Privatelink{
					ObjectMeta: metav1.ObjectMeta{
						Name: fakeFrpsPrivatelinkName,
					},
				},
			},
			expectErr: false,
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			s := runtime.NewScheme()
			g.Expect(eksv1alpha1.SchemeBuilder.AddToScheme(s)).To(Succeed())
			g.Expect(lambdav1alpha1.SchemeBuilder.AddToScheme(s)).To(Succeed())
			cli := fake.NewClientBuilder().WithScheme(s).WithObjects(tc.objs...).Build()
			p := NewFrpsPL(s)
			err := p.Deploy(context.TODO(), cli, cli, tc.info)
			if !tc.expectErr {
				g.Expect(err).To(Succeed())
			} else {
				g.Expect(err).NotTo(Succeed())
			}
		})
	}
}

func TestFrpsPrivatelinkStatus(t *testing.T) {
	addCondition := func(obj *eksv1alpha1.Privatelink, cond eksv1alpha1.Condition) *eksv1alpha1.Privatelink {
		obj.Status.Conditions.SetCondition(cond)
		return obj
	}
	cases := map[string]struct {
		info        ProcedureInfo
		objs        []client.Object
		expectReady bool
		expectErr   bool
	}{
		"not-exist": {
			info:        fakeFrpsPrivatelinkInfo,
			expectErr:   true,
			expectReady: false,
		},
		"privatelink-ready": {
			info: fakeFrpsPrivatelinkInfo,
			objs: []client.Object{
				addCondition(
					getFrpsPrivatelink(fakeFrpsPrivatelinkInfo, fakeFrpsSvcName, fakeFrpsSvcNs),
					eksv1alpha1.Condition{
						Type:   eksv1alpha1.ConditionReady,
						Status: corev1.ConditionTrue,
					},
				),
			},
			expectErr:   false,
			expectReady: true,
		},
		"privatelink-not-ready": {
			info: fakeFrpsPrivatelinkInfo,
			objs: []client.Object{
				addCondition(
					getFrpsPrivatelink(fakeFrpsPrivatelinkInfo, fakeFrpsSvcName, fakeFrpsSvcNs),
					eksv1alpha1.Condition{
						Type:   eksv1alpha1.ConditionReady,
						Status: corev1.ConditionFalse,
					},
				),
			},
			expectErr:   false,
			expectReady: false,
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			s := runtime.NewScheme()
			g.Expect(eksv1alpha1.SchemeBuilder.AddToScheme(s)).To(Succeed())
			cli := fake.NewClientBuilder().WithScheme(s).WithObjects(tc.objs...).Build()
			p := NewFrpsPL(s)
			ready, err := p.Status(context.TODO(), cli, cli, tc.info)
			if !tc.expectErr {
				g.Expect(err).To(Succeed())
			} else {
				g.Expect(err).NotTo(Succeed())
				return
			}
			g.Expect(ready).To(Equal(tc.expectReady))
		})
	}
}
