func suffixLabelsInOrder(metricName string, labels Labels) string {
	orderedLabels := make([]string, 0, len(labels))
	for labelKey, labelVal := range labels {
		orderedLabels = append(orderedLabels, fmt.Sprintf("%s_%s", labelKey, labelVal))
	}
	sort.Strings(orderedLabels)
	for _, l := range orderedLabels {
		metricName = fmt.Sprintf("%s_%s", metricName, l)
	}
	return metricName
}
suffixLabelsInOrder takes the given metricName and appends, in alphabetical
order, the given labels in the form of <label_key>_<label_value>.
package prometheus
import (
	"errors"
	"fmt"
	"math"
	"sort"
	"strconv"
	"strings"
	model "github.com/prometheus/client_model/go"
	"github.com/newrelic/nri-kubernetes/v2/src/definition"
)
var ControlPlaneComponentTypeGenerator = func(
	groupLabel string,
	_ string,
	_ definition.RawGroups,
	clusterName string,
) (string, error) {
	return fmt.Sprintf("k8s:%s:controlplane:%s", clusterName, groupLabel), nil
}
var FromRawEntityIDGenerator = func(_, rawEntityID string, _ definition.RawGroups) (string, error) {
	return rawEntityID, nil
}
func FromLabelValueEntityTypeGenerator(key string) definition.EntityTypeGeneratorFunc {
	return func(groupLabel string, rawEntityID string, g definition.RawGroups, clusterName string) (string, error) {
		switch groupLabel {
		case "namespace", "node":
			return fmt.Sprintf("k8s:%s:%s", clusterName, groupLabel), nil
		case "container":
			labels, err := getLabels(groupLabel, rawEntityID, key, g, "namespace", "pod")
			if err != nil {
				return "", err
			}
			if len(labels) != 2 {
				return "", fmt.Errorf("cannot retrieve values for composing entity type for %q", groupLabel)
			}
			namespace := labels[0]
			podName := labels[1]
			if namespace == "" || podName == "" {
				return "", fmt.Errorf("empty values for generated entity type for %q", groupLabel)
			}
			return fmt.Sprintf("k8s:%s:%s:%s:%s", clusterName, namespace, podName, groupLabel), nil
		default:
			labels, err := getLabels(groupLabel, rawEntityID, key, g, "namespace")
			if err != nil {
				return "", err
			}
			if len(labels) == 0 {
				return "", fmt.Errorf("cannot retrieve values for composing entity type for %q", groupLabel)
			}
			namespace := labels[0]
			if namespace == "" {
				return "", fmt.Errorf("empty namespace for generated entity type for %q", groupLabel)
			}
			return fmt.Sprintf("k8s:%s:%s:%s", clusterName, namespace, groupLabel), nil
		}
	}
}
func getLabels(groupLabel, rawEntityID, key string, groups definition.RawGroups, labels ...string) ([]string, error) {
	var s []string
	for _, label := range labels {
		v, err := FromLabelValue(key, label)(groupLabel, rawEntityID, groups)
		if err != nil {
			return s, fmt.Errorf("cannot fetch label %q for metric %q: %w", label, key, err)
		}
		if v == nil {
			return s, fmt.Errorf("label %q not found for metric %q", label, key)
		}
		val, ok := v.(string)
		if !ok {
			return s, fmt.Errorf("unexpected type %T of label %q for metric %q", v, label, key)
		}
		s = append(s, val)
	}
	return s, nil
}
func FromLabelValueEntityIDGenerator(key, label string) definition.EntityIDGeneratorFunc {
	return func(groupLabel string, rawEntityID string, g definition.RawGroups) (string, error) {
		v, err := FromLabelValue(key, label)(groupLabel, rawEntityID, g)
		if err != nil {
			return "", fmt.Errorf("cannot fetch label %q for metric %q: %w", label, key, err)
		}
		if v == nil {
			return "", fmt.Errorf("unexpected nil value of fetched data for metric %q", key)
		}
		val, ok := v.(string)
		if !ok {
			return "", fmt.Errorf("incorrect type %T of fetched data for metric %q", v, key)
		}
		return val, err
	}
}
func FromLabelsValueEntityIDGeneratorForPendingPods() definition.EntityIDGeneratorFunc {
	return func(groupLabel string, rawEntityID string, g definition.RawGroups) (string, error) {
		podName, err := FromLabelValueEntityIDGenerator("kube_pod_status_phase", "pod")(groupLabel, rawEntityID, g)
		if err != nil {
			return "", err
		}
		isScheduled, err := FromLabelValueEntityIDGenerator("kube_pod_status_scheduled", "condition")(groupLabel, rawEntityID, g)
		if err != nil {
			return "", err
		}
		if isScheduled != "false" {
			return "", fmt.Errorf("ignoring pending pod, which is scheduled: reported from Kubelet endpoint")
		}
		return podName, nil
	}
}
func GroupEntityMetricsBySpec(
	specs definition.SpecGroups,
	families []MetricFamily,
	rawEntityID string,
) (g definition.RawGroups, errs []error) {
	g = make(definition.RawGroups)
	for groupLabel := range specs {
		for _, f := range families {
			for _, m := range f.Metrics {
				if _, ok := g[groupLabel]; !ok {
					g[groupLabel] = make(map[string]definition.RawMetrics)
				}
				if _, ok := g[groupLabel][rawEntityID]; !ok {
					g[groupLabel][rawEntityID] = make(definition.RawMetrics)
				}
				groupedMetrics, ok := g[groupLabel][rawEntityID][f.Name]
				if !ok {
					groupedMetrics = make([]Metric, 0)
				}
				g[groupLabel][rawEntityID][f.Name] = append(groupedMetrics.([]Metric), m)
			}
		}
		if len(g[groupLabel]) == 0 {
			errs = append(errs, fmt.Errorf("no data found for %s object", groupLabel))
			continue
		}
	}
	return g, errs
}
func GroupMetricsBySpec(specs definition.SpecGroups, families []MetricFamily) (g definition.RawGroups, errs []error) {
	g = make(definition.RawGroups)
	for groupLabel := range specs {
		for _, f := range families {
			for _, m := range f.Metrics {
				if !m.Labels.Has(groupLabel) {
					continue
				}
				rawEntityID := ""
				if groupLabel == "node" && m.Labels.Has("pod") {
					continue
				}
				if groupLabel == "namespace" && (m.Labels.Has("daemonset") ||
					m.Labels.Has("pod") ||
					m.Labels.Has("endpoint") ||
					m.Labels.Has("service") ||
					m.Labels.Has("deployment") || m.Labels.Has("replicaset")) {
					continue
				}
				switch groupLabel {
				case "namespace", "node":
					rawEntityID = m.Labels[groupLabel]
				case "container":
					rawEntityID = fmt.Sprintf("%v_%v_%v", m.Labels["namespace"], m.Labels["pod"], m.Labels[groupLabel])
				default:
					rawEntityID = fmt.Sprintf("%v_%v", m.Labels["namespace"], m.Labels[groupLabel])
				}
				if _, ok := g[groupLabel]; !ok {
					g[groupLabel] = make(map[string]definition.RawMetrics)
				}
				if _, ok := g[groupLabel][rawEntityID]; !ok {
					g[groupLabel][rawEntityID] = make(definition.RawMetrics)
				}
				if v, ok := g[groupLabel][rawEntityID][f.Name]; ok {
					if oldMetric, ok := v.(Metric); ok {
						g[groupLabel][rawEntityID][f.Name] = []Metric{oldMetric}
					}
					g[groupLabel][rawEntityID][f.Name] = append(g[groupLabel][rawEntityID][f.Name].([]Metric), m)
					continue
				}
				g[groupLabel][rawEntityID][f.Name] = m
			}
		}
		if len(g[groupLabel]) == 0 {
			errs = append(errs, fmt.Errorf("no data found for %s object", groupLabel))
			continue
		}
	}
	return g, errs
}
type LabelsFilter func(Labels) Labels
func IgnoreLabelsFilter(labelsToIgnore ...string) func(Labels) Labels {
	return func(labels Labels) Labels {
		filteredLabels := make(Labels)
		for label, value := range labels {
			ignore := false
			for _, labelToIgnore := range labelsToIgnore {
				if labelToIgnore == label {
					ignore = true
					break
				}
			}
			if !ignore {
				filteredLabels[label] = value
			}
		}
		return filteredLabels
	}
}
func IncludeOnlyLabelsFilter(labelsToInclude ...string) func(Labels) Labels {
	return func(labels Labels) Labels {
		filteredLabels := make(Labels)
		for label, value := range labels {
			include := false
			for _, labelToIgnore := range labelsToInclude {
				if labelToIgnore == label {
					include = true
					break
				}
			}
			if include {
				filteredLabels[label] = value
			}
		}
		return filteredLabels
	}
}
func attributeName(metricName, nameOverride string, labels Labels, labelsFilter ...LabelsFilter) string {
	for _, filter := range labelsFilter {
		labels = filter(labels)
	}
	if nameOverride != "" {
		return suffixLabelsInOrder(nameOverride, labels)
	}
	return suffixLabelsInOrder(metricName, labels)
}
func fetchedValuesFromRawMetrics(
	metricName string,
	nameOverride string,
	metrics []Metric,
	labelsFilter ...LabelsFilter,
) (definition.FetchedValues, error) {
	val := make(definition.FetchedValues)
	for _, metric := range metrics {
		attrName := attributeName(metricName, nameOverride, metric.Labels, labelsFilter...)
		aggregatedValue, ok := val[attrName]
		if !ok {
			val[attrName] = metric.Value
			continue
		}
		switch metric.Value.(type) {
		case CounterValue:
			aggregatedCounter, ok := aggregatedValue.(CounterValue)
			if !ok {
				return nil, fmt.Errorf(
					"incompatible metric type for %s aggregation. Expected: CounterValue. Got: %T",
					metricName,
					metric.Value,
				)
			}
			val[attrName] = aggregatedCounter + metric.Value.(CounterValue)
		case GaugeValue:
			aggregatedCounter, ok := aggregatedValue.(GaugeValue)
			if !ok {
				return nil, fmt.Errorf(
					"incompatible metric type for %s aggregation. Expected: GaugeValue. Got: %T",
					metricName,
					metric.Value,
				)
			}
			val[attrName] = aggregatedCounter + metric.Value.(GaugeValue)
		}
	}
	return val, nil
}
func FromValue(metricName string, labelsFilter ...LabelsFilter) definition.FetchFunc {
	return FromValueWithOverriddenName(metricName, "", labelsFilter...)
}
func FromValueWithOverriddenName(metricName string, nameOverride string, labelsFilter ...LabelsFilter) definition.FetchFunc {
	return func(groupLabel, entityID string, groups definition.RawGroups) (definition.FetchedValue, error) {
		value, err := definition.FromRaw(metricName)(groupLabel, entityID, groups)
		if err != nil {
			return nil, err
		}
		switch m := value.(type) {
		case Metric:
			return m.Value, nil
		case []Metric:
			return fetchedValuesFromRawMetrics(metricName, nameOverride, m, labelsFilter...)
		}
		return nil, fmt.Errorf(
			"incompatible metric type for %s. Expected: Metric or []Metric. Got: %T",
			metricName,
			value,
		)
	}
}
func FromLabelValue(key, label string) definition.FetchFunc {
	return func(groupLabel, entityID string, groups definition.RawGroups) (definition.FetchedValue, error) {
		value, err := definition.FromRaw(key)(groupLabel, entityID, groups)
		if err != nil {
			return nil, err
		}
		v, ok := value.(Metric)
		if !ok {
			return nil, fmt.Errorf("incompatible metric type. Expected: Metric. Got: %T", value)
		}
		l, ok := v.Labels[label]
		if !ok {
			return nil, errors.New("label not found in prometheus metric")
		}
		return l, nil
	}
}