package legendlabels

import (
	"sort"
	"strings"
)

// ExtractDistinctLegendLabels removes label keys that are identical across all series
// and returns per-series labels containing only differing fields.
//
// When all fields are identical, the corresponding output label is an empty string.
func ExtractDistinctLegendLabels(metrics []map[string]string) []string {
	if len(metrics) == 0 {
		return []string{}
	}

	varyingKeys := findVaryingKeys(metrics)
	labels := make([]string, 0, len(metrics))
	for _, metric := range metrics {
		labels = append(labels, formatLegendLabel(metric, varyingKeys))
	}
	return labels
}

func findVaryingKeys(metrics []map[string]string) map[string]struct{} {
	allKeys := make(map[string]struct{})
	for _, metric := range metrics {
		for key := range metric {
			allKeys[key] = struct{}{}
		}
	}

	varyingKeys := make(map[string]struct{})
	for key := range allKeys {
		firstValue, firstOK := metricValue(metrics[0], key)
		for i := 1; i < len(metrics); i++ {
			currentValue, currentOK := metricValue(metrics[i], key)
			if firstOK != currentOK || firstValue != currentValue {
				varyingKeys[key] = struct{}{}
				break
			}
		}
	}

	if hasInstanceField(metrics) {
		delete(varyingKeys, "pod")
		delete(varyingKeys, "pod_name")
	}

	return varyingKeys
}

func hasInstanceField(metrics []map[string]string) bool {
	for _, metric := range metrics {
		if metric == nil {
			continue
		}
		if _, ok := metric["instance"]; ok {
			return true
		}
	}
	return false
}

func formatLegendLabel(metric map[string]string, varyingKeys map[string]struct{}) string {
	if len(varyingKeys) == 0 {
		return ""
	}

	values := make([]string, 0, len(varyingKeys))
	if _, ok := varyingKeys["__name__"]; ok {
		values = append(values, metric["__name__"])
	}

	keys := make([]string, 0, len(varyingKeys))
	for key := range varyingKeys {
		if key == "__name__" {
			continue
		}
		if _, ok := metric[key]; !ok {
			continue
		}
		keys = append(keys, key)
	}
	sort.Strings(keys)

	for _, key := range keys {
		values = append(values, metric[key])
	}

	return strings.Join(values, " - ")
}

func metricValue(metric map[string]string, key string) (string, bool) {
	if metric == nil {
		return "", false
	}
	value, ok := metric[key]
	return value, ok
}
