package clusterupgradeoperation

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	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"

	"github.com/tidbcloud/observability/pkg/resourceworker/controllers/clusterupgradeoperation/providers"
)

func TestGetNodeGroupVersionFromNodes(t *testing.T) {
	scheme := runtime.NewScheme()
	require.NoError(t, corev1.AddToScheme(scheme))

	// MockCloudProvider.GetNodeGroupLabelSelector returns {"node-group": nodeGroupID}
	// So for nodeGroup.ID = "ng-1", we need nodes with label "node-group": "ng-1"
	mockProvider := &MockCloudProvider{}

	nodeGroup := &providers.NodeGroup{ID: "ng-1", Name: "ng-1"}

	t.Run("empty node list returns empty version", func(t *testing.T) {
		fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
		version := GetNodeGroupVersionFromNodes(context.Background(), fakeClient, mockProvider, "cluster", nodeGroup)
		assert.Empty(t, version)
	})

	t.Run("single node returns kubelet version", func(t *testing.T) {
		node := &corev1.Node{
			ObjectMeta: metav1.ObjectMeta{
				Name:   "node-1",
				Labels: map[string]string{"node-group": "ng-1"},
			},
			Status: corev1.NodeStatus{
				NodeInfo: corev1.NodeSystemInfo{KubeletVersion: "v1.28.5"},
			},
		}
		fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(node).Build()
		version := GetNodeGroupVersionFromNodes(context.Background(), fakeClient, mockProvider, "cluster", nodeGroup)
		assert.Equal(t, "v1.28.5", version)
	})

	t.Run("all nodes same version returns version", func(t *testing.T) {
		nodes := []client.Object{
			&corev1.Node{
				ObjectMeta: metav1.ObjectMeta{Name: "node-1", Labels: map[string]string{"node-group": "ng-1"}},
				Status:     corev1.NodeStatus{NodeInfo: corev1.NodeSystemInfo{KubeletVersion: "v1.28.5"}},
			},
			&corev1.Node{
				ObjectMeta: metav1.ObjectMeta{Name: "node-2", Labels: map[string]string{"node-group": "ng-1"}},
				Status:     corev1.NodeStatus{NodeInfo: corev1.NodeSystemInfo{KubeletVersion: "v1.28.5"}},
			},
		}
		fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(nodes...).Build()
		version := GetNodeGroupVersionFromNodes(context.Background(), fakeClient, mockProvider, "cluster", nodeGroup)
		assert.Equal(t, "v1.28.5", version)
	})

	t.Run("nodes with different versions returns empty", func(t *testing.T) {
		nodes := []client.Object{
			&corev1.Node{
				ObjectMeta: metav1.ObjectMeta{Name: "node-1", Labels: map[string]string{"node-group": "ng-1"}},
				Status:     corev1.NodeStatus{NodeInfo: corev1.NodeSystemInfo{KubeletVersion: "v1.28.5"}},
			},
			&corev1.Node{
				ObjectMeta: metav1.ObjectMeta{Name: "node-2", Labels: map[string]string{"node-group": "ng-1"}},
				Status:     corev1.NodeStatus{NodeInfo: corev1.NodeSystemInfo{KubeletVersion: "v1.29.0"}},
			},
		}
		fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(nodes...).Build()
		version := GetNodeGroupVersionFromNodes(context.Background(), fakeClient, mockProvider, "cluster", nodeGroup)
		assert.Empty(t, version)
	})
}
