// Shared types, registration, and cleanup for RateLimiter + ProgressiveRateLimiter.
// Used by both production (!dev) and dev (dev) builds — keep tag-free.

package mw

import (
	"context"
	"crussell/clock"
	"sync"
	"time"
)

// RateLimiter implements a simple in-memory rate limiter
type RateLimiter struct {
	requests map[string][]time.Time
	mu       sync.RWMutex
	limit    int
	window   time.Duration
}

// ProgressiveRateLimiter implements per-IP rate limiting with increasing backoff.
// Designed for bot-spam prevention across accounts (not account-specific lockout).
type ProgressiveRateLimiter struct {
	requests map[string]*ipProgressiveState
	mu       sync.RWMutex
}

type ipProgressiveState struct {
	// Timestamps of all requests within the tracking window
	timestamps []time.Time
}

var (
	registeredLimiters   []*RateLimiter
	registeredLimitersMu sync.Mutex
)

func registerLimiter(rl *RateLimiter) {
	registeredLimitersMu.Lock()
	registeredLimiters = append(registeredLimiters, rl)
	registeredLimitersMu.Unlock()
}

// CleanupAllRateLimiters runs Cleanup on every registered RateLimiter.
// Called by the centralised jobs scheduler.
func CleanupAllRateLimiters(ctx context.Context) (int, error) {
	registeredLimitersMu.Lock()
	limiters := make([]*RateLimiter, len(registeredLimiters))
	copy(limiters, registeredLimiters)
	registeredLimitersMu.Unlock()
	for _, l := range limiters {
		l.Cleanup()
	}
	return 0, nil
}

// CleanupProgressiveRateLimiter runs Cleanup on the global progressive rate limiter.
// Called by the centralised jobs scheduler.
func CleanupProgressiveRateLimiter(ctx context.Context) (int, error) {
	globalProgressiveLimiter.Cleanup()
	return 0, nil
}

// Cleanup removes expired entries from the rate limiter map.
func (rl *RateLimiter) Cleanup() {
	rl.mu.Lock()
	defer rl.mu.Unlock()
	now := clock.Now()
	for key, times := range rl.requests {
		var valid []time.Time
		for _, t := range times {
			if now.Sub(t) < rl.window {
				valid = append(valid, t)
			}
		}
		if len(valid) == 0 {
			delete(rl.requests, key)
		} else {
			rl.requests[key] = valid
		}
	}
}

// Cleanup removes expired entries from the progressive rate limiter map.
func (prl *ProgressiveRateLimiter) Cleanup() {
	prl.mu.Lock()
	defer prl.mu.Unlock()
	cutoff := clock.Now().Add(-60 * time.Second)
	for ip, state := range prl.requests {
		var valid []time.Time
		for _, t := range state.timestamps {
			if t.After(cutoff) {
				valid = append(valid, t)
			}
		}
		if len(valid) == 0 {
			delete(prl.requests, ip)
		} else {
			state.timestamps = valid
		}
	}
}

var globalProgressiveLimiter = NewProgressiveRateLimiter()
