//go:build !dev

package mw

import (
	"crussell/clock"
	"fmt"
	"net"
	"net/http"
	"time"
)

func NewRateLimiter(limit int, window time.Duration) *RateLimiter {
	rl := &RateLimiter{
		requests: make(map[string][]time.Time),
		limit:    limit,
		window:   window,
	}
	registerLimiter(rl)
	return rl
}

func (rl *RateLimiter) Allow(key string) bool {
	rl.mu.Lock()
	defer rl.mu.Unlock()
	now := clock.Now()
	windowStart := now.Add(-rl.window)

	var valid []time.Time
	for _, t := range rl.requests[key] {
		if t.After(windowStart) {
			valid = append(valid, t)
		}
	}

	if len(valid) >= rl.limit {
		rl.requests[key] = valid
		return false
	}

	rl.requests[key] = append(valid, now)
	return true
}

func NewProgressiveRateLimiter() *ProgressiveRateLimiter {
	return &ProgressiveRateLimiter{
		requests: make(map[string]*ipProgressiveState),
	}
}

// Check returns the delay in milliseconds. Returns 0 if no delay needed.
// Strategy:
//   - Count requests in last 5 seconds (burst): allow up to 30
//   - Count requests in last 60 seconds (sustained): allow up to 60
//   - Only delay when BOTH windows are exceeded (high sustained rate with recent bursts)
//   - Progressive: once throttled, delay increases with sustained rate
func (prl *ProgressiveRateLimiter) Check(ip string) (delayMs int) {
	prl.mu.Lock()
	defer prl.mu.Unlock()

	now := clock.Now()
	state, exists := prl.requests[ip]
	if !exists {
		prl.requests[ip] = &ipProgressiveState{
			timestamps: []time.Time{now},
		}
		return 0
	}

	state.timestamps = append(state.timestamps, now)

	burstCutoff := now.Add(-5 * time.Second)
	burstCount := 0
	for _, t := range state.timestamps {
		if t.After(burstCutoff) {
			burstCount++
		}
	}

	sustainedCutoff := now.Add(-60 * time.Second)
	sustainedCount := 0
	for _, t := range state.timestamps {
		if t.After(sustainedCutoff) {
			sustainedCount++
		}
	}

	if burstCount <= 30 && sustainedCount <= 120 {
		return 0
	}

	// Progressive delay based on how far over the sustained limit they are
	// Rate = requests per minute
	if sustainedCount <= 140 {
		return 500 // 500ms - scraping but not too aggressively
	} else if sustainedCount <= 200 {
		return 2000 // 2s - moderate spam
	} else if sustainedCount <= 300 {
		return 5000 // 5s - heavy spam
	} else {
		return 10000 // 10s - abuse
	}
}

func ProgressiveRateLimit(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ip := r.Header.Get("CF-Connecting-IP")
		if ip == "" {
			ip, _, _ = net.SplitHostPort(r.RemoteAddr)
			if ip == "" {
				ip = r.RemoteAddr
			}
		}

		delay := globalProgressiveLimiter.Check(ip)
		if delay > 0 {
			time.Sleep(time.Duration(delay) * time.Millisecond)
			w.Header().Set("X-RateLimit-Delay", fmt.Sprintf("%d", delay))
		}

		next.ServeHTTP(w, r)
	})
}

// RateLimit middleware - limits requests per IP
func RateLimit(limit int, window time.Duration) func(http.Handler) http.Handler {
	limiter := NewRateLimiter(limit, window)
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			ip := r.Header.Get("CF-Connecting-IP")
			if ip == "" {
				ip, _, _ = net.SplitHostPort(r.RemoteAddr)
				if ip == "" {
					ip = r.RemoteAddr
				}
			}

			if !limiter.Allow(ip) {
				http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
				return
			}

			next.ServeHTTP(w, r)
		})
	}
}
