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

package k8s

import (
	"context"
	"testing"

	"github.com/crossplane/crossplane-runtime/pkg/meta"
	. "github.com/onsi/gomega"
	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/client-go/kubernetes/scheme"
	"sigs.k8s.io/controller-runtime/pkg/client"
	_ "sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
	_ "sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func TestRemoveCrossplaneExternalCreatePending(t *testing.T) {
	const (
		podName = "test-name"
		podNs   = "test-namespace"
	)
	cases := map[string]struct {
		obj    client.Object
		expect client.Object
	}{
		"contains annotation": {
			obj: &corev1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Annotations: map[string]string{
						meta.AnnotationKeyExternalCreatePending: "2022-04-13T14:57:35Z",
					},
					Name:      podName,
					Namespace: podNs,
				},
			},
			expect: &corev1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name:      podName,
					Namespace: podNs,
				},
			},
		},
		"doesn't contain annotation": {
			obj: &corev1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Annotations: map[string]string{},
					Name:        podName,
					Namespace:   podNs,
				},
			},
			expect: &corev1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name:      podName,
					Namespace: podNs,
				},
			},
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			s := runtime.NewScheme()
			g.Expect(scheme.AddToScheme(s)).To(Succeed())
			cli := fake.NewClientBuilder().WithScheme(s).WithObjects(tc.obj).Build()
			patch, err := ConstructPatch(RemoveCrossplaneExternalCreatePending(tc.obj))
			g.Expect(err).To(Succeed())
			g.Expect(cli.Patch(context.TODO(), tc.obj, patch)).To(Succeed())
			newObj := corev1.Pod{}
			g.Expect(cli.Get(context.TODO(), types.NamespacedName{Name: podName, Namespace: podNs}, &newObj)).To(Succeed())
			g.Expect(newObj.GetAnnotations()).To(Equal(tc.expect.GetAnnotations()))
		})
	}
}

func TestTriggerDeletionPatch(t *testing.T) {
	const (
		podName = "test-name"
		podNs   = "test-namespace"
	)
	cases := map[string]struct {
		obj client.Object
	}{
		"contains annotation": {
			obj: &corev1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Annotations: map[string]string{
						pingcapDeletionTrigger: "2022-04-13T14:57:35Z",
					},
					Name:      podName,
					Namespace: podNs,
				},
			},
		},
		"doesn't contain annotation": {
			obj: &corev1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Annotations: map[string]string{
						"foo": "bar",
					},
					Name:      podName,
					Namespace: podNs,
				},
			},
		},
	}
	for name, tc := range cases {
		t.Run(name, func(t *testing.T) {
			g := NewGomegaWithT(t)
			s := runtime.NewScheme()
			g.Expect(scheme.AddToScheme(s)).To(Succeed())
			cli := fake.NewClientBuilder().WithScheme(s).WithObjects(tc.obj).Build()
			patch, err := ConstructPatch(DeletionTrigger(tc.obj))
			g.Expect(err).To(Succeed())
			g.Expect(cli.Patch(context.TODO(), tc.obj, patch)).To(Succeed())
			newObj := corev1.Pod{}
			g.Expect(cli.Get(context.TODO(), types.NamespacedName{Name: podName, Namespace: podNs}, &newObj)).To(Succeed())
			g.Expect(newObj.GetAnnotations()).To(HaveKey(pingcapDeletionTrigger))
		})
	}
}
