package main

import (
	"sync"
	"time"
)

// RateLimiter is a per-IP fixed-window counter used to throttle the public
// /book endpoint. It is intentionally simple — in-memory, single-process —
// matching the single-tenant deployment model. A multi-replica deployment
// would need a shared store (Redis or DB) before this limiter is meaningful.
type RateLimiter struct {
	mu      sync.Mutex
	buckets map[string]*rlBucket
	perHour int
}

type rlBucket struct {
	count    int
	resetsAt time.Time
}

// NewRateLimiter returns a limiter that allows perHour requests per IP in any
// rolling 1-hour window (resetting after the first request in a fresh window).
func NewRateLimiter(perHour int) *RateLimiter {
	return &RateLimiter{
		buckets: make(map[string]*rlBucket),
		perHour: perHour,
	}
}

// Allow consumes one allowance for ip; returns false when the window's quota
// is exhausted.
func (r *RateLimiter) Allow(ip string) bool {
	r.mu.Lock()
	defer r.mu.Unlock()
	now := time.Now()
	b := r.buckets[ip]
	if b == nil || now.After(b.resetsAt) {
		r.buckets[ip] = &rlBucket{count: 1, resetsAt: now.Add(time.Hour)}
		return true
	}
	if b.count >= r.perHour {
		return false
	}
	b.count++
	return true
}

// SweepStale drops buckets whose window has elapsed. Driven by
// helpers.StartCleanupLoop on a periodic schedule.
func (r *RateLimiter) SweepStale() {
	r.mu.Lock()
	defer r.mu.Unlock()
	now := time.Now()
	for ip, b := range r.buckets {
		if now.After(b.resetsAt) {
			delete(r.buckets, ip)
		}
	}
}
