package parent

import (
	"context"
	"errors"
	"testing"

	vmv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1"
	"github.com/go-logr/logr/testr"
	prometheusv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	observabilitypingcapcomv1 "github.com/tidbcloud/observability/api/regionalserver/resourceworker/v1"
	"github.com/tidbcloud/observability/pkg/common/configs"
	"github.com/tidbcloud/observability/pkg/common/constants"
	"github.com/tidbcloud/observability/pkg/common/structs"
	"github.com/tidbcloud/observability/pkg/resourceworker/controllers/serverless/task"
	k8sapiv1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func init() {
	// Initialize configs package to avoid panic in MultiTenancyRuleEnabled
	configs.Init(structs.ServiceInfo{
		BizTypeConfigs: map[string]structs.BizTypeConfig{
			"test-biz": {
				MultiTenancyRule: false,
			},
			"multi-tenant-biz": {
				MultiTenancyRule: true,
			},
		},
		MonitorSpec: map[string]structs.MonitorSpec{
			vmAlertDefaultSpecID: {
				Cpu:    "100m",
				Memory: "128Mi",
			},
			"custom-spec": {
				Cpu:    "100m",
				Memory: "128Mi",
			},
		},
	})
}

func TestNewVMAlert(t *testing.T) {
	ctx := &task.Context{
		Cluster: &observabilitypingcapcomv1.Cluster{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "test-cluster",
				Namespace: "test-namespace",
			},
		},
		Logger: testr.New(t),
	}

	vma := NewVMAlert(ctx)
	assert.NotNil(t, vma)
	assert.Equal(t, ctx, vma.Context)
}

func TestVMAlert_Name(t *testing.T) {
	ctx := &task.Context{
		Cluster: &observabilitypingcapcomv1.Cluster{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "test-cluster",
				Namespace: "test-namespace",
			},
		},
		Logger: testr.New(t),
	}

	vma := NewVMAlert(ctx)
	assert.Equal(t, "VMAlert", vma.Name())
}

func TestVMAlert_Create_NilRemote(t *testing.T) {
	ctx := &task.Context{
		Cluster: &observabilitypingcapcomv1.Cluster{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "test-cluster",
				Namespace: "test-namespace",
			},
		},
		Logger: testr.New(t),
		Remote: nil,
	}

	vma := NewVMAlert(ctx)
	result := vma.Create(context.Background())

	assert.False(t, result.IsDoneState())
	assert.NotNil(t, result.Error())
	assert.Contains(t, result.Error().Error(), "remote client is not initialized")
}

func TestVMAlert_Create_NilCluster(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	ctx := &task.Context{
		Cluster: nil,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).Build(),
	}

	vma := NewVMAlert(ctx)
	result := vma.Create(context.Background())

	assert.False(t, result.IsDoneState())
	assert.NotNil(t, result.Error())
	assert.Contains(t, result.Error().Error(), "cluster context is nil")
}

func TestVMAlert_Create_MuteAlert(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			MuteAlert: true,
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).Build(),
	}

	vma := NewVMAlert(ctx)
	result := vma.Create(context.Background())

	assert.True(t, result.IsDoneState())
	assert.Nil(t, result.Error())

	// Verify no VMAlert was created
	alert := &vmv1beta1.VMAlert{}
	err := ctx.Remote.Get(context.Background(), client.ObjectKey{
		Namespace: cluster.Namespace,
		Name:      cluster.Name,
	}, alert)
	assert.True(t, apierrors.IsNotFound(err))
}

func TestVMAlert_Create_Success(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)
	_ = k8sapiv1.AddToScheme(scheme)

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Labels: map[string]string{
				constants.LabelBizType:       "test-biz",
				constants.LabelRegion:        "us-west-2",
				constants.LabelTenantID:      "tenant-1",
				constants.LabelProjectID:     "project-1",
				constants.LabelApplicationID: constants.ApplicationIDBase, // Use base to avoid multi-tenancy check
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			VictoriaAccount: observabilitypingcapcomv1.VictoriaAccount{
				VMClusterName:    "test-vm-cluster",
				AccountID:        12345,
				ProjectID:        67890,
				AvailabilityZone: "us-west-2a",
				AlertResources: k8sapiv1.ResourceRequirements{
					Requests: k8sapiv1.ResourceList{
						k8sapiv1.ResourceCPU:    resource.MustParse("100m"),
						k8sapiv1.ResourceMemory: resource.MustParse("128Mi"),
					},
				},
			},
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).Build(),
		Config: structs.ServiceInfo{
			MonitorSpec: map[string]structs.MonitorSpec{
				vmAlertDefaultSpecID: {
					Cpu:    "100m",
					Memory: "128Mi",
				},
			},
		},
	}

	vma := NewVMAlert(ctx)
	result := vma.Create(context.Background())

	assert.True(t, result.IsDoneState())
	assert.Nil(t, result.Error())

	// Verify VMAlert was created
	alert := &vmv1beta1.VMAlert{}
	err := ctx.Remote.Get(context.Background(), client.ObjectKey{
		Namespace: cluster.Namespace,
		Name:      cluster.Name,
	}, alert)
	require.NoError(t, err)
	assert.Equal(t, cluster.Name, alert.Name)
	assert.Equal(t, cluster.Namespace, alert.Namespace)
	assert.NotNil(t, alert.Spec.Datasource)
	assert.NotNil(t, alert.Spec.RemoteRead)
	assert.NotNil(t, alert.Spec.RemoteWrite)
}

func TestVMAlert_Create_WithAlerting(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)
	_ = k8sapiv1.AddToScheme(scheme)

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Labels: map[string]string{
				constants.LabelBizType:       "test-biz",
				constants.LabelApplicationID: constants.ApplicationIDBase, // Use base to avoid multi-tenancy check
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			VictoriaAccount: observabilitypingcapcomv1.VictoriaAccount{
				VMClusterName:    "test-vm-cluster",
				AccountID:        12345,
				ProjectID:        67890,
				AvailabilityZone: "us-west-2a",
				Alerting: &prometheusv1.AlertingSpec{
					Alertmanagers: []prometheusv1.AlertmanagerEndpoints{
						{
							Name:      "alertmanager",
							Namespace: "monitoring",
						},
					},
				},
			},
		},
	}

	// Create AlertManager service
	amService := &k8sapiv1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "alertmanager",
			Namespace: "monitoring",
		},
		Spec: k8sapiv1.ServiceSpec{
			Selector: map[string]string{
				"app": "alertmanager",
			},
			Ports: []k8sapiv1.ServicePort{
				{
					Name: "alertmanager",
					Port: 9093,
				},
			},
		},
	}

	// Create AlertManager pods
	amPod := &k8sapiv1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "alertmanager-0",
			Namespace: "monitoring",
			Labels: map[string]string{
				"app": "alertmanager",
			},
		},
		Status: k8sapiv1.PodStatus{
			Phase: k8sapiv1.PodRunning,
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).WithObjects(amService, amPod).Build(),
		Config: structs.ServiceInfo{
			MonitorSpec: map[string]structs.MonitorSpec{
				vmAlertDefaultSpecID: {},
			},
		},
	}

	vma := NewVMAlert(ctx)
	result := vma.Create(context.Background())

	assert.True(t, result.IsDoneState())
	assert.Nil(t, result.Error())

	// Verify VMAlert was created with notifiers
	alert := &vmv1beta1.VMAlert{}
	err := ctx.Remote.Get(context.Background(), client.ObjectKey{
		Namespace: cluster.Namespace,
		Name:      cluster.Name,
	}, alert)
	require.NoError(t, err)
	assert.NotEmpty(t, alert.Spec.Notifiers)
}

func TestVMAlert_Delete_NilRemote(t *testing.T) {
	ctx := &task.Context{
		Cluster: &observabilitypingcapcomv1.Cluster{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "test-cluster",
				Namespace: "test-namespace",
			},
		},
		Logger: testr.New(t),
		Remote: nil,
	}

	vma := NewVMAlert(ctx)
	result := vma.Delete(context.Background())

	assert.False(t, result.IsDoneState())
	assert.NotNil(t, result.Error())
	assert.Contains(t, result.Error().Error(), "remote client is not initialized")
}

func TestVMAlert_Delete_NilCluster(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	ctx := &task.Context{
		Cluster: nil,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).Build(),
	}

	vma := NewVMAlert(ctx)
	result := vma.Delete(context.Background())

	assert.False(t, result.IsDoneState())
	assert.NotNil(t, result.Error())
	assert.Contains(t, result.Error().Error(), "cluster context is nil")
}

func TestVMAlert_Delete_Success(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
		},
	}

	// Create a VMAlert to delete
	alert := &vmv1beta1.VMAlert{
		ObjectMeta: metav1.ObjectMeta{
			Name:      cluster.Name,
			Namespace: cluster.Namespace,
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).WithObjects(alert).Build(),
	}

	vma := NewVMAlert(ctx)
	result := vma.Delete(context.Background())

	assert.True(t, result.IsDoneState())
	assert.Nil(t, result.Error())

	// Verify VMAlert was deleted
	deleted := &vmv1beta1.VMAlert{}
	err := ctx.Remote.Get(context.Background(), client.ObjectKey{
		Namespace: cluster.Namespace,
		Name:      cluster.Name,
	}, deleted)
	assert.True(t, apierrors.IsNotFound(err))
}

func TestVMAlert_Delete_NotFound(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).Build(),
	}

	vma := NewVMAlert(ctx)
	result := vma.Delete(context.Background())

	assert.True(t, result.IsDoneState())
	assert.Nil(t, result.Error())
}

func TestVMAlert_VMAlertNeeded_MuteAlert(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			MuteAlert: true,
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).Build(),
	}

	vma := NewVMAlert(ctx)
	needed, err := vma.vmAlertNeeded(context.Background(), cluster)

	assert.NoError(t, err)
	assert.False(t, needed)
}

func TestVMAlert_VMAlertNeeded_NoMultiTenancy(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Labels: map[string]string{
				constants.LabelBizType:       "test-biz",
				constants.LabelApplicationID: constants.ApplicationIDBase, // Use base to avoid multi-tenancy check
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			MuteAlert: false,
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).Build(),
	}

	vma := NewVMAlert(ctx)
	needed, err := vma.vmAlertNeeded(context.Background(), cluster)

	assert.NoError(t, err)
	assert.True(t, needed)
}

func TestVMAlert_VMAlertNeeded_MultiTenancy_NoVMRule(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Labels: map[string]string{
				constants.LabelBizType:       "multi-tenant-biz",
				constants.LabelApplicationID: "custom-app", // Not special ID, will check multi-tenancy
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			MuteAlert: false,
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).Build(),
	}

	vma := NewVMAlert(ctx)
	needed, err := vma.vmAlertNeeded(context.Background(), cluster)

	assert.NoError(t, err)
	assert.True(t, needed) // VMRule not found, so VMAlert is needed
}

func TestVMAlert_VMAlertNeeded_MultiTenancy_WithVMRule_NotMultiTenant(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Labels: map[string]string{
				constants.LabelBizType:       "multi-tenant-biz",
				constants.LabelApplicationID: "custom-app",
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			MuteAlert: false,
		},
	}

	// Create VMRule without multi-tenancy label
	vmRule := &vmv1beta1.VMRule{
		ObjectMeta: metav1.ObjectMeta{
			Name:      cluster.Name,
			Namespace: cluster.Namespace,
			Labels: map[string]string{
				"app": "test",
			},
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).WithObjects(vmRule).Build(),
	}

	vma := NewVMAlert(ctx)
	needed, err := vma.vmAlertNeeded(context.Background(), cluster)

	assert.NoError(t, err)
	assert.True(t, needed) // VMRule exists but not marked as multi-tenant
}

func TestVMAlert_VMAlertNeeded_MultiTenancy_WithVMRule_MultiTenant(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Labels: map[string]string{
				constants.LabelBizType:       "multi-tenant-biz",
				constants.LabelApplicationID: "custom-app",
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			MuteAlert: false,
		},
	}

	// Create VMRule with multi-tenancy label
	vmRule := &vmv1beta1.VMRule{
		ObjectMeta: metav1.ObjectMeta{
			Name:      cluster.Name,
			Namespace: cluster.Namespace,
			Labels: map[string]string{
				constants.LabelMultiTenancyRule: "true",
			},
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).WithObjects(vmRule).Build(),
	}

	vma := NewVMAlert(ctx)
	needed, err := vma.vmAlertNeeded(context.Background(), cluster)

	assert.NoError(t, err)
	assert.False(t, needed) // VMRule exists with multi-tenant label, VMAlert not needed
}

func TestVMAlert_VMAlertNeeded_GetError(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Labels: map[string]string{
				constants.LabelBizType:       "multi-tenant-biz",
				constants.LabelApplicationID: "custom-app",
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			MuteAlert: false,
		},
	}

	mockClient := &mockVMAlertErrorClient{
		getError: errors.New("get error"),
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  mockClient,
	}

	vma := NewVMAlert(ctx)
	needed, err := vma.vmAlertNeeded(context.Background(), cluster)

	assert.Error(t, err)
	assert.Contains(t, err.Error(), "get error")
	assert.False(t, needed)
}

func TestVMAlert_MakeVMAlert(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)
	_ = k8sapiv1.AddToScheme(scheme)

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Labels: map[string]string{
				constants.LabelBizType:       "test-biz",
				constants.LabelRegion:        "us-west-2",
				constants.LabelVendor:        "aws",
				constants.LabelTenantID:      "tenant-1",
				constants.LabelProjectID:     "project-1",
				constants.LabelApplicationID: constants.ApplicationIDBase,
			},
			Annotations: map[string]string{
				constants.AnnotationMonitorSpec: "custom-spec",
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			VictoriaAccount: observabilitypingcapcomv1.VictoriaAccount{
				VMClusterName:    "test-vm-cluster",
				AccountID:        12345,
				ProjectID:        67890,
				AvailabilityZone: "us-west-2a",
				RuleSelector: &metav1.LabelSelector{
					MatchLabels: map[string]string{
						"app": "test",
					},
				},
				AlertResources: k8sapiv1.ResourceRequirements{
					Requests: k8sapiv1.ResourceList{
						k8sapiv1.ResourceCPU:    resource.MustParse("200m"),
						k8sapiv1.ResourceMemory: resource.MustParse("256Mi"),
					},
					Limits: k8sapiv1.ResourceList{
						k8sapiv1.ResourceCPU:    resource.MustParse("500m"),
						k8sapiv1.ResourceMemory: resource.MustParse("512Mi"),
					},
				},
			},
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).Build(),
		Config: structs.ServiceInfo{
			MonitorSpec: map[string]structs.MonitorSpec{
				"custom-spec": {
					Cpu:    "100m",
					Memory: "128Mi",
				},
			},
		},
	}

	vma := NewVMAlert(ctx)
	obj, err := vma.makeVMAlert(context.Background(), cluster)

	require.NoError(t, err)
	require.NotNil(t, obj)

	alert, ok := obj.(*vmv1beta1.VMAlert)
	require.True(t, ok)

	assert.Equal(t, cluster.Name, alert.Name)
	assert.Equal(t, cluster.Namespace, alert.Namespace)
	assert.Equal(t, cluster.Labels, alert.Labels)
	assert.Equal(t, cluster.Annotations, alert.Annotations)

	// Check datasource
	assert.Contains(t, alert.Spec.Datasource.URL, "vmselect-")
	assert.Contains(t, alert.Spec.Datasource.URL, cluster.Spec.VictoriaAccount.VMClusterName)

	// Check remote read
	assert.NotNil(t, alert.Spec.RemoteRead)
	assert.Contains(t, alert.Spec.RemoteRead.URL, "vmselect-")

	// Check remote write
	assert.NotNil(t, alert.Spec.RemoteWrite)
	assert.Contains(t, alert.Spec.RemoteWrite.URL, "vminsert-")
	assert.Equal(t, int32(5000), *alert.Spec.RemoteWrite.MaxBatchSize)

	// Check resources
	assert.Equal(t, "200m", alert.Spec.Resources.Requests.Cpu().String())
	assert.Equal(t, "256Mi", alert.Spec.Resources.Requests.Memory().String())
	assert.Equal(t, "500m", alert.Spec.Resources.Limits.Cpu().String())
	assert.Equal(t, "512Mi", alert.Spec.Resources.Limits.Memory().String())

	// Check external labels
	assert.Equal(t, "test-biz", alert.Spec.ExternalLabels[constants.ExternalLabelBizType])
	assert.Equal(t, "aws-us-west-2", alert.Spec.ExternalLabels[constants.ExternalLabelRegion])
	assert.Equal(t, "tenant-1", alert.Spec.ExternalLabels[constants.ExternalLabelTenantID])
	assert.Equal(t, cluster.Name, alert.Spec.ExternalLabels[constants.ExternalLabelO11yID])

	// Check rule selector
	assert.NotNil(t, alert.Spec.RuleSelector)
	assert.Equal(t, "test", alert.Spec.RuleSelector.MatchLabels["app"])

	// Check node selector
	assert.Equal(t, "worker", alert.Spec.NodeSelector["node_type"])
	assert.Equal(t, "us-west-2a", alert.Spec.NodeSelector["topology.kubernetes.io/zone"])

	// Check containers
	assert.Len(t, alert.Spec.Containers, 1)
	assert.Equal(t, "config-reloader", alert.Spec.Containers[0].Name)
}

func TestVMAlert_MakeVMAlert_WithMaxQueueSize(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	maxQueueSize := int32(2000)
	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Labels: map[string]string{
				constants.LabelBizType:       "test-biz",
				constants.LabelApplicationID: constants.ApplicationIDBase,
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			VictoriaAccount: observabilitypingcapcomv1.VictoriaAccount{
				VMClusterName:    "test-vm-cluster",
				AccountID:        12345,
				ProjectID:        67890,
				AvailabilityZone: "us-west-2a",
				MaxQueueSize:     &maxQueueSize,
			},
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).Build(),
		Config: structs.ServiceInfo{
			MonitorSpec: map[string]structs.MonitorSpec{
				vmAlertDefaultSpecID: {},
			},
		},
	}

	vma := NewVMAlert(ctx)
	obj, err := vma.makeVMAlert(context.Background(), cluster)

	require.NoError(t, err)
	alert := obj.(*vmv1beta1.VMAlert)
	assert.Equal(t, int32(2000), *alert.Spec.RemoteWrite.MaxQueueSize)
}

func TestVMAlert_BuildNotifiers_NoAlerting(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	ctx := &task.Context{
		Logger: testr.New(t),
		Remote: fake.NewClientBuilder().WithScheme(scheme).Build(),
	}

	vma := NewVMAlert(ctx)
	notifiers, err := vma.buildNotifiers(context.Background(), nil)

	assert.NoError(t, err)
	assert.Nil(t, notifiers)
}

func TestVMAlert_BuildNotifiers_EmptyAlertmanagers(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

	ctx := &task.Context{
		Logger: testr.New(t),
		Remote: fake.NewClientBuilder().WithScheme(scheme).Build(),
	}

	vma := NewVMAlert(ctx)
	alerting := &prometheusv1.AlertingSpec{
		Alertmanagers: []prometheusv1.AlertmanagerEndpoints{},
	}
	notifiers, err := vma.buildNotifiers(context.Background(), alerting)

	assert.NoError(t, err)
	assert.Nil(t, notifiers)
}

func TestVMAlert_BuildNotifiers_ServiceNotFound(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)
	_ = k8sapiv1.AddToScheme(scheme)

	ctx := &task.Context{
		Logger: testr.New(t),
		Remote: fake.NewClientBuilder().WithScheme(scheme).Build(),
	}

	vma := NewVMAlert(ctx)
	alerting := &prometheusv1.AlertingSpec{
		Alertmanagers: []prometheusv1.AlertmanagerEndpoints{
			{
				Name:      "alertmanager",
				Namespace: "monitoring",
			},
		},
	}
	notifiers, err := vma.buildNotifiers(context.Background(), alerting)

	assert.NoError(t, err)
	assert.Len(t, notifiers, 1)
	assert.Contains(t, notifiers[0].URL, "alertmanager.monitoring.svc.cluster.local")
}

func TestVMAlert_BuildNotifiers_NoPods(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)
	_ = k8sapiv1.AddToScheme(scheme)

	amService := &k8sapiv1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "alertmanager",
			Namespace: "monitoring",
		},
		Spec: k8sapiv1.ServiceSpec{
			Selector: map[string]string{
				"app": "alertmanager",
			},
		},
	}

	ctx := &task.Context{
		Logger: testr.New(t),
		Remote: fake.NewClientBuilder().WithScheme(scheme).WithObjects(amService).Build(),
	}

	vma := NewVMAlert(ctx)
	alerting := &prometheusv1.AlertingSpec{
		Alertmanagers: []prometheusv1.AlertmanagerEndpoints{
			{
				Name:      "alertmanager",
				Namespace: "monitoring",
			},
		},
	}
	notifiers, err := vma.buildNotifiers(context.Background(), alerting)

	assert.NoError(t, err)
	assert.Len(t, notifiers, 1)
	assert.Contains(t, notifiers[0].URL, "alertmanager.monitoring.svc.cluster.local")
}

func TestVMAlert_BuildNotifiers_WithPods(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = vmv1beta1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)
	_ = k8sapiv1.AddToScheme(scheme)

	amService := &k8sapiv1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "alertmanager",
			Namespace: "monitoring",
		},
		Spec: k8sapiv1.ServiceSpec{
			Selector: map[string]string{
				"app": "alertmanager",
			},
			Ports: []k8sapiv1.ServicePort{
				{
					Name: "alertmanager",
					Port: 9093,
				},
			},
		},
	}

	pod1 := &k8sapiv1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "alertmanager-0",
			Namespace: "monitoring",
			Labels: map[string]string{
				"app": "alertmanager",
			},
		},
	}

	pod2 := &k8sapiv1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "alertmanager-1",
			Namespace: "monitoring",
			Labels: map[string]string{
				"app": "alertmanager",
			},
		},
	}

	ctx := &task.Context{
		Logger: testr.New(t),
		Remote: fake.NewClientBuilder().WithScheme(scheme).WithObjects(amService, pod1, pod2).Build(),
	}

	vma := NewVMAlert(ctx)
	alerting := &prometheusv1.AlertingSpec{
		Alertmanagers: []prometheusv1.AlertmanagerEndpoints{
			{
				Name:      "alertmanager",
				Namespace: "monitoring",
			},
		},
	}
	notifiers, err := vma.buildNotifiers(context.Background(), alerting)

	assert.NoError(t, err)
	assert.Len(t, notifiers, 2)
	assert.Contains(t, notifiers[0].URL, "alertmanager-0.alertmanager.monitoring.svc.cluster.local")
	assert.Contains(t, notifiers[1].URL, "alertmanager-1.alertmanager.monitoring.svc.cluster.local")
	// Verify sorted
	assert.True(t, notifiers[0].URL < notifiers[1].URL)
}

func TestOverrideContainer(t *testing.T) {
	container := overrideContainer("test-container")

	assert.Equal(t, "test-container", container.Name)
	assert.Equal(t, "10m", container.Resources.Requests.Cpu().String())
	assert.Equal(t, "10m", container.Resources.Limits.Cpu().String())
}

// Mock client for error scenarios
type mockVMAlertErrorClient struct {
	client.Client
	getError    error
	deleteError error
	listError   error
	scheme      *runtime.Scheme
}

func (m *mockVMAlertErrorClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
	if m.getError != nil {
		return m.getError
	}
	switch obj.(type) {
	case *vmv1beta1.VMAlert:
		if alert, ok := obj.(*vmv1beta1.VMAlert); ok {
			alert.SetName(key.Name)
			alert.SetNamespace(key.Namespace)
			return nil
		}
	case *k8sapiv1.Service:
		return apierrors.NewNotFound(schema.GroupResource{}, key.Name)
	}
	return apierrors.NewNotFound(schema.GroupResource{}, key.Name)
}

func (m *mockVMAlertErrorClient) Delete(ctx context.Context, obj client.Object, opts ...client.DeleteOption) error {
	if m.deleteError != nil {
		return m.deleteError
	}
	return nil
}

func (m *mockVMAlertErrorClient) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error {
	if m.listError != nil {
		return m.listError
	}
	return nil
}

func (m *mockVMAlertErrorClient) Scheme() *runtime.Scheme {
	if m.scheme == nil {
		m.scheme = runtime.NewScheme()
		_ = vmv1beta1.AddToScheme(m.scheme)
		_ = observabilitypingcapcomv1.AddToScheme(m.scheme)
		_ = k8sapiv1.AddToScheme(m.scheme)
	}
	return m.scheme
}

func TestVMAlert_EnsureVMAlertDeleted_DeleteError(t *testing.T) {
	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
		},
	}

	mockClient := &mockVMAlertErrorClient{
		getError:    nil,
		deleteError: errors.New("delete error"),
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  mockClient,
	}

	vma := NewVMAlert(ctx)
	err := vma.ensureVMAlertDeleted(context.Background())

	assert.Error(t, err)
	assert.Contains(t, err.Error(), "delete error")
}

func TestVMAlert_EnsureVMAlertDeleted_GetError(t *testing.T) {
	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
		},
	}

	mockClient := &mockVMAlertErrorClient{
		getError: errors.New("get error"),
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  mockClient,
	}

	vma := NewVMAlert(ctx)
	err := vma.ensureVMAlertDeleted(context.Background())

	assert.Error(t, err)
	assert.Contains(t, err.Error(), "get error")
}

func TestVMAlert_BuildNotifiers_ListError(t *testing.T) {
	amService := &k8sapiv1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "alertmanager",
			Namespace: "monitoring",
		},
		Spec: k8sapiv1.ServiceSpec{
			Selector: map[string]string{
				"app": "alertmanager",
			},
		},
	}

	mockClient := &mockVMAlertErrorClient{
		getError:  nil,
		listError: errors.New("list error"),
	}
	// Need to set up the Get to return the service
	scheme := runtime.NewScheme()
	_ = k8sapiv1.AddToScheme(scheme)
	realClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(amService).Build()

	// Create a wrapper that uses real client for Get but mock for List
	type compositeClient struct {
		client.Client
		mockList *mockVMAlertErrorClient
	}

	composite := &compositeClient{
		Client:   realClient,
		mockList: mockClient,
	}

	ctx := &task.Context{
		Logger: testr.New(t),
		Remote: composite.mockList,
	}

	// Get the service first
	svc := &k8sapiv1.Service{}
	_ = realClient.Get(context.Background(), client.ObjectKey{Name: "alertmanager", Namespace: "monitoring"}, svc)

	// Now manually create the context with proper setup
	ctx.Remote = mockClient

	vma := NewVMAlert(ctx)
	alerting := &prometheusv1.AlertingSpec{
		Alertmanagers: []prometheusv1.AlertmanagerEndpoints{
			{
				Name:      "alertmanager",
				Namespace: "monitoring",
			},
		},
	}

	// This should return error from List
	_, err := vma.buildNotifiers(context.Background(), alerting)

	// Since Get returns NotFound, it will use default notifier
	assert.NoError(t, err)
}
