package alertmanager_test

import (
	"fmt"
	"log/slog"
	"testing"
	"time"

	"github.com/prymitive/karma/internal/alertmanager"
	"github.com/prymitive/karma/internal/config"
	"github.com/prymitive/karma/internal/log"
	"github.com/prymitive/karma/internal/mock"

	"github.com/jarcoal/httpmock"
	"github.com/spf13/pflag"
)

func init() {
	log.SetLevel(slog.LevelError)

	httpmock.Activate()

	for _, version := range mock.ListAllMocks() {
		name := "dedup-mock-" + version
		uri := fmt.Sprintf("http://%s.localhost", version)
		am, err := alertmanager.NewAlertmanager("cluster", name, uri, alertmanager.WithRequestTimeout(time.Second))
		if err != nil {
			panic(fmt.Sprintf("Error: %v", err))
		}
		err = alertmanager.RegisterAlertmanager(am)
		if err != nil {
			panic(fmt.Sprintf("Failed to register Alertmanager instance %s: %s", am.Name, err))
		}

		mock.RegisterURL(uri+"/metrics", version, "metrics")
		mock.RegisterURL(uri+"/api/v2/silences", version, "api/v2/silences")
		mock.RegisterURL(uri+"/api/v2/alerts/groups", version, "api/v2/alerts/groups")
	}
}

func pullAlerts() error {
	for _, am := range alertmanager.GetAlertmanagers() {
		err := am.Pull()
		if err != nil {
			return err
		}
	}
	return nil
}

func mockConfigRead() {
	f := pflag.NewFlagSet(".", pflag.ExitOnError)
	config.SetupFlags(f)
	_, _ = config.Config.Read(f)
}

func TestDedupAlerts(t *testing.T) {
	if err := pullAlerts(); err != nil {
		t.Error(err)
	}
	alertGroups := alertmanager.DedupAlerts()

	if len(alertGroups) != 10 {
		t.Errorf("Expected %d alert groups, got %d", 10, len(alertGroups))
	}

	totalAlerts := 0
	for _, ag := range alertGroups {
		totalAlerts += len(ag.Alerts)
	}
	if totalAlerts != 24 {
		t.Errorf("Expected %d total alerts, got %d", 24, totalAlerts)
	}
}

func TestDedupAlertsWithoutLabels(t *testing.T) {
	config.Config.Labels.Keep = []string{"xyz"}
	if err := pullAlerts(); err != nil {
		t.Error(err)
	}
	alertGroups := alertmanager.DedupAlerts()
	config.Config.Labels.Keep = []string{}

	if len(alertGroups) != 10 {
		t.Errorf("Expected %d alert groups, got %d", 10, len(alertGroups))
	}

	totalAlerts := 0
	for _, ag := range alertGroups {
		totalAlerts += len(ag.Alerts)
	}
	if totalAlerts != 24 {
		t.Errorf("Expected %d total alerts, got %d", 24, totalAlerts)
	}
}

func TestDedupSilences(t *testing.T) {
	t.Setenv("ALERTMANAGER_URI", "http://localhost")
	mockConfigRead()
	if err := pullAlerts(); err != nil {
		t.Error(err)
	}
	silences := alertmanager.DedupSilences()

	mockCount := len(mock.ListAllMockURIs())
	expected := mockCount * 3
	if len(silences) != expected {
		t.Errorf("Expected %d silences keys, got %d", expected, len(silences))
	}
}

func TestDedupAutocomplete(t *testing.T) {
	if err := pullAlerts(); err != nil {
		t.Error(err)
	}
	ac := alertmanager.DedupAutocomplete()
	mockCount := len(mock.ListAllMockURIs())
	// 74 hints for everything except @alertmanager and @silenced_by
	// 4 hints for @silenced_by 1 and 2
	// 2 hints per @alertmanager
	// 2 hits per @cluster
	// 6 hints for silences in for each alertmanager
	// silence id might get duplicated so this check isn't very strict
	expected := 74 + 4 + mockCount*2 + mockCount*2 + mockCount*6
	if len(ac) <= int(float64(expected)*0.8) || len(ac) > expected {
		t.Errorf("Expected %d autocomplete hints, got %d", expected, len(ac))
	}
}

func TestDedupColors(t *testing.T) {
	t.Setenv("LABELS_COLOR_UNIQUE", "cluster instance @receiver")
	t.Setenv("ALERTMANAGER_URI", "http://localhost")
	mockConfigRead()
	if err := pullAlerts(); err != nil {
		t.Error(err)
	}
	colors := alertmanager.DedupColors()
	expected := 3
	if len(colors) != expected {
		t.Errorf("Expected %d color keys, got %d", expected, len(colors))
	}
}

func TestDedupKnownLabels(t *testing.T) {
	t.Setenv("ALERTMANAGER_URI", "http://localhost")
	mockConfigRead()
	if err := pullAlerts(); err != nil {
		t.Error(err)
	}
	labels := alertmanager.DedupKnownLabels()
	expected := 6
	if len(labels) != expected {
		t.Errorf("Expected %d knownLabels keys, got %d", expected, len(labels))
	}
}

func TestDedupKnownLabelValues(t *testing.T) {
	t.Setenv("ALERTMANAGER_URI", "http://localhost")
	mockConfigRead()
	if err := pullAlerts(); err != nil {
		t.Error(err)
	}
	values := alertmanager.DedupKnownLabelValues("alertname")
	expected := 4
	if len(values) != expected {
		t.Errorf("Expected %d knownLabelValues keys, got %d", expected, len(values))
	}
}

func TestStripReceivers(t *testing.T) {
	t.Setenv("RECEIVERS_STRIP", "by-name by-cluster-service")
	t.Setenv("ALERTMANAGER_URI", "http://localhost")
	mockConfigRead()
	if err := pullAlerts(); err != nil {
		t.Error(err)
	}
	alerts := alertmanager.DedupAlerts()
	if len(alerts) > 0 {
		t.Errorf("Expected no alerts after stripping all receivers, got %d", len(alerts))
	}
}

func TestClearData(t *testing.T) {
	httpmock.Activate()
	defer httpmock.DeactivateAndReset()

	for _, version := range mock.ListAllMocks() {
		name := "clear-data-mock-" + version
		uri := "http://localhost/clear/" + version
		am, _ := alertmanager.NewAlertmanager("cluster", name, uri, alertmanager.WithRequestTimeout(time.Second))

		mock.RegisterURL(uri+"/metrics", version, "metrics")
		_ = am.Pull()
		if am.Version() == "" {
			t.Errorf("[%s] Got empty version string", am.Name)
		}
		if am.Error() == "" {
			t.Errorf("[%s] Got empty error string", am.Name)
		}
		if len(am.Silences()) != 0 {
			t.Errorf("[%s] Get %d silences", am.Name, len(am.Silences()))
		}
		if len(am.Alerts()) != 0 {
			t.Errorf("[%s] Get %d alerts", am.Name, len(am.Alerts()))
		}
		if len(am.KnownLabels()) != 0 {
			t.Errorf("[%s] Get %d known labels", am.Name, len(am.KnownLabels()))
		}

		mock.RegisterURL(uri+"/api/v2/silences", version, "api/v2/silences")
		_ = am.Pull()
		if am.Version() == "" {
			t.Errorf("[%s] Got empty version string: %s", am.Name, am.Version())
		}
		if am.Error() == "" {
			t.Errorf("[%s] Got empty error string", am.Name)
		}
		if len(am.Silences()) != 0 {
			t.Errorf("[%s] Get %d silences", am.Name, len(am.Silences()))
		}
		if len(am.Alerts()) != 0 {
			t.Errorf("[%s] Get %d alerts", am.Name, len(am.Alerts()))
		}
		if len(am.KnownLabels()) != 0 {
			t.Errorf("[%s] Get %d known labels", am.Name, len(am.KnownLabels()))
		}

		mock.RegisterURL(uri+"/api/v2/alerts/groups", version, "api/v2/alerts/groups")
		_ = am.Pull()
		if am.Version() == "" {
			t.Errorf("[%s] Got empty version string", am.Name)
		}
		if len(am.Silences()) == 0 {
			t.Errorf("[%s] Get %d silences", am.Name, len(am.Silences()))
		}
		if len(am.Alerts()) == 0 {
			t.Errorf("[%s] Get %d alerts", am.Name, len(am.Alerts()))
		}
		if len(am.KnownLabels()) == 0 {
			t.Errorf("[%s] Get %d known labels", am.Name, len(am.KnownLabels()))
		}
	}
}
