package ratelimit

import (
	"sync"
	"time"

	"github.com/supabase/auth/internal/conf"
)

// IntervalLimiter will limit the number of calls to Allow per interval.
type IntervalLimiter struct {
	mu    sync.Mutex
	ival  time.Duration // Count is reset and time updated every ival.
	limit int           // Limit calls to Allow() per ival.

	// Guarded by mu.
	last  time.Time // When the limiter was last reset.
	count int       // Total calls to Allow() since time.
}

// NewIntervalLimiter returns a rate limiter using the given conf.Rate.
func NewIntervalLimiter(r conf.Rate) *IntervalLimiter {
	return &IntervalLimiter{
		ival:  r.OverTime,
		limit: int(r.Events),
		last:  time.Now(),
	}
}

// Allow implements Limiter by calling AllowAt with the current time.
func (rl *IntervalLimiter) Allow() bool {
	rl.mu.Lock()
	defer rl.mu.Unlock()

	return rl.allowAt(time.Now())
}

// AllowAt implements Limiter by checking if the current number of permitted
// events within this interval would permit 1 additional event at the current
// time.
//
// When called with a time outside the current active interval the counter is
// reset, meaning it can be vulnerable at the edge of it's intervals so avoid
// small intervals.
func (rl *IntervalLimiter) AllowAt(at time.Time) bool {
	rl.mu.Lock()
	defer rl.mu.Unlock()

	return rl.allowAt(at)
}

func (rl *IntervalLimiter) allowAt(at time.Time) bool {
	since := at.Sub(rl.last)
	if ivals := int64(since / rl.ival); ivals > 0 {
		rl.last = rl.last.Add(time.Duration(ivals) * rl.ival)
		rl.count = 0
	}
	if rl.count < rl.limit {
		rl.count++
		return true
	}
	return false
}
