package network

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	k8sruntime "k8s.io/apimachinery/pkg/runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	awsfake "github.com/tidbcloud/infra-api/pkg/providers/aws/fake"
	"github.com/tidbcloud/shared-provider/pkg/types"
	"github.com/tidbcloud/shared-provider/pkg/utils/labels"
	"github.com/tidbcloud/shared-provider/pkg/utils/namespace"
)

func TestSyncNamespace(t *testing.T) {
	var (
		net = cloudv1.Network{
			ObjectMeta: metav1.ObjectMeta{
				Namespace: "network-ns1",
				Name:      "ns-network",
				UID:       "net-uid",
				Labels: map[string]string{
					"tags.tidbcloud.com/kubernetes": kubeClusterName,
				},
			},
			Spec: cloudv1.NetworkSpec{
				Provider: types.ProviderTypeAWSSharedTier,
				Region:   region,
				CIDR:     cidr,
			},
		}
		ns = corev1.Namespace{
			ObjectMeta: metav1.ObjectMeta{
				Name: namespace.GetOwnedNamespaceNameFromNetwork(&net),
			},
		}

		objs          []k8sruntime.Object
		infraObjs     = []k8sruntime.Object{&net}
		fakeAwsClient = awsfake.NewMockClient(t)
		r             = newFakeReconciler(objs, infraObjs)
	)

	mockAWSClient(fakeAwsClient)
	r.AwsClient = fakeAwsClient

	// sync without namespace
	nsGet := &corev1.Namespace{}
	assert.True(t, apierrors.IsNotFound(r.Client.Get(context.Background(), client.ObjectKey{Name: ns.Name}, nsGet)))
	assert.NoError(t, r.syncNamespace(context.Background(), &net))
	assert.NoError(t, r.Client.Get(context.Background(), client.ObjectKey{Name: ns.Name}, nsGet)) // namespace created
	assert.Equal(t, ns.Name, nsGet.Name)
	assert.Equal(t, labels.GetKubeClusterFromLabels(nsGet.Labels), r.KubeClusterName)
	_, netNs, netName, netUID := labels.GetOwnerFromLabels(nsGet.Labels)
	assert.Equal(t, net.Namespace, netNs)
	assert.Equal(t, net.Name, netName)
	assert.Equal(t, string(net.UID), netUID)
	assert.False(t, namespace.HasOwnedNamespaceName(&net)) // no own ns set yet

	// sync with namespace again, own ns set
	assert.NoError(t, r.syncNamespace(context.Background(), &net))
	assert.True(t, namespace.HasOwnedNamespaceName(&net))

	// sync with a wrong UID label
	wrongUID := "wrong UID"
	nsGet.Labels[labels.LabelKeyUID] = wrongUID
	assert.NoError(t, r.Client.Update(context.Background(), nsGet))
	assert.NoError(t, r.Client.Get(context.Background(), client.ObjectKey{Name: ns.Name}, nsGet))
	// nolint: dogsled // only need to use netUID
	_, _, _, netUID = labels.GetOwnerFromLabels(nsGet.Labels)
	assert.Equal(t, wrongUID, netUID)
	assert.NoError(t, r.syncNamespace(context.Background(), &net))
	assert.NoError(t, r.Client.Get(context.Background(), client.ObjectKey{Name: ns.Name}, nsGet))
	// nolint: dogsled // only need to use netUID
	_, _, _, netUID = labels.GetOwnerFromLabels(nsGet.Labels)
	assert.Equal(t, string(net.UID), netUID) // updated
}
