package namer

import (
	"crypto/md5"
	"fmt"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	hashSize = 8

	GcpPscNameLimit = 63
)

type IdentifierOptions func(string) string

// WithPrefix adds a prefix to the generated unique identifier.
func WithPrefix(prefix string) IdentifierOptions {
	return func(name string) string {
		if len(prefix) == 0 || prefix[len(prefix)-1] == '-' {
			return prefix + name
		}
		return prefix + "-" + name
	}
}

// WithLimit limits the generated unique identifier.
// Note this option needs to be the last option for `UniqueIdentifier` function
// namely `WithLimit` should be after `WithPrefix` and other `IdentifierOptions`.
// Otherwise, the final generated uid would exceed the limit
func WithLimit(limit int) IdentifierOptions {
	return func(name string) string {
		resultLimit := limit - hashSize - 1
		if len(name) > resultLimit {
			return name[:resultLimit]
		}
		return name
	}
}

// UniqueIdentifier generates a unique identifier based on the given object's metadata.
// It combines the namespace and name of the object, applies any provided options,
// and appends an 8-character MD5 hash to ensure uniqueness within Kubernetes label constraints.
func UniqueIdentifier(meta metav1.ObjectMeta, options ...IdentifierOptions) string {
	// See https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#rfc-1035-label-names
	const (
		labelLengthLimit = 63
		resultLimit      = labelLengthLimit - hashSize - 1
	)

	namespacedName := fmt.Sprintf("%s/%s", meta.GetNamespace(), meta.GetName())
	hash := fmt.Sprintf("%x", md5.Sum([]byte(namespacedName)))[:hashSize]

	// The 8-digit hash will not be trimmed. Keep the namespaced name if possible
	result := meta.GetName()
	if meta.GetNamespace() != "" {
		result = fmt.Sprintf("%s-%s", meta.GetNamespace(), meta.GetName())
	}
	for _, opt := range options {
		result = opt(result)
	}
	if len(result) > resultLimit {
		result = result[:resultLimit]
	}
	return fmt.Sprintf("%s-%s", result, hash)
}
