package deployer

import (
	"regexp"
	"strings"
)

// maxSubdomainLen is the maximum length of a single DNS label (RFC 1035).
const maxSubdomainLen = 63

// invalidDNSChars matches characters not allowed in a DNS label.
var invalidDNSChars = regexp.MustCompile(`[^a-z0-9-]`)

// GenerateSubdomain builds a subdomain string from the given pattern and parameters.
// The pattern may contain {stage}, {project}, and {tag} placeholders.
// If the stage has a custom subdomain override, that value is used instead of the pattern.
func GenerateSubdomain(pattern, project, stage, tag, stageSubdomain string) string {
	if stageSubdomain != "" {
		return SanitizeDNSLabel(stageSubdomain)
	}

	result := pattern
	result = strings.ReplaceAll(result, "{stage}", stage)
	result = strings.ReplaceAll(result, "{project}", project)
	result = strings.ReplaceAll(result, "{tag}", tag)

	return SanitizeDNSLabel(result)
}

// GenerateTaggedSubdomain builds a subdomain that includes the tag for multi-instance support.
// It appends "-{sanitized_tag}" to the base subdomain.
func GenerateTaggedSubdomain(pattern, project, stage, tag, stageSubdomain string) string {
	base := GenerateSubdomain(pattern, project, stage, "", stageSubdomain)
	sanitizedTag := SanitizeDNSLabel(tag)

	if sanitizedTag == "" {
		return base
	}

	combined := base + "-" + sanitizedTag
	return truncateDNSLabel(combined)
}

// SanitizeDNSLabel converts an arbitrary string into a valid DNS label.
// It lowercases, replaces dots and invalid characters with hyphens,
// collapses consecutive hyphens, trims leading/trailing hyphens, and truncates.
func SanitizeDNSLabel(s string) string {
	s = strings.ToLower(s)
	s = strings.ReplaceAll(s, ".", "-")
	s = invalidDNSChars.ReplaceAllString(s, "-")
	s = collapseHyphens(s)
	s = strings.Trim(s, "-")
	return truncateDNSLabel(s)
}

// collapseHyphens replaces consecutive hyphens with a single hyphen.
func collapseHyphens(s string) string {
	prev := false
	var b strings.Builder
	b.Grow(len(s))

	for _, r := range s {
		if r == '-' {
			if !prev {
				b.WriteRune(r)
			}
			prev = true
		} else {
			b.WriteRune(r)
			prev = false
		}
	}
	return b.String()
}

// truncateDNSLabel truncates a label to maxSubdomainLen characters,
// ensuring it does not end with a hyphen after truncation.
func truncateDNSLabel(s string) string {
	if len(s) <= maxSubdomainLen {
		return s
	}
	s = s[:maxSubdomainLen]
	return strings.TrimRight(s, "-")
}
