package collectors

import (
	"testing"

	"github.com/prometheus/client_golang/prometheus"
	dto "github.com/prometheus/client_model/go"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/consts"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"
)

func TestLoadBalancerCollectsPublicDNSReachability(t *testing.T) {
	scheme := runtime.NewScheme()
	utilruntime.Must(v1alpha1.SchemeBuilder.AddToScheme(scheme))

	lb := &v1alpha1.LoadBalancer{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "public-lb",
			Namespace: "dataplane-metadata",
			Labels: map[string]string{
				consts.TenantLabelKey:    "tenant-1",
				consts.ClusterLabelKey:   "cluster-1",
				consts.TiDBGroupLabelKey: "group-1",
			},
			Annotations: map[string]string{
				consts.AnnotationKeyPublicDNSReachable: "false",
				consts.AnnotationKeyPublicDNSReason:    "DNS_NOT_REACHABLE",
			},
		},
		Spec: v1alpha1.LoadBalancerSpec{Type: v1alpha1.PublicLoadBalancerType},
		Status: v1alpha1.LoadBalancerStatus{
			DNS:   "tidb.example.com",
			Phase: v1alpha1.LoadBalancerPhaseCreating,
		},
	}

	cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(lb).Build()
	collector := &loadBalancer{cli: cli, log: ctrl.Log}
	ch := make(chan prometheus.Metric, 10)
	collector.Collect(ch)

	var dnsMetric *dto.Metric
	for len(ch) > 0 {
		metric := <-ch
		out := &dto.Metric{}
		if err := metric.Write(out); err != nil {
			t.Fatalf("write metric: %v", err)
		}
		if hasLabel(out, "dns") {
			dnsMetric = out
			break
		}
	}
	if dnsMetric == nil {
		t.Fatalf("public DNS reachability metric not found")
	}
	if got := dnsMetric.GetGauge().GetValue(); got != 0 {
		t.Fatalf("gauge value = %v, want 0", got)
	}
	assertMetricLabel(t, dnsMetric, "tenant_id", "tenant-1")
	assertMetricLabel(t, dnsMetric, "cluster_id", "cluster-1")
	assertMetricLabel(t, dnsMetric, "tidb_group_id", "group-1")
	assertMetricLabel(t, dnsMetric, "dns", "tidb.example.com")
	assertMetricLabel(t, dnsMetric, "reason", "DNS_NOT_REACHABLE")
}

func hasLabel(metric *dto.Metric, name string) bool {
	for _, label := range metric.GetLabel() {
		if label.GetName() == name {
			return true
		}
	}
	return false
}

func assertMetricLabel(t *testing.T, metric *dto.Metric, name, want string) {
	t.Helper()
	for _, label := range metric.GetLabel() {
		if label.GetName() == name {
			if got := label.GetValue(); got != want {
				t.Fatalf("label %s = %q, want %q", name, got, want)
			}
			return
		}
	}
	t.Fatalf("label %s not found", name)
}
