package agentbuild

import (
	"context"
	"testing"

	vmoperatorv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1"
	ec2v1alpha1 "github.com/crossplane-contrib/provider-aws/apis/ec2/v1alpha1"
	eksv1alpha1 "github.com/crossplane-contrib/provider-aws/apis/eks/manualv1alpha1"
	gkecontainerv1beta1 "github.com/crossplane-contrib/provider-gcp/apis/container/v1beta1"
	"github.com/stretchr/testify/assert"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/observability/agent-control-server/pkg/testkits"
)

func TestCheckAndUpdateServiceAccountAnnotations(t *testing.T) {
	tests := []struct {
		name           string
		setupResources []client.Object
		targetName     string
		targetNS       string
		annotationKey  string
		wantUpdated    bool
		wantErr        bool
		verify         func(t *testing.T, cli client.Client)
	}{
		{
			name:           "service account not found",
			setupResources: nil,
			targetName:     "missing-sa",
			targetNS:       "default",
			annotationKey:  "some-key",
			wantUpdated:    false,
			wantErr:        false,
		},
		{
			name: "annotation not present",
			setupResources: []client.Object{
				&corev1.ServiceAccount{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "sa-1",
						Namespace: "default",
						Labels:    map[string]string{"foo": "bar"},
					},
				},
			},
			targetName:    "sa-1",
			targetNS:      "default",
			annotationKey: "some-key",
			wantUpdated:   false,
			wantErr:       false,
		},
		{
			name: "annotation present and removed",
			setupResources: []client.Object{
				&corev1.ServiceAccount{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "sa-3",
						Namespace: "default",
						Annotations: map[string]string{
							"remove-me": "val",
							"keep-me":   "val2",
						},
					},
				},
			},
			targetName:    "sa-3",
			targetNS:      "default",
			annotationKey: "remove-me",
			wantUpdated:   true,
			wantErr:       false,
			verify: func(t *testing.T, cli client.Client) {
				sa := &corev1.ServiceAccount{}
				err := cli.Get(context.Background(), types.NamespacedName{Name: "sa-3", Namespace: "default"}, sa)
				assert.NoError(t, err)

				_, ok := sa.Annotations["remove-me"]
				assert.False(t, ok)
				assert.Equal(t, "val2", sa.Annotations["keep-me"])
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			fakeCli := testkits.BuildClient(tt.setupResources...)
			updated, err := CheckAndUpdateServiceAccountAnnotations(context.Background(), fakeCli, tt.targetName, tt.targetNS, tt.annotationKey)
			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}

			assert.Equal(t, tt.wantUpdated, updated)
			if tt.verify != nil {
				tt.verify(t, fakeCli)
			}
		})
	}
}

func TestCheckAndUpdatePVCLabels(t *testing.T) {
	tests := []struct {
		name           string
		setupResources []client.Object
		targetName     string
		targetNS       string
		desiredLabels  map[string]string
		wantUpdated    bool
		wantErr        bool
		verify         func(t *testing.T, cli client.Client)
	}{
		{
			name:           "pvc not found",
			setupResources: nil,
			targetName:     "missing-pvc",
			targetNS:       "default",
			desiredLabels:  map[string]string{"foo": "bar"},
			wantUpdated:    false,
			wantErr:        true,
		},
		{
			name: "add new labels to nil map",
			setupResources: []client.Object{
				&corev1.PersistentVolumeClaim{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "pvc-1",
						Namespace: "default",
					},
				},
			},
			targetName:    "pvc-1",
			targetNS:      "default",
			desiredLabels: map[string]string{"foo": "bar"},
			wantUpdated:   true,
			wantErr:       false,
			verify: func(t *testing.T, cli client.Client) {
				pvc := &corev1.PersistentVolumeClaim{}
				err := cli.Get(context.Background(), types.NamespacedName{Name: "pvc-1", Namespace: "default"}, pvc)
				assert.NoError(t, err)
				assert.Equal(t, "bar", pvc.Labels["foo"])
			},
		},
		{
			name: "update existing labels",
			setupResources: []client.Object{
				&corev1.PersistentVolumeClaim{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "pvc-2",
						Namespace: "default",
						Labels: map[string]string{
							"foo": "old",
							"baz": "qux",
						},
					},
				},
			},
			targetName:    "pvc-2",
			targetNS:      "default",
			desiredLabels: map[string]string{"foo": "new"},
			wantUpdated:   true,
			wantErr:       false,
			verify: func(t *testing.T, cli client.Client) {
				pvc := &corev1.PersistentVolumeClaim{}
				err := cli.Get(context.Background(), types.NamespacedName{Name: "pvc-2", Namespace: "default"}, pvc)
				assert.NoError(t, err)
				assert.Equal(t, "new", pvc.Labels["foo"])
				assert.Equal(t, "qux", pvc.Labels["baz"])
			},
		},
		{
			name: "no update needed",
			setupResources: []client.Object{
				&corev1.PersistentVolumeClaim{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "pvc-3",
						Namespace: "default",
						Labels: map[string]string{
							"foo": "bar",
						},
					},
				},
			},
			targetName:    "pvc-3",
			targetNS:      "default",
			desiredLabels: map[string]string{"foo": "bar"},
			wantUpdated:   false,
			wantErr:       false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			fakeCli := testkits.BuildClient(tt.setupResources...)
			updated, err := CheckAndUpdatePVCLabels(context.Background(), fakeCli, tt.targetName, tt.targetNS, tt.desiredLabels)
			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}

			assert.Equal(t, tt.wantUpdated, updated)
			if tt.verify != nil {
				tt.verify(t, fakeCli)
			}
		})
	}
}

type checkReadyTestCase struct {
	name           string
	setupResources []client.Object
	targetName     string
	targetNS       string
	wantErr        bool
	errMsg         string
}

func runCheckReadyTests(
	t *testing.T,
	checkFunc func(ctx context.Context, cli client.Client, name, namespace string) error,
	cases []checkReadyTestCase,
) {
	for _, tt := range cases {
		t.Run(tt.name, func(t *testing.T) {
			fakeCli := testkits.BuildClient(tt.setupResources...)
			err := checkFunc(context.Background(), fakeCli, tt.targetName, tt.targetNS)
			if tt.wantErr {
				assert.Error(t, err)
				assert.Contains(t, err.Error(), tt.errMsg)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestCheckDaemonSetReady(t *testing.T) {
	runCheckReadyTests(t, CheckDaemonSetReady, []checkReadyTestCase{
		{
			name:           "daemonset not found",
			setupResources: nil,
			targetName:     "missing-ds",
			targetNS:       "default",
			wantErr:        true,
			errMsg:         "failed to get daemonset",
		},
		{
			name: "daemonset not ready",
			setupResources: []client.Object{
				&appsv1.DaemonSet{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "ds-1",
						Namespace: "default",
					},
					Status: appsv1.DaemonSetStatus{
						DesiredNumberScheduled: 3,
						NumberReady:            2,
					},
				},
			},
			targetName: "ds-1",
			targetNS:   "default",
			wantErr:    true,
			errMsg:     "is not ready",
		},
		{
			name: "daemonset ready",
			setupResources: []client.Object{
				&appsv1.DaemonSet{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "ds-2",
						Namespace: "default",
					},
					Status: appsv1.DaemonSetStatus{
						DesiredNumberScheduled: 3,
						NumberReady:            3,
					},
				},
			},
			targetName: "ds-2",
			targetNS:   "default",
			wantErr:    false,
		},
	})
}

func TestCheckDeploymentReady(t *testing.T) {
	runCheckReadyTests(t, CheckDeploymentReady, []checkReadyTestCase{
		{
			name:           "deployment not found",
			setupResources: nil,
			targetName:     "missing-dep",
			targetNS:       "default",
			wantErr:        true,
			errMsg:         "failed to get deployment",
		},
		{
			name: "deployment not ready",
			setupResources: []client.Object{
				&appsv1.Deployment{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "dep-1",
						Namespace: "default",
					},
					Status: appsv1.DeploymentStatus{
						Conditions: []appsv1.DeploymentCondition{
							{
								Type:   appsv1.DeploymentAvailable,
								Status: "False",
							},
						},
					},
				},
			},
			targetName: "dep-1",
			targetNS:   "default",
			wantErr:    true,
			errMsg:     "is not ready",
		},
		{
			name: "deployment ready",
			setupResources: []client.Object{
				&appsv1.Deployment{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "dep-2",
						Namespace: "default",
					},
					Status: appsv1.DeploymentStatus{
						Conditions: []appsv1.DeploymentCondition{
							{
								Type:   appsv1.DeploymentAvailable,
								Status: "True",
							},
						},
					},
				},
			},
			targetName: "dep-2",
			targetNS:   "default",
			wantErr:    false,
		},
	})
}

func TestCheckStatefulSetReady(t *testing.T) {
	runCheckReadyTests(t, CheckStatefulSetReady, []checkReadyTestCase{
		{
			name:           "statefulset not found",
			setupResources: nil,
			targetName:     "missing-sts",
			targetNS:       "default",
			wantErr:        true,
			errMsg:         "failed to get statefulset",
		},
		{
			name: "statefulset replicas nil",
			setupResources: []client.Object{
				&appsv1.StatefulSet{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "sts-1",
						Namespace: "default",
					},
					Spec: appsv1.StatefulSetSpec{
						Replicas: nil,
					},
				},
			},
			targetName: "sts-1",
			targetNS:   "default",
			wantErr:    true,
			errMsg:     "is not ready",
		},
		{
			name: "statefulset not ready",
			setupResources: []client.Object{
				&appsv1.StatefulSet{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "sts-2",
						Namespace: "default",
					},
					Spec: appsv1.StatefulSetSpec{
						Replicas: func() *int32 { i := int32(3); return &i }(),
					},
					Status: appsv1.StatefulSetStatus{
						AvailableReplicas: 2,
					},
				},
			},
			targetName: "sts-2",
			targetNS:   "default",
			wantErr:    true,
			errMsg:     "is not ready",
		},
		{
			name: "statefulset ready",
			setupResources: []client.Object{
				&appsv1.StatefulSet{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "sts-3",
						Namespace: "default",
					},
					Spec: appsv1.StatefulSetSpec{
						Replicas: func() *int32 { i := int32(3); return &i }(),
					},
					Status: appsv1.StatefulSetStatus{
						AvailableReplicas: 3,
					},
				},
			},
			targetName: "sts-3",
			targetNS:   "default",
			wantErr:    false,
		},
	})
}

func TestCheckVMPodScrapeReady(t *testing.T) {
	runCheckReadyTests(t, CheckVMPodScrapeReady, []checkReadyTestCase{
		{
			name:           "vmpodscrape not found",
			setupResources: nil,
			targetName:     "missing-scrape",
			targetNS:       "default",
			wantErr:        true,
			errMsg:         "failed to get vmpodscrape",
		},
		{
			name: "vmpodscrape not operational",
			setupResources: []client.Object{
				&vmoperatorv1beta1.VMPodScrape{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "scrape-1",
						Namespace: "default",
					},
					Status: vmoperatorv1beta1.ScrapeObjectStatus{
						StatusMetadata: vmoperatorv1beta1.StatusMetadata{
							UpdateStatus: "pending",
						},
					},
				},
			},
			targetName: "scrape-1",
			targetNS:   "default",
			wantErr:    true,
			errMsg:     "status is not operational",
		},
		{
			name: "vmpodscrape operational",
			setupResources: []client.Object{
				&vmoperatorv1beta1.VMPodScrape{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "scrape-2",
						Namespace: "default",
					},
					Status: vmoperatorv1beta1.ScrapeObjectStatus{
						StatusMetadata: vmoperatorv1beta1.StatusMetadata{
							UpdateStatus: vmoperatorv1beta1.UpdateStatusOperational,
						},
					},
				},
			},
			targetName: "scrape-2",
			targetNS:   "default",
			wantErr:    false,
		},
	})
}

func TestCheckVMServiceScrapeReady(t *testing.T) {
	runCheckReadyTests(t, CheckVMServiceScrapeReady, []checkReadyTestCase{
		{
			name:           "vmservicescrape not found",
			setupResources: nil,
			targetName:     "missing-svc-scrape",
			targetNS:       "default",
			wantErr:        true,
			errMsg:         "failed to get vmservicescrape",
		},
		{
			name: "vmservicescrape not operational",
			setupResources: []client.Object{
				&vmoperatorv1beta1.VMServiceScrape{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "svc-scrape-1",
						Namespace: "default",
					},
					Status: vmoperatorv1beta1.ScrapeObjectStatus{
						StatusMetadata: vmoperatorv1beta1.StatusMetadata{
							UpdateStatus: "pending",
						},
					},
				},
			},
			targetName: "svc-scrape-1",
			targetNS:   "default",
			wantErr:    true,
			errMsg:     "status is not operational",
		},
		{
			name: "vmservicescrape operational",
			setupResources: []client.Object{
				&vmoperatorv1beta1.VMServiceScrape{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "svc-scrape-2",
						Namespace: "default",
					},
					Status: vmoperatorv1beta1.ScrapeObjectStatus{
						StatusMetadata: vmoperatorv1beta1.StatusMetadata{
							UpdateStatus: vmoperatorv1beta1.UpdateStatusOperational,
						},
					},
				},
			},
			targetName: "svc-scrape-2",
			targetNS:   "default",
			wantErr:    false,
		},
	})
}

func TestCheckNodeGroupReady(t *testing.T) {
	runCheckReadyTests(t, func(ctx context.Context, cli client.Client, name, namespace string) error {
		return CheckNodeGroupReady(ctx, cli, name)
	}, []checkReadyTestCase{
		{
			name:           "nodegroup not found",
			setupResources: nil,
			targetName:     "missing-ng",
			wantErr:        true,
			errMsg:         "failed to get nodegroup",
		},
		{
			name: "nodegroup not ready",
			setupResources: []client.Object{
				&eksv1alpha1.NodeGroup{
					ObjectMeta: metav1.ObjectMeta{
						Name: "ng-1",
					},
					Status: eksv1alpha1.NodeGroupStatus{
						AtProvider: eksv1alpha1.NodeGroupObservation{
							Status: "creating",
						},
					},
				},
			},
			targetName: "ng-1",
			wantErr:    true,
			errMsg:     "is not ready",
		},
		{
			name: "nodegroup ready",
			setupResources: []client.Object{
				&eksv1alpha1.NodeGroup{
					ObjectMeta: metav1.ObjectMeta{
						Name: "ng-2",
					},
					Status: eksv1alpha1.NodeGroupStatus{
						AtProvider: eksv1alpha1.NodeGroupObservation{
							Status: eksv1alpha1.NodeGroupStatusActive,
						},
					},
				},
			},
			targetName: "ng-2",
			wantErr:    false,
		},
	})
}

func TestCheckNodePoolReady(t *testing.T) {
	runCheckReadyTests(t, func(ctx context.Context, cli client.Client, name, namespace string) error {
		return CheckNodePoolReady(ctx, cli, name)
	}, []checkReadyTestCase{
		{
			name:           "nodepool not found",
			setupResources: nil,
			targetName:     "missing-np",
			wantErr:        true,
			errMsg:         "failed to get nodepool",
		},
		{
			name: "nodepool not ready",
			setupResources: []client.Object{
				&gkecontainerv1beta1.NodePool{
					ObjectMeta: metav1.ObjectMeta{
						Name: "np-1",
					},
					Status: gkecontainerv1beta1.NodePoolStatus{
						AtProvider: gkecontainerv1beta1.NodePoolObservation{
							Status: "provisioning",
						},
					},
				},
			},
			targetName: "np-1",
			wantErr:    true,
			errMsg:     "is not ready",
		},
		{
			name: "nodepool ready",
			setupResources: []client.Object{
				&gkecontainerv1beta1.NodePool{
					ObjectMeta: metav1.ObjectMeta{
						Name: "np-2",
					},
					Status: gkecontainerv1beta1.NodePoolStatus{
						AtProvider: gkecontainerv1beta1.NodePoolObservation{
							Status: gkecontainerv1beta1.NodePoolStateRunning,
						},
					},
				},
			},
			targetName: "np-2",
			wantErr:    false,
		},
	})
}

func TestCheckNamespaceExists(t *testing.T) {
	tests := []struct {
		name            string
		setupResources  []client.Object
		targetNamespace string
		wantErr         bool
	}{
		{
			name:            "namespace not found",
			setupResources:  nil,
			targetNamespace: "missing-ns",
			wantErr:         true,
		},
		{
			name: "namespace exists",
			setupResources: []client.Object{
				&corev1.Namespace{
					ObjectMeta: metav1.ObjectMeta{
						Name: "test-ns",
					},
				},
			},
			targetNamespace: "test-ns",
			wantErr:         false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			fakeCli := testkits.BuildClient(tt.setupResources...)
			err := CheckNamespaceExists(context.Background(), fakeCli, tt.targetNamespace)
			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

type checkDeletedTestCase struct {
	name           string
	setupResources []client.Object
	targetName     string
	targetNS       string
	wantErr        bool
	errMsg         string
}

func runCheckDeletedTests(
	t *testing.T,
	checkFunc func(ctx context.Context, cli client.Client, name, namespace string) error,
	cases []checkDeletedTestCase,
) {
	for _, tt := range cases {
		t.Run(tt.name, func(t *testing.T) {
			fakeCli := testkits.BuildClient(tt.setupResources...)
			err := checkFunc(context.Background(), fakeCli, tt.targetName, tt.targetNS)
			if tt.wantErr {
				assert.Error(t, err)
				assert.Contains(t, err.Error(), tt.errMsg)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func getStandardCheckDeletedTestCases[T client.Object](
	newObj func(name, namespace string) T,
	namespace string,
) []checkDeletedTestCase {
	var targetName string
	if namespace != "" {
		targetName = "deleted-ns"
	} else {
		targetName = "deleted-cluster"
	}
	return []checkDeletedTestCase{
		{
			name:           "resource deleted (not found)",
			setupResources: nil,
			targetName:     targetName,
			targetNS:       namespace,
			wantErr:        false,
		},
		{
			name:           "resource still present",
			setupResources: []client.Object{newObj("test-resource", namespace)},
			targetName:     "test-resource",
			targetNS:       namespace,
			wantErr:        true,
			errMsg:         "still present",
		},
	}
}

type checkDeletedWithLabelsTestCase struct {
	name           string
	setupResources []client.Object
	labels         map[string]string
	wantErr        bool
}

func runCheckDeletedWithLabelsTests(
	t *testing.T,
	checkFunc func(ctx context.Context, cli client.Client, labels map[string]string) error,
	cases []checkDeletedWithLabelsTestCase,
) {
	for _, tt := range cases {
		t.Run(tt.name, func(t *testing.T) {
			fakeCli := testkits.BuildClient(tt.setupResources...)
			err := checkFunc(context.Background(), fakeCli, tt.labels)
			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func getStandardCheckDeletedWithLabelsTestCases[T client.Object](
	newObj func(name string, labels map[string]string) T,
) []checkDeletedWithLabelsTestCase {
	return []checkDeletedWithLabelsTestCase{
		{
			name:           "no items",
			setupResources: []client.Object{},
			labels:         map[string]string{"app": "test"},
			wantErr:        false,
		},
		{
			name:           "items present but not matching labels",
			setupResources: []client.Object{newObj("resource-1", map[string]string{"app": "other"})},
			labels:         map[string]string{"app": "test"},
			wantErr:        false,
		},
		{
			name:           "items present and matching labels",
			setupResources: []client.Object{newObj("resource-1", map[string]string{"app": "test"})},
			labels:         map[string]string{"app": "test"},
			wantErr:        true,
		},
	}
}

func TestCheckNodeGroupDeletedWithLabels(t *testing.T) {
	runCheckDeletedWithLabelsTests(t, CheckNodeGroupDeletedWithLabels, getStandardCheckDeletedWithLabelsTestCases(
		func(name string, labels map[string]string) *eksv1alpha1.NodeGroup {
			return &eksv1alpha1.NodeGroup{ObjectMeta: metav1.ObjectMeta{Name: name, Labels: labels}}
		},
	))
}

func TestCheckNodePoolDeletedWithLabels(t *testing.T) {
	runCheckDeletedWithLabelsTests(t, CheckNodePoolDeletedWithLabels, getStandardCheckDeletedWithLabelsTestCases(
		func(name string, labels map[string]string) *gkecontainerv1beta1.NodePool {
			return &gkecontainerv1beta1.NodePool{ObjectMeta: metav1.ObjectMeta{Name: name, Labels: labels}}
		},
	))
}

func TestCheckDaemonSetDeleted(t *testing.T) {
	runCheckDeletedTests(t, CheckDaemonSetDeleted, getStandardCheckDeletedTestCases(
		func(name, namespace string) *appsv1.DaemonSet {
			return &appsv1.DaemonSet{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}}
		},
		"default",
	))
}

func TestCheckStatefulSetDeleted(t *testing.T) {
	runCheckDeletedTests(t, CheckStatefulSetDeleted, getStandardCheckDeletedTestCases(
		func(name, namespace string) *appsv1.StatefulSet {
			return &appsv1.StatefulSet{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}}
		},
		"default",
	))
}

func TestCheckVMPodScrapeDeleted(t *testing.T) {
	runCheckDeletedTests(t, CheckVMPodScrapeDeleted, getStandardCheckDeletedTestCases(
		func(name, namespace string) *vmoperatorv1beta1.VMPodScrape {
			return &vmoperatorv1beta1.VMPodScrape{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}}
		},
		"default",
	))
}

func TestCheckVMServiceScrapeDeleted(t *testing.T) {
	runCheckDeletedTests(t, CheckVMServiceScrapeDeleted, getStandardCheckDeletedTestCases(
		func(name, namespace string) *vmoperatorv1beta1.VMServiceScrape {
			return &vmoperatorv1beta1.VMServiceScrape{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}}
		},
		"default",
	))
}

func TestCheckNodeGroupDeleted(t *testing.T) {
	runCheckDeletedTests(t, func(ctx context.Context, cli client.Client, name, namespace string) error {
		return CheckNodeGroupDeleted(ctx, cli, name)
	}, getStandardCheckDeletedTestCases(
		func(name, namespace string) *eksv1alpha1.NodeGroup {
			return &eksv1alpha1.NodeGroup{ObjectMeta: metav1.ObjectMeta{Name: name}}
		},
		"",
	))
}

func TestCheckNodePoolDeleted(t *testing.T) {
	runCheckDeletedTests(t, func(ctx context.Context, cli client.Client, name, namespace string) error {
		return CheckNodePoolDeleted(ctx, cli, name)
	}, getStandardCheckDeletedTestCases(
		func(name, namespace string) *gkecontainerv1beta1.NodePool {
			return &gkecontainerv1beta1.NodePool{ObjectMeta: metav1.ObjectMeta{Name: name}}
		},
		"",
	))
}

func TestCheckAwsVPCEndpointDeleted(t *testing.T) {
	runCheckDeletedTests(t, func(ctx context.Context, cli client.Client, name, namespace string) error {
		return CheckAwsVPCEndpointDeleted(ctx, cli, name)
	}, getStandardCheckDeletedTestCases(
		func(name, namespace string) *ec2v1alpha1.VPCEndpoint {
			return &ec2v1alpha1.VPCEndpoint{ObjectMeta: metav1.ObjectMeta{Name: name}}
		},
		"",
	))
}

func TestCheckVMAgentsDeleted(t *testing.T) {
	runCheckDeletedTests(t, CheckVMAgentsDeleted, getStandardCheckDeletedTestCases(
		func(name, namespace string) *vmoperatorv1beta1.VMAgent {
			return &vmoperatorv1beta1.VMAgent{ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace}}
		},
		"default",
	))
}

func TestCheckStatefulSetReadyWithLabels(t *testing.T) {
	tests := []struct {
		name           string
		setupResources []client.Object
		namespace      string
		labels         map[string]string
		wantErr        bool
	}{
		{
			name:           "no matching statefulsets returns nil",
			setupResources: nil,
			namespace:      "default",
			labels:         map[string]string{"app": "test"},
			wantErr:        false,
		},
		{
			name: "matching statefulset not ready",
			setupResources: []client.Object{
				&appsv1.StatefulSet{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "sts-1",
						Namespace: "default",
						Labels:    map[string]string{"app": "test"},
					},
					Spec: appsv1.StatefulSetSpec{
						Replicas: func() *int32 { i := int32(3); return &i }(),
					},
					Status: appsv1.StatefulSetStatus{
						AvailableReplicas: 1,
					},
				},
			},
			namespace: "default",
			labels:    map[string]string{"app": "test"},
			wantErr:   true,
		},
		{
			name: "matching statefulset ready",
			setupResources: []client.Object{
				&appsv1.StatefulSet{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "sts-2",
						Namespace: "default",
						Labels:    map[string]string{"app": "test"},
					},
					Spec: appsv1.StatefulSetSpec{
						Replicas: func() *int32 { i := int32(3); return &i }(),
					},
					Status: appsv1.StatefulSetStatus{
						AvailableReplicas: 3,
					},
				},
			},
			namespace: "default",
			labels:    map[string]string{"app": "test"},
			wantErr:   false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			fakeCli := testkits.BuildClient(tt.setupResources...)
			err := CheckStatefulSetReadyWithLabels(context.Background(), fakeCli, tt.namespace, tt.labels)
			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestCheckStatefulSetDeletedWithLabels(t *testing.T) {
	tests := []struct {
		name           string
		setupResources []client.Object
		namespace      string
		labels         map[string]string
		wantErr        bool
	}{
		{
			name:           "no matching statefulsets returns nil",
			setupResources: nil,
			namespace:      "default",
			labels:         map[string]string{"app": "test"},
			wantErr:        false,
		},
		{
			name: "matching statefulsets still present",
			setupResources: []client.Object{
				&appsv1.StatefulSet{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "sts-1",
						Namespace: "default",
						Labels:    map[string]string{"app": "test"},
					},
				},
			},
			namespace: "default",
			labels:    map[string]string{"app": "test"},
			wantErr:   true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			fakeCli := testkits.BuildClient(tt.setupResources...)
			err := CheckStatefulSetDeletedWithLabels(context.Background(), fakeCli, tt.namespace, tt.labels)
			if tt.wantErr {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}
