package infra

import (
	"math"
	"time"
)

// RateLimiterCheck evalua si un request identificado por key puede consumir un token.
// Recarga tokens segun el tiempo transcurrido desde el ultimo check (token bucket).
// Retorna RateLimitResult con Allowed, Remaining, ResetAt y RetryAfter.
// Es seguro para uso concurrente.
func RateLimiterCheck(rl *RateLimiter, key string) RateLimitResult {
	rl.mu.Lock()
	defer rl.mu.Unlock()

	now := time.Now()
	client, ok := rl.clients[key]
	if !ok {
		// Primera vez para esta key: bucket lleno menos el token consumido.
		client = &rateLimiterClient{tokens: float64(rl.burst), lastSeen: now}
		rl.clients[key] = client
	} else {
		// Recargar tokens segun tiempo transcurrido.
		elapsed := now.Sub(client.lastSeen).Seconds()
		client.tokens = math.Min(float64(rl.burst), client.tokens+elapsed*rl.rate)
		client.lastSeen = now
	}

	if client.tokens >= 1 {
		client.tokens -= 1
		remaining := int(math.Floor(client.tokens))
		// ResetAt: cuando volvera a estar lleno.
		missing := float64(rl.burst) - client.tokens
		secondsToFull := missing / rl.rate
		return RateLimitResult{
			Allowed:    true,
			Remaining:  remaining,
			ResetAt:    now.Add(time.Duration(secondsToFull * float64(time.Second))),
			RetryAfter: 0,
		}
	}

	// Sin tokens suficientes — calcular cuanto esperar.
	needed := 1 - client.tokens
	retryAfter := needed / rl.rate
	missing := float64(rl.burst) - client.tokens
	secondsToFull := missing / rl.rate
	return RateLimitResult{
		Allowed:    false,
		Remaining:  0,
		ResetAt:    now.Add(time.Duration(secondsToFull * float64(time.Second))),
		RetryAfter: retryAfter,
	}
}
