package httpapi

// ratelimit.go — Einfaches In-Memory-Rate-Limiting für POST /login (N1).
//
// Implementierung: Sliding-Window-Counter pro IP-Adresse.
// Erlaubt maximal loginMaxAttempts Versuche pro loginWindow.
// Ältere Einträge werden periodisch aus der Map bereinigt.

import (
	"net"
	"net/http"
	"sync"
	"time"
)

const (
	loginMaxAttempts = 5
	loginWindow      = 1 * time.Minute
	cleanupInterval  = 5 * time.Minute
)

type loginAttempt struct {
	count     int
	windowEnd time.Time
}

type loginRateLimiter struct {
	mu      sync.Mutex
	entries map[string]*loginAttempt
}

func newLoginRateLimiter() *loginRateLimiter {
	rl := &loginRateLimiter{
		entries: make(map[string]*loginAttempt),
	}
	go rl.cleanup()
	return rl
}

// Allow returns true if the IP is within the rate limit, false if it should be blocked.
func (rl *loginRateLimiter) Allow(ip string) bool {
	rl.mu.Lock()
	defer rl.mu.Unlock()

	now := time.Now()
	e, ok := rl.entries[ip]
	if !ok || now.After(e.windowEnd) {
		// Neues Fenster.
		rl.entries[ip] = &loginAttempt{count: 1, windowEnd: now.Add(loginWindow)}
		return true
	}
	e.count++
	return e.count <= loginMaxAttempts
}

// cleanup bereinigt abgelaufene Einträge periodisch.
func (rl *loginRateLimiter) cleanup() {
	ticker := time.NewTicker(cleanupInterval)
	defer ticker.Stop()
	for range ticker.C {
		rl.mu.Lock()
		now := time.Now()
		for ip, e := range rl.entries {
			if now.After(e.windowEnd) {
				delete(rl.entries, ip)
			}
		}
		rl.mu.Unlock()
	}
}

// LoginRateLimit ist eine globale Instanz des Rate-Limiters (package-level Singleton).
var LoginRateLimit = newLoginRateLimiter()

// RateLimitLogin ist Middleware, die Brute-Force-Angriffe auf den Login-Endpoint verhindert.
// Bei Überschreitung wird 429 Too Many Requests zurückgegeben.
func RateLimitLogin(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// IP-Adresse extrahieren (berücksichtigt X-Forwarded-For nicht, um Spoofing zu vermeiden).
		ip, _, err := net.SplitHostPort(r.RemoteAddr)
		if err != nil {
			ip = r.RemoteAddr
		}

		if !LoginRateLimit.Allow(ip) {
			http.Error(w, "Zu viele Anmeldeversuche. Bitte warte eine Minute.", http.StatusTooManyRequests)
			return
		}
		next.ServeHTTP(w, r)
	})
}
