package transform

import (
	"regexp"
	"slices"
	"strings"

	"github.com/prometheus/prometheus/model/labels"

	"github.com/prymitive/karma/internal/models"
	sliceutils "github.com/prymitive/karma/internal/slices"
)

// StripLabels allows filtering out some labels from alerts
// it takes the list of label keys to ignore and alert label map
// it will return label map without labels found on the ignore list
func StripLabels(keptLabels, ignoredLabels []string, keptLabelsRegex, ignoredLabelsRegex []*regexp.Regexp,
	sourceLabels labels.Labels,
) labels.Labels {
	// empty keep lists means keep everything by default
	keepAll := len(keptLabels) == 0 && len(keptLabelsRegex) == 0
	// if we keep everything and there's nothing to strip then simply return source labels as-is
	if keepAll && len(ignoredLabels) == 0 && len(ignoredLabelsRegex) == 0 {
		return sourceLabels
	}
	b := labels.NewBuilder(labels.EmptyLabels())
	var inKeep, inStrip bool
	sourceLabels.Range(func(l labels.Label) {
		// is explicitly marked to be kept
		inKeep = slices.Contains(keptLabels, l.Name) || sliceutils.MatchesAnyRegex(l.Name, keptLabelsRegex)
		// is explicitly marked to be stripped
		inStrip = slices.Contains(ignoredLabels, l.Name) || sliceutils.MatchesAnyRegex(l.Name, ignoredLabelsRegex)
		if (keepAll || inKeep) && !inStrip {
			// strip leading and trailing space in label value
			// this is to normalize values in case space is added by Alertmanager rules
			b.Set(l.Name, strings.TrimSpace(l.Value))
		}
	})
	return b.Labels()
}

// StripReceivers allows filtering all alerts for specified receiver(s)
// it will return true if alert uses receiver that should be stripped
func StripReceivers(keptReceivers, ignoredReceivers []string, keptReceiversRegex, ignoredReceiversRegex []*regexp.Regexp, alertReceiver string) bool {
	// empty keep lists means keep everything by default
	keepAll := len(keptReceivers) == 0 && len(keptReceiversRegex) == 0
	// is explicitly marked to be kept
	inKeep := slices.Contains(keptReceivers, alertReceiver) || sliceutils.MatchesAnyRegex(alertReceiver, keptReceiversRegex)
	// is explicitly marked to be stripped
	inStrip := slices.Contains(ignoredReceivers, alertReceiver) || sliceutils.MatchesAnyRegex(alertReceiver, ignoredReceiversRegex)

	if (keepAll || inKeep) && !inStrip {
		return false
	}
	return true
}

// StripAnnotations allows to ignore some annotations when pulling data
// Alertmanager, it will return true if passed annotation name should be
// stripped
func StripAnnotations(keptAnnotations, ignoredAnnotations []string, sourceAnnotations models.Annotations) models.Annotations {
	// empty keep list means keep everything by default
	keepAll := len(keptAnnotations) == 0
	annotations := make(models.Annotations, 0, len(sourceAnnotations))
	for _, annotation := range sourceAnnotations {
		// is explicitly marked to be kept
		inKeep := slices.Contains(keptAnnotations, annotation.Name)
		// is explicitly marked to be stripped
		inStrip := slices.Contains(ignoredAnnotations, annotation.Name)
		if (keepAll || inKeep) && !inStrip {
			annotations = append(annotations, annotation)
		}
	}
	return annotations
}
