package testing

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	kubetesting "k8s.io/client-go/testing"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

func TestGet(t *testing.T) {
	cases := []struct {
		desc string
		key  client.ObjectKey
		obj  client.Object

		expectedAction kubetesting.Action
		expectedObj    client.Object
	}{
		{
			desc: "normal get cluster scope resource",
			key:  client.ObjectKey{Name: "aaa"},
			obj:  &corev1.Namespace{},

			expectedAction: kubetesting.NewRootGetAction(
				schema.GroupVersionResource{
					Group:    "",
					Version:  "v1",
					Resource: "namespaces",
				},
				"aaa",
			),
			expectedObj: &corev1.Namespace{
				ObjectMeta: metav1.ObjectMeta{
					Name: "aaa",
				},
			},
		},
		{
			desc: "normal get in namespace resource",
			key:  client.ObjectKey{Name: "aaa", Namespace: "xxx"},
			obj:  &corev1.Pod{},

			expectedAction: kubetesting.NewGetAction(
				schema.GroupVersionResource{
					Group:    "",
					Version:  "v1",
					Resource: "pods",
				},
				"xxx",
				"aaa",
			),
			expectedObj: &corev1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name:      "aaa",
					Namespace: "xxx",
				},
			},
		},
		{
			desc: "get by unstructured",
			key:  client.ObjectKey{Name: "aaa"},
			obj: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"apiVersion": "v1",
					"kind":       "Namespace",
				},
			},

			expectedAction: kubetesting.NewRootGetAction(
				schema.GroupVersionResource{
					Group:    "",
					Version:  "v1",
					Resource: "namespaces",
				},
				"aaa",
			),
			expectedObj: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"apiVersion": "v1",
					"kind":       "Namespace",
					"metadata": map[string]interface{}{
						"name":              "aaa",
						"creationTimestamp": nil,
					},
					"spec":   map[string]interface{}{},
					"status": map[string]interface{}{},
				},
			},
		},
	}

	objs := []client.Object{
		&corev1.Namespace{
			ObjectMeta: metav1.ObjectMeta{
				Name: "aaa",
			},
		},
		&corev1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "aaa",
				Namespace: "xxx",
			},
		},
	}

	scheme := runtime.NewScheme()
	utilruntime.Must(clientgoscheme.AddToScheme(scheme))

	for _, c := range cases {
		fc := NewClientBuilder().
			WithScheme(scheme).
			WithObjects(objs...).
			Build()

		fc.PrependReactor("*", "*", func(act kubetesting.Action) (bool, runtime.Object, error) {
			assert.Equal(t, c.expectedAction, act, c.desc)
			return false, nil, nil
		})

		err := fc.Get(context.TODO(), c.key, c.obj)
		assert.NoError(t, err, c.desc)
		assert.Equal(t, c.expectedObj, c.obj, c.desc)
	}
}
