<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\Database;

/**
 * RateLimiter
 *
 * Simple sliding-window limiter: "has this key had more than N hits in
 * the last M minutes?" Backed by the rate_limit_hits table so it works
 * correctly even across multiple PHP-FPM workers/app servers (unlike
 * an in-memory counter). Named limiter configs live in
 * config/security.php under 'rate_limits'.
 */
final class RateLimiter
{
    public static function tooManyAttempts(string $key, int $maxAttempts, int $decayMinutes): bool
    {
        return self::attempts($key, $decayMinutes) >= $maxAttempts;
    }

    public static function attempts(string $key, int $decayMinutes): int
    {
        $stmt = Database::connection()->prepare(
            'SELECT COUNT(*) FROM rate_limit_hits WHERE limiter_key = :key AND created_at > :cutoff'
        );
        $stmt->execute([
            'key' => $key,
            'cutoff' => date('Y-m-d H:i:s', time() - ($decayMinutes * 60)),
        ]);

        return (int) $stmt->fetchColumn();
    }

    public static function hit(string $key): void
    {
        $stmt = Database::connection()->prepare(
            'INSERT INTO rate_limit_hits (limiter_key, created_at) VALUES (:key, NOW())'
        );
        $stmt->execute(['key' => $key]);
    }

    public static function remaining(string $key, int $maxAttempts, int $decayMinutes): int
    {
        return max(0, $maxAttempts - self::attempts($key, $decayMinutes));
    }

    public static function clear(string $key): void
    {
        $stmt = Database::connection()->prepare('DELETE FROM rate_limit_hits WHERE limiter_key = :key');
        $stmt->execute(['key' => $key]);
    }

    /** Housekeeping — call from a scheduled task so the table doesn't grow forever. */
    public static function purgeOlderThan(int $hours = 24): int
    {
        $stmt = Database::connection()->prepare(
            'DELETE FROM rate_limit_hits WHERE created_at < :cutoff'
        );
        $stmt->execute(['cutoff' => date('Y-m-d H:i:s', time() - ($hours * 3600))]);

        return $stmt->rowCount();
    }
}
