package shared

import (
	"net/http"
	"strconv"
	"strings"
)

// PriorityHeader carries the caller's scheduling priority. Priority is a
// property of the caller's intent, not of the model — the same model serves
// both an interactive request and a batch job — so it travels as a header
// rather than as per-model configuration, and leaves the OpenAI-compatible
// request body untouched.
const PriorityHeader = "X-LlamaSwap-Priority"

// Priority band anchors. The number is the interface: callers may send any
// signed integer, and these names are conveniences that resolve to a value.
//
// Bands are spaced 100 apart so a consumer can add a small per-caller offset
// (a subscription tier, say) on top of a band without ever promoting a request
// across one: a "max member" batch job at -98 still loses to every normal
// request at 0.
const (
	PriorityInteractive = 100 // a human is waiting
	PriorityNormal      = 0   // the default
	PriorityBatch       = -100
)

// bandHalfWidth is how far a priority may sit from a band anchor and still be
// reported as that band. It is half the 100-point band spacing, so every
// integer belongs to exactly one band.
const bandHalfWidth = 50

// Band names used for metrics labels, ordered most to least urgent.
const (
	BandInteractive = "interactive"
	BandNormal      = "normal"
	BandBatch       = "batch"
)

// PriorityBands is every band name, in descending order of urgency. Metrics
// emit a series per band regardless of whether any request currently occupies
// it, so the label set stays stable.
var PriorityBands = []string{BandInteractive, BandNormal, BandBatch}

var priorityAliases = map[string]int{
	"interactive": PriorityInteractive,
	"normal":      PriorityNormal,
	"batch":       PriorityBatch,
}

// ParsePriority resolves a PriorityHeader value to a signed integer. A numeric
// value is used as-is; a recognised alias resolves to its anchor. Anything
// absent or unparseable is PriorityNormal, so a malformed header degrades to
// the default rather than failing the request.
//
// Values are deliberately not clamped: the caller composes band, tier offset
// and anything else it wants into one number, and llama-swap honours it.
func ParsePriority(value string) int {
	value = strings.TrimSpace(value)
	if value == "" {
		return PriorityNormal
	}
	if n, err := strconv.Atoi(value); err == nil {
		return n
	}
	if n, ok := priorityAliases[strings.ToLower(value)]; ok {
		return n
	}
	return PriorityNormal
}

// RequestPriority reads PriorityHeader off r and resolves it. See ParsePriority.
func RequestPriority(r *http.Request) int {
	return ParsePriority(r.Header.Get(PriorityHeader))
}

// PriorityBand buckets a priority into the band it belongs to, for metrics
// labels. Values beyond the anchors saturate: +1000 is still "interactive".
func PriorityBand(priority int) string {
	switch {
	case priority >= PriorityInteractive-bandHalfWidth:
		return BandInteractive
	case priority <= PriorityBatch+bandHalfWidth:
		return BandBatch
	default:
		return BandNormal
	}
}
