package parent

import (
	"context"
	"errors"
	"testing"

	"github.com/go-logr/logr/testr"
	"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/constants"
	"github.com/tidbcloud/observability/pkg/common/structs"
	"github.com/tidbcloud/observability/pkg/resourceworker/controllers/serverless/task"
	k8snetworkv1 "k8s.io/api/networking/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	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 TestNewIngress(t *testing.T) {
	ctx := &task.Context{
		Cluster: &observabilitypingcapcomv1.Cluster{
			ObjectMeta: metav1.ObjectMeta{
				Name:      "test-cluster",
				Namespace: "test-namespace",
			},
		},
		Logger: testr.New(t),
	}

	ing := NewIngress(ctx)
	assert.NotNil(t, ing)
	assert.Equal(t, ctx, ing.Context)
}

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

	ing := NewIngress(ctx)
	assert.Equal(t, "Ingress", ing.Name())
}

func TestIngress_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,
	}

	ing := NewIngress(ctx)
	result := ing.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 TestIngress_Create_NilCluster(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = k8snetworkv1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

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

	ing := NewIngress(ctx)
	result := ing.Create(context.Background())

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

func TestIngress_Create_Success(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = k8snetworkv1.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.LabelTenantID:      "tenant-1",
				constants.LabelProjectID:     "project-1",
				constants.LabelApplicationID: "app-1",
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			Endpoint: observabilitypingcapcomv1.Endpoint{
				Name: "test-endpoint",
			},
			VictoriaAccount: observabilitypingcapcomv1.VictoriaAccount{
				AvailabilityZone: "us-west-2a",
				VMClusterName:    "test-vm-cluster",
				AccountID:        12345,
				ProjectID:        67890,
			},
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).Build(),
		Config: structs.ServiceInfo{
			AZConfig: map[string]structs.AvailabilityZoneConfig{
				"us-west-2a": {
					IngressClassName:     "nginx",
					ReadIngressClassName: "nginx-read",
				},
			},
			BizTypeConfigs: map[string]structs.BizTypeConfig{
				"test-biz": {
					ExternalApiAuthUrlTemplate: "https://auth.example.com/tenant/%s",
				},
			},
		},
	}

	ing := NewIngress(ctx)
	result := ing.Create(context.Background())

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

	// Verify VMSelect read ingress was created
	vmSelectIngress := &k8snetworkv1.Ingress{}
	err := ctx.Remote.Get(context.Background(), client.ObjectKey{
		Namespace: cluster.Namespace,
		Name:      svcPrefix + cluster.Spec.Endpoint.Name + readSuffix,
	}, vmSelectIngress)
	require.NoError(t, err)
	assert.Equal(t, "nginx-read", *vmSelectIngress.Spec.IngressClassName)

	// Verify VMSelect open read ingress was created
	vmOpenSelectIngress := &k8snetworkv1.Ingress{}
	err = ctx.Remote.Get(context.Background(), client.ObjectKey{
		Namespace: cluster.Namespace,
		Name:      svcPrefix + cluster.Spec.Endpoint.Name + openReadSuffix,
	}, vmOpenSelectIngress)
	require.NoError(t, err)
	assert.Equal(t, "nginx-read", *vmOpenSelectIngress.Spec.IngressClassName)

	// Verify VMInsert ingress was created
	vmInsertIngress := &k8snetworkv1.Ingress{}
	err = ctx.Remote.Get(context.Background(), client.ObjectKey{
		Namespace: cluster.Namespace,
		Name:      svcPrefix + cluster.Spec.Endpoint.Name,
	}, vmInsertIngress)
	require.NoError(t, err)
	assert.Equal(t, "nginx", *vmInsertIngress.Spec.IngressClassName)

	// Verify serverless insert ingress was created
	serverlessIngress := &k8snetworkv1.Ingress{}
	err = ctx.Remote.Get(context.Background(), client.ObjectKey{
		Namespace: cluster.Namespace,
		Name:      serverlessPrefix + cluster.Spec.Endpoint.Name,
	}, serverlessIngress)
	require.NoError(t, err)
	assert.Equal(t, "nginx", *serverlessIngress.Spec.IngressClassName)
}

func TestIngress_Create_MissingAZConfig(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = k8snetworkv1.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",
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			Endpoint: observabilitypingcapcomv1.Endpoint{
				Name: "test-endpoint",
			},
			VictoriaAccount: observabilitypingcapcomv1.VictoriaAccount{
				AvailabilityZone: "unknown-az",
			},
		},
	}

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

	ing := NewIngress(ctx)
	result := ing.Create(context.Background())

	assert.False(t, result.IsDoneState())
	assert.NotNil(t, result.Error())
	assert.Contains(t, result.Error().Error(), "not found in config")
}

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

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Labels: map[string]string{
				constants.LabelBizType: "unknown-biz",
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			Endpoint: observabilitypingcapcomv1.Endpoint{
				Name: "test-endpoint",
			},
			VictoriaAccount: observabilitypingcapcomv1.VictoriaAccount{
				AvailabilityZone: "us-west-2a",
			},
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Remote:  fake.NewClientBuilder().WithScheme(scheme).Build(),
		Config: structs.ServiceInfo{
			AZConfig: map[string]structs.AvailabilityZoneConfig{
				"us-west-2a": {
					IngressClassName:     "nginx",
					ReadIngressClassName: "nginx-read",
				},
			},
			BizTypeConfigs: map[string]structs.BizTypeConfig{},
		},
	}

	ing := NewIngress(ctx)
	result := ing.Create(context.Background())

	assert.False(t, result.IsDoneState())
	assert.NotNil(t, result.Error())
	assert.Contains(t, result.Error().Error(), "biz type")
}

func TestIngress_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,
	}

	ing := NewIngress(ctx)
	result := ing.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 TestIngress_Delete_NilCluster(t *testing.T) {
	scheme := runtime.NewScheme()
	_ = k8snetworkv1.AddToScheme(scheme)
	_ = observabilitypingcapcomv1.AddToScheme(scheme)

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

	ing := NewIngress(ctx)
	result := ing.Delete(context.Background())

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

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

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Annotations: map[string]string{
				constants.AnnotationShareMode: "dedicated",
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			Endpoint: observabilitypingcapcomv1.Endpoint{
				Name: "test-endpoint",
			},
		},
	}

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

	ing := NewIngress(ctx)
	result := ing.Delete(context.Background())

	// Should return Done immediately for non-shared mode
	assert.True(t, result.IsDoneState())
	assert.Nil(t, result.Error())
}

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

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Annotations: map[string]string{
				constants.AnnotationShareMode: string(constants.SHARED),
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			Endpoint: observabilitypingcapcomv1.Endpoint{
				Name: "test-endpoint",
			},
		},
	}

	// Create ingresses to delete
	ingress1 := &k8snetworkv1.Ingress{
		ObjectMeta: metav1.ObjectMeta{
			Name:      svcPrefix + cluster.Spec.Endpoint.Name + readSuffix,
			Namespace: cluster.Namespace,
		},
	}

	ingress2 := &k8snetworkv1.Ingress{
		ObjectMeta: metav1.ObjectMeta{
			Name:      svcPrefix + cluster.Spec.Endpoint.Name + openReadSuffix,
			Namespace: cluster.Namespace,
		},
	}

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

	ing := NewIngress(ctx)
	result := ing.Delete(context.Background())

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

	// Verify ingresses were deleted
	deleted := &k8snetworkv1.Ingress{}
	err := ctx.Remote.Get(context.Background(), client.ObjectKey{
		Namespace: cluster.Namespace,
		Name:      svcPrefix + cluster.Spec.Endpoint.Name + readSuffix,
	}, deleted)
	assert.True(t, apierrors.IsNotFound(err))
}

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

	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Annotations: map[string]string{
				constants.AnnotationShareMode: string(constants.SHARED),
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			Endpoint: observabilitypingcapcomv1.Endpoint{
				Name: "test-endpoint",
			},
		},
	}

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

	ing := NewIngress(ctx)
	result := ing.Delete(context.Background())

	// Should succeed even when ingresses don't exist
	assert.True(t, result.IsDoneState())
	assert.Nil(t, result.Error())
}

func TestIngress_MakeVMSelectIngress(t *testing.T) {
	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			Endpoint: observabilitypingcapcomv1.Endpoint{
				Name: "test-endpoint",
			},
			VictoriaAccount: observabilitypingcapcomv1.VictoriaAccount{
				AvailabilityZone: "us-west-2a",
				VMClusterName:    "test-vm",
				AccountID:        12345,
				ProjectID:        67890,
			},
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Config: structs.ServiceInfo{
			AZConfig: map[string]structs.AvailabilityZoneConfig{
				"us-west-2a": {
					ReadIngressClassName: "nginx-read",
				},
			},
		},
	}

	ing := NewIngress(ctx)
	ingress, err := ing.makeVMSelectIngress()

	require.NoError(t, err)
	require.NotNil(t, ingress)
	assert.Equal(t, svcPrefix+cluster.Spec.Endpoint.Name+readSuffix, ingress.Name)
	assert.Equal(t, "nginx-read", *ingress.Spec.IngressClassName)
	assert.Contains(t, ingress.Annotations, "nginx.ingress.kubernetes.io/rewrite-target")
	assert.Len(t, ingress.Spec.Rules, 1)
	assert.NotNil(t, ingress.Spec.Rules[0].HTTP)
	assert.Len(t, ingress.Spec.Rules[0].HTTP.Paths, 1)
	assert.Equal(t, vmSelectSvcPrefix+cluster.Name, ingress.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name)
	assert.Equal(t, vmSelectPort, ingress.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Port.Number)
}

func TestIngress_MakeVMOpenSelectIngress(t *testing.T) {
	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Labels: map[string]string{
				constants.LabelBizType:       "test-biz",
				constants.LabelTenantID:      "tenant-1",
				constants.LabelProjectID:     "project-1",
				constants.LabelApplicationID: "app-1",
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			Endpoint: observabilitypingcapcomv1.Endpoint{
				Name: "test-endpoint",
			},
			VictoriaAccount: observabilitypingcapcomv1.VictoriaAccount{
				AvailabilityZone: "us-west-2a",
				VMClusterName:    "test-vm",
				AccountID:        12345,
				ProjectID:        67890,
			},
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Config: structs.ServiceInfo{
			AZConfig: map[string]structs.AvailabilityZoneConfig{
				"us-west-2a": {
					ReadIngressClassName: "nginx-read",
				},
			},
			BizTypeConfigs: map[string]structs.BizTypeConfig{
				"test-biz": {
					ExternalApiAuthUrlTemplate: "https://auth.example.com/tenant/%s",
				},
			},
		},
	}

	ing := NewIngress(ctx)
	ingress, err := ing.makeVMOpenSelectIngress()

	require.NoError(t, err)
	require.NotNil(t, ingress)
	assert.Equal(t, svcPrefix+cluster.Spec.Endpoint.Name+openReadSuffix, ingress.Name)
	assert.Equal(t, "nginx-read", *ingress.Spec.IngressClassName)
	assert.Contains(t, ingress.Annotations, "nginx.ingress.kubernetes.io/auth-url")
	assert.Contains(t, ingress.Annotations, "nginx.ingress.kubernetes.io/enable-cors")
	assert.Equal(t, "https://auth.example.com/tenant/tenant-1", ingress.Annotations["nginx.ingress.kubernetes.io/auth-url"])
}

func TestIngress_MakeVMInsertIngress(t *testing.T) {
	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			Endpoint: observabilitypingcapcomv1.Endpoint{
				Name: "test-endpoint",
			},
			VictoriaAccount: observabilitypingcapcomv1.VictoriaAccount{
				AvailabilityZone: "us-west-2a",
				VMClusterName:    "test-vm",
				AccountID:        12345,
				ProjectID:        67890,
			},
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Config: structs.ServiceInfo{
			AZConfig: map[string]structs.AvailabilityZoneConfig{
				"us-west-2a": {
					IngressClassName: "nginx",
				},
			},
		},
	}

	ing := NewIngress(ctx)
	ingress, err := ing.makeVMInsertIngress()

	require.NoError(t, err)
	require.NotNil(t, ingress)
	assert.Equal(t, svcPrefix+cluster.Spec.Endpoint.Name, ingress.Name)
	assert.Equal(t, "nginx", *ingress.Spec.IngressClassName)
	assert.Contains(t, ingress.Annotations, "nginx.ingress.kubernetes.io/rewrite-target")
	assert.NotContains(t, ingress.Annotations, "nginx.ingress.kubernetes.io/auth-url")
	assert.Len(t, ingress.Spec.Rules, 1)
	assert.Equal(t, vmInsertSvcPrefix+cluster.Name, ingress.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name)
	assert.Equal(t, vmInsertPort, ingress.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Port.Number)
}

func TestIngress_MakeServerlessInsertIngress(t *testing.T) {
	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			Endpoint: observabilitypingcapcomv1.Endpoint{
				Name: "test-endpoint",
			},
			VictoriaAccount: observabilitypingcapcomv1.VictoriaAccount{
				AvailabilityZone: "us-west-2a",
				VMClusterName:    "test-vm",
				AccountID:        12345,
				ProjectID:        67890,
			},
		},
	}

	ctx := &task.Context{
		Cluster: cluster,
		Logger:  testr.New(t),
		Config: structs.ServiceInfo{
			AZConfig: map[string]structs.AvailabilityZoneConfig{
				"us-west-2a": {
					IngressClassName: "nginx",
				},
			},
		},
	}

	ing := NewIngress(ctx)
	ingress, err := ing.makeServerlessInsertIngress()

	require.NoError(t, err)
	require.NotNil(t, ingress)
	assert.Equal(t, serverlessPrefix+cluster.Spec.Endpoint.Name, ingress.Name)
	assert.Equal(t, "nginx", *ingress.Spec.IngressClassName)
	assert.Contains(t, ingress.Annotations, "nginx.ingress.kubernetes.io/rewrite-target")
	// Serverless ingress uses serverless template
	assert.Contains(t, ingress.Annotations["nginx.ingress.kubernetes.io/rewrite-target"], "multitenant")
	assert.Equal(t, vmInsertSvcPrefix+cluster.Name, ingress.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Name)
}

func TestIngress_BuildIngress(t *testing.T) {
	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			VictoriaAccount: observabilitypingcapcomv1.VictoriaAccount{
				AccountID: 12345,
				ProjectID: 67890,
			},
		},
	}

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

	ing := NewIngress(ctx)

	tests := []struct {
		name          string
		ingressName   string
		ingressClass  string
		targetPrefix  string
		sourcePath    string
		targetService string
		targetPort    int32
		authURL       string
		serverless    bool
		checkFunc     func(*testing.T, *k8snetworkv1.Ingress)
	}{
		{
			name:          "basic ingress without auth",
			ingressName:   "test-ingress",
			ingressClass:  "nginx",
			targetPrefix:  "/insert/",
			sourcePath:    "/metrics/write/test",
			targetService: "vminsert-test",
			targetPort:    8480,
			authURL:       "",
			serverless:    false,
			checkFunc: func(t *testing.T, ingress *k8snetworkv1.Ingress) {
				assert.Equal(t, "test-ingress", ingress.Name)
				assert.Equal(t, "nginx", *ingress.Spec.IngressClassName)
				assert.NotContains(t, ingress.Annotations, "nginx.ingress.kubernetes.io/auth-url")
				assert.Contains(t, ingress.Annotations["nginx.ingress.kubernetes.io/rewrite-target"], "12345:67890")
			},
		},
		{
			name:          "ingress with auth",
			ingressName:   "test-ingress-auth",
			ingressClass:  "nginx-read",
			targetPrefix:  "/select/",
			sourcePath:    "/metrics/read/test",
			targetService: "vmselect-test",
			targetPort:    8481,
			authURL:       "https://auth.example.com",
			serverless:    false,
			checkFunc: func(t *testing.T, ingress *k8snetworkv1.Ingress) {
				assert.Contains(t, ingress.Annotations, "nginx.ingress.kubernetes.io/auth-url")
				assert.Equal(t, "https://auth.example.com", ingress.Annotations["nginx.ingress.kubernetes.io/auth-url"])
				assert.Equal(t, "true", ingress.Annotations["nginx.ingress.kubernetes.io/enable-cors"])
				assert.Equal(t, "*", ingress.Annotations["nginx.ingress.kubernetes.io/cors-allow-origin"])
			},
		},
		{
			name:          "serverless ingress",
			ingressName:   "serverless-ingress",
			ingressClass:  "nginx",
			targetPrefix:  "/insert/",
			sourcePath:    "/metrics/write/serverless",
			targetService: "vminsert-test",
			targetPort:    8480,
			authURL:       "",
			serverless:    true,
			checkFunc: func(t *testing.T, ingress *k8snetworkv1.Ingress) {
				assert.Contains(t, ingress.Annotations["nginx.ingress.kubernetes.io/rewrite-target"], "multitenant")
				assert.NotContains(t, ingress.Annotations["nginx.ingress.kubernetes.io/rewrite-target"], "12345")
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ingress := ing.buildIngress(
				tt.ingressName,
				tt.ingressClass,
				tt.targetPrefix,
				tt.sourcePath,
				tt.targetService,
				tt.targetPort,
				tt.authURL,
				tt.serverless,
			)

			require.NotNil(t, ingress)
			assert.Equal(t, cluster.Namespace, ingress.Namespace)
			assert.Equal(t, "true", ingress.Annotations["nginx.ingress.kubernetes.io/use-regex"])
			assert.Len(t, ingress.Spec.Rules, 1)
			assert.NotNil(t, ingress.Spec.Rules[0].HTTP)
			assert.Len(t, ingress.Spec.Rules[0].HTTP.Paths, 1)

			path := ingress.Spec.Rules[0].HTTP.Paths[0]
			assert.Equal(t, tt.sourcePath+"(/|$)(.*)", path.Path)
			assert.Equal(t, tt.targetService, path.Backend.Service.Name)
			assert.Equal(t, tt.targetPort, path.Backend.Service.Port.Number)

			if tt.checkFunc != nil {
				tt.checkFunc(t, ingress)
			}
		})
	}
}

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

func (m *mockIngressErrorClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error {
	if m.getError != nil {
		return m.getError
	}
	if ingress, ok := obj.(*k8snetworkv1.Ingress); ok {
		ingress.SetName(key.Name)
		ingress.SetNamespace(key.Namespace)
		return nil
	}
	return apierrors.NewNotFound(schema.GroupResource{}, key.Name)
}

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

func (m *mockIngressErrorClient) Scheme() *runtime.Scheme {
	if m.scheme == nil {
		m.scheme = runtime.NewScheme()
		_ = k8snetworkv1.AddToScheme(m.scheme)
		_ = observabilitypingcapcomv1.AddToScheme(m.scheme)
	}
	return m.scheme
}

func TestIngress_Delete_GetError(t *testing.T) {
	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Annotations: map[string]string{
				constants.AnnotationShareMode: string(constants.SHARED),
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			Endpoint: observabilitypingcapcomv1.Endpoint{
				Name: "test-endpoint",
			},
		},
	}

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

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

	ing := NewIngress(ctx)
	result := ing.Delete(context.Background())

	assert.False(t, result.IsDoneState())
	assert.NotNil(t, result.Error())
	assert.Contains(t, result.Error().Error(), "get error")
}

func TestIngress_Delete_DeleteError(t *testing.T) {
	cluster := &observabilitypingcapcomv1.Cluster{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "test-cluster",
			Namespace: "test-namespace",
			Annotations: map[string]string{
				constants.AnnotationShareMode: string(constants.SHARED),
			},
		},
		Spec: observabilitypingcapcomv1.ClusterSpec{
			Endpoint: observabilitypingcapcomv1.Endpoint{
				Name: "test-endpoint",
			},
		},
	}

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

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

	ing := NewIngress(ctx)
	result := ing.Delete(context.Background())

	assert.False(t, result.IsDoneState())
	assert.NotNil(t, result.Error())
	assert.Contains(t, result.Error().Error(), "delete error")
}
