package metrics

import (
	"math"
	"sort"
	"strings"
	"sync"
)

// RecentLatencyByKey tracks a bounded in-memory latency window per logical key.
// It is intentionally small and process-local; Prometheus histograms keep the
// lifetime view, while this gives capacity-check a recent-window signal.
type RecentLatencyByKey struct {
	mu      sync.Mutex
	size    int
	windows map[string]*recentLatencyWindow
}

type recentLatencyWindow struct {
	values []float64
	next   int
	count  int
}

func NewRecentLatencyByKey(size int) *RecentLatencyByKey {
	if size <= 0 {
		size = 1
	}
	return &RecentLatencyByKey{
		size:    size,
		windows: map[string]*recentLatencyWindow{},
	}
}

func (r *RecentLatencyByKey) Observe(key string, value float64) (p99 float64, samples int) {
	if r == nil {
		return 0, 0
	}
	key = strings.TrimSpace(key)
	if key == "" {
		key = "unknown"
	}
	if value < 0 || math.IsNaN(value) {
		value = 0
	}
	if math.IsInf(value, 1) {
		value = math.MaxFloat64
	}

	r.mu.Lock()
	defer r.mu.Unlock()
	window := r.windows[key]
	if window == nil {
		window = &recentLatencyWindow{values: make([]float64, r.size)}
		r.windows[key] = window
	}
	window.values[window.next] = value
	window.next = (window.next + 1) % len(window.values)
	if window.count < len(window.values) {
		window.count++
	}
	return window.quantileLocked(0.99), window.count
}

func (w *recentLatencyWindow) quantileLocked(q float64) float64 {
	if w == nil || w.count == 0 {
		return 0
	}
	snapshot := append([]float64(nil), w.values[:w.count]...)
	sort.Float64s(snapshot)
	index := int(math.Ceil(q*float64(len(snapshot)))) - 1
	if index < 0 {
		index = 0
	}
	if index >= len(snapshot) {
		index = len(snapshot) - 1
	}
	return snapshot[index]
}
