package de.effigenix.infrastructure.security;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.util.concurrent.ConcurrentHashMap;

/**
 * In-memory rate limiter for login attempts.
 * Prevents brute-force attacks by limiting failed login attempts per IP/username.
 *
 * Configuration:
 * - Max 5 attempts per 15 minutes per key (IP or username)
 * - Automatic cleanup of expired entries every 5 minutes
 *
 * For production: consider Redis-based rate limiting for distributed environments.
 */
@Component
public class LoginRateLimiter {

    private static final int MAX_ATTEMPTS = 5;
    private static final long WINDOW_MS = 15 * 60 * 1000L; // 15 minutes

    private final ConcurrentHashMap<String, AttemptRecord> attempts = new ConcurrentHashMap<>();

    /**
     * Records a failed login attempt for the given key (e.g., IP address or username).
     */
    public void recordFailedAttempt(String key) {
        attempts.compute(key, (k, existing) -> {
            Instant now = Instant.now();
            if (existing == null || existing.windowStart.plusMillis(WINDOW_MS).isBefore(now)) {
                return new AttemptRecord(now, 1);
            }
            return new AttemptRecord(existing.windowStart, existing.count + 1);
        });
    }

    /**
     * Checks if the given key is rate-limited (too many failed attempts).
     */
    public boolean isRateLimited(String key) {
        AttemptRecord record = attempts.get(key);
        if (record == null) return false;

        Instant now = Instant.now();
        if (record.windowStart.plusMillis(WINDOW_MS).isBefore(now)) {
            attempts.remove(key);
            return false;
        }

        return record.count >= MAX_ATTEMPTS;
    }

    /**
     * Resets the attempt counter for a key (e.g., after successful login).
     */
    public void resetAttempts(String key) {
        attempts.remove(key);
    }

    /**
     * Scheduled cleanup of expired attempt records.
     * Runs every 5 minutes.
     */
    @Scheduled(fixedRate = 300_000)
    public void cleanupExpiredEntries() {
        Instant now = Instant.now();
        attempts.entrySet().removeIf(entry ->
                entry.getValue().windowStart.plusMillis(WINDOW_MS).isBefore(now));
    }

    private record AttemptRecord(Instant windowStart, int count) {}
}
