<?php
/**
 * Rate limiter — Redis primary, no-op fallback.
 *
 * Usage:
 *   RateLimit::hit('login:'.$ip, 10, 900);  // 10 attempts per 15 min
 *   RateLimit::hit('signup:'.$ip, 5, 3600); // 5 per hour
 */

declare(strict_types=1);

class RateLimit
{
    /**
     * Increment counter for a key. Aborts request if limit exceeded.
     *
     * @param string $key      Logical key (e.g. "login:1.2.3.4")
     * @param int    $max      Max hits in window
     * @param int    $window   Window length in seconds
     * @param string $errorMsg Optional override message
     */
    public static function hit(string $key, int $max, int $window, string $errorMsg = ''): void
    {
        $r = redis();
        if (!$r) return;  // graceful degrade

        try {
            $cacheKey = 'rl:' . sha1($key);
            $count = $r->incr($cacheKey);
            if ($count === 1) $r->expire($cacheKey, $window);

            if ($count > $max) {
                $ttl = $r->ttl($cacheKey);
                $msg = $errorMsg !== ''
                    ? $errorMsg
                    : "Too many attempts. Try again in {$ttl} seconds.";
                jsonError($msg, 429);
            }
        } catch (Throwable $e) {
            Logger::warning('Rate limiter error', ['error' => $e->getMessage()]);
        }
    }

    /**
     * Check current count without incrementing.
     */
    public static function count(string $key): int
    {
        $r = redis();
        if (!$r) return 0;
        try {
            $val = $r->get('rl:' . sha1($key));
            return (int)$val;
        } catch (Throwable) {
            return 0;
        }
    }

    /**
     * Reset a key (e.g. after successful login).
     */
    public static function reset(string $key): void
    {
        $r = redis();
        if (!$r) return;
        try {
            $r->del('rl:' . sha1($key));
        } catch (Throwable) { /* ignore */ }
    }
}
