// Package diagnostics provides bounded, safe text for deterministic D&D
// decisions and warnings.
package diagnostics

import (
	"fmt"
	"strconv"
	"strings"

	"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
)

const (
	MaxIssues         = 20
	MaxWarnings       = 20
	MaxDisplayedRunes = 128
	MaxMessageBytes   = 4096
)

func Truncate(value string) string {
	runes := []rune(value)
	if len(runes) <= MaxDisplayedRunes {
		return value
	}
	return string(runes[:MaxDisplayedRunes-1]) + "…"
}

func Quote(value string) string { return strconv.Quote(Truncate(value)) }

func Aggregate(prefix string, issues []string) string {
	displayed := make([]string, 0, min(len(issues), MaxIssues))
	for len(displayed) < len(issues) && len(displayed) < MaxIssues {
		issue := Truncate(issues[len(displayed)])
		candidate := aggregateMessage(prefix, append(displayed, issue), len(issues)-len(displayed)-1)
		if len([]byte(candidate)) > MaxMessageBytes {
			break
		}
		displayed = append(displayed, issue)
	}
	return aggregateMessage(prefix, displayed, len(issues)-len(displayed))
}

// LimitWarnings returns at most MaxWarnings warnings, reserving the final
// position for a deterministic omission summary when truncation is required.
func LimitWarnings(warnings []contracts.Warning, scope, reasonCode string) []contracts.Warning {
	if warnings == nil {
		return nil
	}
	if len(warnings) <= MaxWarnings {
		bounded := make([]contracts.Warning, len(warnings))
		copy(bounded, warnings)
		return bounded
	}
	displayed := MaxWarnings - 1
	bounded := make([]contracts.Warning, displayed, MaxWarnings)
	copy(bounded, warnings[:displayed])
	bounded = append(bounded, contracts.Warning{
		Scope:      scope,
		ReasonCode: reasonCode,
		Message:    fmt.Sprintf("%d additional warning(s) omitted", len(warnings)-displayed),
	})
	return bounded
}

func aggregateMessage(prefix string, issues []string, omitted int) string {
	message := prefix + ": " + strings.Join(issues, ", ")
	if omitted > 0 {
		message += fmt.Sprintf("; %d additional issue(s) omitted", omitted)
	}
	return message
}

func min(left, right int) int {
	if left < right {
		return left
	}
	return right
}
