/*
Copyright 2022 The Karmada Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package native

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	appsv1 "k8s.io/api/apps/v1"
	autoscalingv2 "k8s.io/api/autoscaling/v2"
	batchv1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
	networkingv1 "k8s.io/api/networking/v1"
	policyv1 "k8s.io/api/policy/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"

	workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
	"github.com/karmada-io/karmada/pkg/util"
	"github.com/karmada-io/karmada/pkg/util/helper"
)

func Test_reflectPodDisruptionBudgetStatus(t *testing.T) {
	tests := []struct {
		name    string
		object  *unstructured.Unstructured
		want    *runtime.RawExtension
		wantErr bool
	}{
		{
			name: "PDB with valid status",
			object: func() *unstructured.Unstructured {
				pdb := &policyv1.PodDisruptionBudget{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "test-pdb",
						Namespace: "test-ns",
					},
					Status: policyv1.PodDisruptionBudgetStatus{
						CurrentHealthy:     2,
						DesiredHealthy:     3,
						DisruptionsAllowed: 1,
						ExpectedPods:       3,
						DisruptedPods: map[string]metav1.Time{
							"pod1": metav1.Now(),
						},
					},
				}
				obj, _ := helper.ToUnstructured(pdb)
				return obj
			}(),
			want: func() *runtime.RawExtension {
				status := &policyv1.PodDisruptionBudgetStatus{
					CurrentHealthy:     2,
					DesiredHealthy:     3,
					DisruptionsAllowed: 1,
					ExpectedPods:       3,
					DisruptedPods: map[string]metav1.Time{
						"pod1": metav1.Now(),
					},
				}
				raw, _ := helper.BuildStatusRawExtension(status)
				return raw
			}(),
			wantErr: false,
		},
		{
			name: "PDB without status",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"apiVersion": "policy/v1",
					"kind":       "PodDisruptionBudget",
					"metadata": map[string]interface{}{
						"name":      "test-pdb",
						"namespace": "test-ns",
					},
				},
			},
			want:    nil,
			wantErr: false,
		},
		{
			name: "PDB with invalid status format",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"apiVersion": "policy/v1",
					"kind":       "PodDisruptionBudget",
					"metadata": map[string]interface{}{
						"name":      "test-pdb",
						"namespace": "test-ns",
					},
					"status": "invalid",
				},
			},
			want:    nil,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := reflectPodDisruptionBudgetStatus(tt.object)
			if tt.wantErr {
				assert.Error(t, err)
				return
			}
			assert.NoError(t, err)
			if tt.want == nil {
				assert.Nil(t, got)
			} else {
				assert.NotNil(t, got)
				assert.JSONEq(t, string(tt.want.Raw), string(got.Raw))
			}
		})
	}
}

func Test_reflectHorizontalPodAutoscalerStatus(t *testing.T) {
	tests := []struct {
		name    string
		object  *unstructured.Unstructured
		want    *runtime.RawExtension
		wantErr bool
	}{
		{
			name: "HPA with valid status",
			object: func() *unstructured.Unstructured {
				hpa := &autoscalingv2.HorizontalPodAutoscaler{
					ObjectMeta: metav1.ObjectMeta{
						Name:      "test-hpa",
						Namespace: "test-ns",
					},
					Status: autoscalingv2.HorizontalPodAutoscalerStatus{
						CurrentReplicas: 2,
						DesiredReplicas: 3,
					},
				}
				obj, _ := helper.ToUnstructured(hpa)
				return obj
			}(),
			want: func() *runtime.RawExtension {
				status := &autoscalingv2.HorizontalPodAutoscalerStatus{
					CurrentReplicas: 2,
					DesiredReplicas: 3,
				}
				raw, _ := helper.BuildStatusRawExtension(status)
				return raw
			}(),
			wantErr: false,
		},
		{
			name: "HPA without status",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"apiVersion": "autoscaling/v2",
					"kind":       "HorizontalPodAutoscaler",
					"metadata": map[string]interface{}{
						"name":      "test-hpa",
						"namespace": "test-ns",
					},
				},
			},
			want:    nil,
			wantErr: false,
		},
		{
			name: "HPA with invalid status format",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"apiVersion": "autoscaling/v2",
					"kind":       "HorizontalPodAutoscaler",
					"metadata": map[string]interface{}{
						"name":      "test-hpa",
						"namespace": "test-ns",
					},
					"status": "invalid",
				},
			},
			want:    nil,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := reflectHorizontalPodAutoscalerStatus(tt.object)
			if tt.wantErr {
				assert.Error(t, err)
				return
			}
			assert.NoError(t, err)
			if tt.want == nil {
				assert.Nil(t, got)
			} else {
				assert.NotNil(t, got)
				assert.JSONEq(t, string(tt.want.Raw), string(got.Raw))
			}
		})
	}
}

func Test_reflectWholeStatus(t *testing.T) {
	tests := []struct {
		name    string
		object  *unstructured.Unstructured
		want    *runtime.RawExtension
		wantErr bool
	}{
		{
			name: "object with valid status",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"status": map[string]interface{}{
						"key": "value",
						"num": int64(1),
					},
				},
			},
			want: func() *runtime.RawExtension {
				status := map[string]interface{}{
					"key": "value",
					"num": int64(1),
				}
				raw, _ := helper.BuildStatusRawExtension(status)
				return raw
			}(),
			wantErr: false,
		},
		{
			name: "object without status",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{},
			},
			want:    nil,
			wantErr: false,
		},
		{
			name: "object with invalid status format",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"status": "invalid",
				},
			},
			want:    nil,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := reflectWholeStatus(tt.object)
			if tt.wantErr {
				assert.Error(t, err)
				return
			}
			assert.NoError(t, err)
			if tt.want == nil {
				assert.Nil(t, got)
			} else {
				assert.NotNil(t, got)
				assert.JSONEq(t, string(tt.want.Raw), string(got.Raw))
			}
		})
	}
}

func Test_getAllDefaultReflectStatusInterpreter(t *testing.T) {
	tests := []struct {
		name   string
		gvk    schema.GroupVersionKind
		wantFn bool
	}{
		{
			name:   "Deployment interpreter exists",
			gvk:    appsv1.SchemeGroupVersion.WithKind(util.DeploymentKind),
			wantFn: true,
		},
		{
			name:   "Service interpreter exists",
			gvk:    corev1.SchemeGroupVersion.WithKind(util.ServiceKind),
			wantFn: true,
		},
		{
			name:   "Ingress interpreter exists",
			gvk:    networkingv1.SchemeGroupVersion.WithKind(util.IngressKind),
			wantFn: true,
		},
		{
			name:   "Job interpreter exists",
			gvk:    batchv1.SchemeGroupVersion.WithKind(util.JobKind),
			wantFn: true,
		},
		{
			name:   "DaemonSet interpreter exists",
			gvk:    appsv1.SchemeGroupVersion.WithKind(util.DaemonSetKind),
			wantFn: true,
		},
		{
			name:   "StatefulSet interpreter exists",
			gvk:    appsv1.SchemeGroupVersion.WithKind(util.StatefulSetKind),
			wantFn: true,
		},
		{
			name:   "PodDisruptionBudget interpreter exists",
			gvk:    policyv1.SchemeGroupVersion.WithKind(util.PodDisruptionBudgetKind),
			wantFn: true,
		},
		{
			name:   "HorizontalPodAutoscaler interpreter exists",
			gvk:    autoscalingv2.SchemeGroupVersion.WithKind(util.HorizontalPodAutoscalerKind),
			wantFn: true,
		},
		{
			name:   "Non-existent resource should not have interpreter",
			gvk:    schema.GroupVersionKind{Group: "fake", Version: "v1", Kind: "Fake"},
			wantFn: false,
		},
	}

	interpreters := getAllDefaultReflectStatusInterpreter()

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			interpreter, exists := interpreters[tt.gvk]
			assert.Equal(t, tt.wantFn, exists, "interpreter existence mismatch for %v", tt.gvk)
			if tt.wantFn {
				assert.NotNil(t, interpreter, "interpreter should not be nil for %v", tt.gvk)
				// Type checking
				assert.IsType(t, (reflectStatusInterpreter)(nil), interpreter,
					"interpreter should be of type reflectStatusInterpreter")
			}
		})
	}

	// Verify total number of interpreters
	assert.Len(t, interpreters, 8, "unexpected number of interpreters")

	// Verify map is not nil
	assert.NotNil(t, interpreters, "interpreters map should not be nil")
}

func Test_reflectDeploymentStatus(t *testing.T) {
	validDeployment := &appsv1.Deployment{
		ObjectMeta: metav1.ObjectMeta{
			Name:       "test-deployment",
			Namespace:  "test-ns",
			Generation: 2,
			Annotations: map[string]string{
				workv1alpha2.ResourceTemplateGenerationAnnotationKey: "1",
			},
		},
		Status: appsv1.DeploymentStatus{
			Replicas:            3,
			UpdatedReplicas:     3,
			ReadyReplicas:       3,
			AvailableReplicas:   3,
			UnavailableReplicas: 0,
			ObservedGeneration:  2,
		},
	}

	tests := []struct {
		name       string
		deployment *appsv1.Deployment
		modifyFunc func(*unstructured.Unstructured)
		want       *runtime.RawExtension
		wantErr    bool
	}{
		{
			name:       "deployment with valid status and generation annotation",
			deployment: validDeployment.DeepCopy(),
			want: func() *runtime.RawExtension {
				wantStatus := &WrappedDeploymentStatus{
					FederatedGeneration: FederatedGeneration{
						Generation:                 2,
						ResourceTemplateGeneration: 1,
					},
					DeploymentStatus: validDeployment.Status,
				}
				raw, _ := helper.BuildStatusRawExtension(wantStatus)
				return raw
			}(),
			wantErr: false,
		},
		{
			name:       "deployment without status field",
			deployment: validDeployment.DeepCopy(),
			modifyFunc: func(u *unstructured.Unstructured) {
				delete(u.Object, "status")
			},
			want:    nil,
			wantErr: false,
		},
		{
			name:       "deployment with invalid generation annotation",
			deployment: validDeployment.DeepCopy(),
			modifyFunc: func(u *unstructured.Unstructured) {
				annotations := u.GetAnnotations()
				annotations[workv1alpha2.ResourceTemplateGenerationAnnotationKey] = "invalid"
				u.SetAnnotations(annotations)
			},
			want:    nil,
			wantErr: true,
		},
		{
			name:       "deployment without generation annotation",
			deployment: validDeployment.DeepCopy(),
			modifyFunc: func(u *unstructured.Unstructured) {
				annotations := u.GetAnnotations()
				delete(annotations, workv1alpha2.ResourceTemplateGenerationAnnotationKey)
				u.SetAnnotations(annotations)
			},
			want:    nil,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Convert deployment to unstructured
			unstrObj, err := helper.ToUnstructured(tt.deployment)
			require.NoError(t, err, "Failed to convert deployment to unstructured")

			// Apply modifications if specified
			if tt.modifyFunc != nil {
				tt.modifyFunc(unstrObj)
			}

			got, err := reflectDeploymentStatus(unstrObj)

			if tt.wantErr {
				assert.Error(t, err)
				return
			}
			require.NoError(t, err)

			if tt.want == nil {
				assert.Nil(t, got)
				return
			}

			assert.NotNil(t, got)
			assert.JSONEq(t, string(tt.want.Raw), string(got.Raw))
		})
	}
}

func Test_reflectServiceStatus(t *testing.T) {
	tests := []struct {
		name    string
		object  *unstructured.Unstructured
		want    *runtime.RawExtension
		wantErr bool
	}{
		{
			name: "non-LoadBalancer service should return nil",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"spec": map[string]interface{}{
						"type": "ClusterIP",
					},
				},
			},
			want:    nil,
			wantErr: false,
		},
		{
			name: "LoadBalancer service without status should return nil",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"spec": map[string]interface{}{
						"type": "LoadBalancer",
					},
				},
			},
			want:    nil,
			wantErr: false,
		},
		{
			name: "LoadBalancer service with status should return status",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"spec": map[string]interface{}{
						"type": "LoadBalancer",
					},
					"status": map[string]interface{}{
						"loadBalancer": map[string]interface{}{
							"ingress": []interface{}{
								map[string]interface{}{
									"ip": "192.0.2.1",
								},
							},
						},
					},
				},
			},
			want: func() *runtime.RawExtension {
				status := corev1.ServiceStatus{
					LoadBalancer: corev1.LoadBalancerStatus{
						Ingress: []corev1.LoadBalancerIngress{
							{
								IP: "192.0.2.1",
							},
						},
					},
				}
				raw, _ := helper.BuildStatusRawExtension(status)
				return raw
			}(),
			wantErr: false,
		},
		{
			name: "invalid status format should return error",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"spec": map[string]interface{}{
						"type": "LoadBalancer",
					},
					"status": "invalid",
				},
			},
			want:    nil,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := reflectServiceStatus(tt.object)
			if tt.wantErr {
				assert.Error(t, err)
				return
			}
			assert.NoError(t, err)
			assert.Equal(t, tt.want, got)
		})
	}
}

func Test_reflectIngressStatus(t *testing.T) {
	testIngress := networkingv1.IngressStatus{
		LoadBalancer: networkingv1.IngressLoadBalancerStatus{
			Ingress: []networkingv1.IngressLoadBalancerIngress{
				{
					IP: "192.0.2.1",
				},
			},
		},
	}

	ingressStatusMap, _ := helper.ToUnstructured(&networkingv1.Ingress{Status: testIngress})
	wantRawExtension, _ := helper.BuildStatusRawExtension(testIngress)

	tests := []struct {
		name    string
		object  *unstructured.Unstructured
		want    *runtime.RawExtension
		wantErr bool
	}{
		{
			name:    "ingress with valid status",
			object:  ingressStatusMap,
			want:    wantRawExtension,
			wantErr: false,
		},
		{
			name: "ingress without status",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{},
			},
			want:    nil,
			wantErr: false,
		},
		{
			name: "ingress with invalid status",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"status": "invalid",
				},
			},
			want:    nil,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := reflectIngressStatus(tt.object)
			if tt.wantErr {
				assert.Error(t, err)
				return
			}
			assert.NoError(t, err)
			assert.Equal(t, tt.want, got)
		})
	}
}

func Test_reflectJobStatus(t *testing.T) {
	timeNow := metav1.Now()
	completionTime := timeNow.DeepCopy()
	startTime := timeNow.DeepCopy()
	jobStatus := batchv1.JobStatus{
		Active:         1,
		Succeeded:      2,
		Failed:         0,
		CompletionTime: completionTime,
		StartTime:      startTime,
		Conditions: []batchv1.JobCondition{
			{
				Type:    "Complete",
				Status:  "True",
				Reason:  "JobComplete",
				Message: "Job completed successfully",
			},
		},
	}

	tests := []struct {
		name    string
		object  *unstructured.Unstructured
		want    *runtime.RawExtension
		wantErr bool
	}{
		{
			name: "job with all status fields",
			object: func() *unstructured.Unstructured {
				obj, _ := helper.ToUnstructured(&batchv1.Job{Status: jobStatus})
				return obj
			}(),
			want: func() *runtime.RawExtension {
				raw, _ := helper.BuildStatusRawExtension(jobStatus)
				return raw
			}(),
			wantErr: false,
		},
		{
			name: "job without status",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{},
			},
			want:    nil,
			wantErr: false,
		},
		{
			name: "job with invalid status type",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"status": map[string]interface{}{
						"active": "invalid",
					},
				},
			},
			want:    nil,
			wantErr: true,
		},
		{
			name: "job with empty conditions",
			object: func() *unstructured.Unstructured {
				status := jobStatus.DeepCopy()
				status.Conditions = []batchv1.JobCondition{}
				obj, _ := helper.ToUnstructured(&batchv1.Job{Status: *status})
				return obj
			}(),
			want: func() *runtime.RawExtension {
				status := jobStatus.DeepCopy()
				status.Conditions = []batchv1.JobCondition{}
				raw, _ := helper.BuildStatusRawExtension(*status)
				return raw
			}(),
			wantErr: false,
		},
		{
			name: "job with failed status",
			object: func() *unstructured.Unstructured {
				status := jobStatus.DeepCopy()
				status.Failed = 1
				status.Active = 0
				status.Succeeded = 0
				status.Conditions = []batchv1.JobCondition{
					{
						Type:    "Failed",
						Status:  "True",
						Reason:  "JobFailed",
						Message: "Job failed due to error",
					},
				}
				obj, _ := helper.ToUnstructured(&batchv1.Job{Status: *status})
				return obj
			}(),
			want: func() *runtime.RawExtension {
				status := jobStatus.DeepCopy()
				status.Failed = 1
				status.Active = 0
				status.Succeeded = 0
				status.Conditions = []batchv1.JobCondition{
					{
						Type:    "Failed",
						Status:  "True",
						Reason:  "JobFailed",
						Message: "Job failed due to error",
					},
				}
				raw, _ := helper.BuildStatusRawExtension(*status)
				return raw
			}(),
			wantErr: false,
		},
		{
			name: "job with only active status",
			object: func() *unstructured.Unstructured {
				status := jobStatus.DeepCopy()
				status.Failed = 0
				status.Succeeded = 0
				status.Active = 1
				status.CompletionTime = nil
				status.Conditions = []batchv1.JobCondition{}
				obj, _ := helper.ToUnstructured(&batchv1.Job{Status: *status})
				return obj
			}(),
			want: func() *runtime.RawExtension {
				status := jobStatus.DeepCopy()
				status.Failed = 0
				status.Succeeded = 0
				status.Active = 1
				status.CompletionTime = nil
				status.Conditions = []batchv1.JobCondition{}
				raw, _ := helper.BuildStatusRawExtension(*status)
				return raw
			}(),
			wantErr: false,
		},
		{
			name: "job with invalid status field",
			object: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"status": "invalid",
				},
			},
			want:    nil,
			wantErr: true,
		},
		{
			name: "job with nil completion time",
			object: func() *unstructured.Unstructured {
				status := jobStatus.DeepCopy()
				status.CompletionTime = nil
				obj, _ := helper.ToUnstructured(&batchv1.Job{Status: *status})
				return obj
			}(),
			want: func() *runtime.RawExtension {
				status := jobStatus.DeepCopy()
				status.CompletionTime = nil
				raw, _ := helper.BuildStatusRawExtension(*status)
				return raw
			}(),
			wantErr: false,
		},
		{
			name: "job with nil start time",
			object: func() *unstructured.Unstructured {
				status := jobStatus.DeepCopy()
				status.StartTime = nil
				obj, _ := helper.ToUnstructured(&batchv1.Job{Status: *status})
				return obj
			}(),
			want: func() *runtime.RawExtension {
				status := jobStatus.DeepCopy()
				status.StartTime = nil
				raw, _ := helper.BuildStatusRawExtension(*status)
				return raw
			}(),
			wantErr: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := reflectJobStatus(tt.object)
			if tt.wantErr {
				assert.Error(t, err)
				return
			}
			assert.NoError(t, err)
			if tt.want == nil {
				assert.Nil(t, got)
			} else {
				assert.NotNil(t, got)
				assert.JSONEq(t, string(tt.want.Raw), string(got.Raw))
			}
		})
	}
}

func Test_reflectDaemonSetStatus(t *testing.T) {
	tests := []struct {
		name    string
		object  *unstructured.Unstructured
		want    *runtime.RawExtension
		wantErr bool
	}{
		{
			name: "daemonset with valid status and generation annotation",
			object: func() *unstructured.Unstructured {
				ds := &appsv1.DaemonSet{
					ObjectMeta: metav1.ObjectMeta{
						Name:       "test-daemonset",
						Namespace:  "test-ns",
						Generation: 2,
						Annotations: map[string]string{
							workv1alpha2.ResourceTemplateGenerationAnnotationKey: "1",
						},
					},
					Status: appsv1.DaemonSetStatus{
						CurrentNumberScheduled: 3,
						DesiredNumberScheduled: 3,
						NumberAvailable:        3,
						NumberMisscheduled:     0,
						NumberReady:            3,
						UpdatedNumberScheduled: 3,
						NumberUnavailable:      0,
						ObservedGeneration:     2,
					},
				}
				obj, _ := helper.ToUnstructured(ds)
				return obj
			}(),
			want: func() *runtime.RawExtension {
				status := &WrappedDaemonSetStatus{
					FederatedGeneration: FederatedGeneration{
						Generation:                 2,
						ResourceTemplateGeneration: 1,
					},
					DaemonSetStatus: appsv1.DaemonSetStatus{
						CurrentNumberScheduled: 3,
						DesiredNumberScheduled: 3,
						NumberAvailable:        3,
						NumberMisscheduled:     0,
						NumberReady:            3,
						UpdatedNumberScheduled: 3,
						NumberUnavailable:      0,
						ObservedGeneration:     2,
					},
				}
				raw, _ := helper.BuildStatusRawExtension(status)
				return raw
			}(),
			wantErr: false,
		},
		{
			name: "daemonset without status field",
			object: func() *unstructured.Unstructured {
				obj := &unstructured.Unstructured{
					Object: map[string]interface{}{
						"apiVersion": "apps/v1",
						"kind":       "DaemonSet",
						"metadata": map[string]interface{}{
							"name":       "test-daemonset",
							"namespace":  "test-ns",
							"generation": int64(1),
							"annotations": map[string]interface{}{
								workv1alpha2.ResourceTemplateGenerationAnnotationKey: "1",
							},
						},
					},
				}
				return obj
			}(),
			want:    nil,
			wantErr: false,
		},
		{
			name: "daemonset with invalid generation annotation",
			object: func() *unstructured.Unstructured {
				obj := &unstructured.Unstructured{
					Object: map[string]interface{}{
						"apiVersion": "apps/v1",
						"kind":       "DaemonSet",
						"metadata": map[string]interface{}{
							"name":       "test-daemonset",
							"namespace":  "test-ns",
							"generation": int64(1),
							"annotations": map[string]interface{}{
								workv1alpha2.ResourceTemplateGenerationAnnotationKey: "invalid",
							},
						},
						"status": map[string]interface{}{
							"currentNumberScheduled": int64(1),
							"numberReady":            int64(1),
						},
					},
				}
				return obj
			}(),
			want:    nil,
			wantErr: true,
		},
		{
			name: "daemonset with invalid status format",
			object: func() *unstructured.Unstructured {
				return &unstructured.Unstructured{
					Object: map[string]interface{}{
						"apiVersion": "apps/v1",
						"kind":       "DaemonSet",
						"metadata": map[string]interface{}{
							"name":      "test-daemonset",
							"namespace": "test-ns",
							"annotations": map[string]interface{}{
								workv1alpha2.ResourceTemplateGenerationAnnotationKey: "1",
							},
						},
						"status": "invalid",
					},
				}
			}(),
			want:    nil,
			wantErr: true,
		},
		{
			name: "daemonset without generation annotation",
			object: func() *unstructured.Unstructured {
				return &unstructured.Unstructured{
					Object: map[string]interface{}{
						"apiVersion": "apps/v1",
						"kind":       "DaemonSet",
						"metadata": map[string]interface{}{
							"name":      "test-daemonset",
							"namespace": "test-ns",
						},
						"status": map[string]interface{}{
							"currentNumberScheduled": int64(1),
						},
					},
				}
			}(),
			want:    nil,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := reflectDaemonSetStatus(tt.object)
			if tt.wantErr {
				assert.Error(t, err)
				return
			}

			assert.NoError(t, err)
			if tt.want == nil {
				assert.Nil(t, got)
			} else {
				assert.NotNil(t, got)
				assert.JSONEq(t, string(tt.want.Raw), string(got.Raw))
			}
		})
	}
}

func Test_reflectStatefulSetStatus(t *testing.T) {
	tests := []struct {
		name    string
		object  *unstructured.Unstructured
		want    *runtime.RawExtension
		wantErr bool
	}{
		{
			name: "statefulset with valid status and generation annotation",
			object: func() *unstructured.Unstructured {
				sts := &appsv1.StatefulSet{
					ObjectMeta: metav1.ObjectMeta{
						Name:       "test-statefulset",
						Namespace:  "test-ns",
						Generation: 2,
						Annotations: map[string]string{
							workv1alpha2.ResourceTemplateGenerationAnnotationKey: "1",
						},
					},
					Status: appsv1.StatefulSetStatus{
						Replicas:          3,
						ReadyReplicas:     3,
						CurrentReplicas:   3,
						UpdatedReplicas:   3,
						AvailableReplicas: 3,
					},
				}
				obj, _ := helper.ToUnstructured(sts)
				return obj
			}(),
			want: func() *runtime.RawExtension {
				status := &WrappedStatefulSetStatus{
					FederatedGeneration: FederatedGeneration{
						Generation:                 2,
						ResourceTemplateGeneration: 1,
					},
					StatefulSetStatus: appsv1.StatefulSetStatus{
						Replicas:          3,
						ReadyReplicas:     3,
						CurrentReplicas:   3,
						UpdatedReplicas:   3,
						AvailableReplicas: 3,
					},
				}
				raw, _ := helper.BuildStatusRawExtension(status)
				return raw
			}(),
			wantErr: false,
		},
		{
			name: "statefulset without status field",
			object: func() *unstructured.Unstructured {
				obj := &unstructured.Unstructured{
					Object: map[string]interface{}{
						"apiVersion": "apps/v1",
						"kind":       "StatefulSet",
						"metadata": map[string]interface{}{
							"name":       "test-statefulset",
							"namespace":  "test-ns",
							"generation": int64(1),
							"annotations": map[string]interface{}{
								workv1alpha2.ResourceTemplateGenerationAnnotationKey: "1",
							},
						},
					},
				}
				return obj
			}(),
			want:    nil,
			wantErr: false,
		},
		{
			name: "statefulset with invalid status format",
			object: func() *unstructured.Unstructured {
				return &unstructured.Unstructured{
					Object: map[string]interface{}{
						"apiVersion": "apps/v1",
						"kind":       "StatefulSet",
						"metadata": map[string]interface{}{
							"name":      "test-statefulset",
							"namespace": "test-ns",
							"annotations": map[string]interface{}{
								workv1alpha2.ResourceTemplateGenerationAnnotationKey: "1",
							},
						},
						"status": "invalid",
					},
				}
			}(),
			want:    nil,
			wantErr: true,
		},
		{
			name: "statefulset with partial status fields",
			object: func() *unstructured.Unstructured {
				sts := &appsv1.StatefulSet{
					ObjectMeta: metav1.ObjectMeta{
						Name:       "test-statefulset",
						Namespace:  "test-ns",
						Generation: 2,
						Annotations: map[string]string{
							workv1alpha2.ResourceTemplateGenerationAnnotationKey: "1",
						},
					},
					Status: appsv1.StatefulSetStatus{
						Replicas:        2,
						ReadyReplicas:   1,
						CurrentReplicas: 1,
						// UpdatedReplicas and AvailableReplicas missing
					},
				}
				obj, _ := helper.ToUnstructured(sts)
				return obj
			}(),
			want: func() *runtime.RawExtension {
				status := &WrappedStatefulSetStatus{
					FederatedGeneration: FederatedGeneration{
						Generation:                 2,
						ResourceTemplateGeneration: 1,
					},
					StatefulSetStatus: appsv1.StatefulSetStatus{
						Replicas:        2,
						ReadyReplicas:   1,
						CurrentReplicas: 1,
					},
				}
				raw, _ := helper.BuildStatusRawExtension(status)
				return raw
			}(),
			wantErr: false,
		},
		{
			name: "statefulset with no annotations",
			object: func() *unstructured.Unstructured {
				return &unstructured.Unstructured{
					Object: map[string]interface{}{
						"apiVersion": "apps/v1",
						"kind":       "StatefulSet",
						"metadata": map[string]interface{}{
							"name":      "test-statefulset",
							"namespace": "test-ns",
						},
						"status": map[string]interface{}{
							"replicas":        int64(1),
							"readyReplicas":   int64(1),
							"currentReplicas": int64(1),
						},
					},
				}
			}(),
			want:    nil,
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := reflectStatefulSetStatus(tt.object)
			if tt.wantErr {
				assert.Error(t, err)
				return
			}
			assert.NoError(t, err)
			if tt.want == nil {
				assert.Nil(t, got)
			} else {
				assert.NotNil(t, got)
				assert.JSONEq(t, string(tt.want.Raw), string(got.Raw))
			}
		})
	}
}
