<?php
declare(strict_types=1);

namespace App\Core;

final class RateLimiter
{
    public static function tooManyAttempts(string $key, int $maxAttempts, int $decaySeconds): bool
    {
        $attempt = self::record($key, false, $decaySeconds);

        if ($attempt === null) {
            return false;
        }

        return $attempt['count'] >= $maxAttempts && (time() - $attempt['first_attempt_at']) < $decaySeconds;
    }

    public static function hit(string $key, int $decaySeconds = 900): void
    {
        self::record($key, true, $decaySeconds);
    }

    public static function clear(string $key): void
    {
        $data = self::all();
        unset($data[$key]);
        self::save($data);
    }

    public static function availableIn(string $key, int $decaySeconds): int
    {
        $attempt = self::record($key, false, $decaySeconds);
        if ($attempt === null) {
            return 0;
        }

        $elapsed = time() - $attempt['first_attempt_at'];
        if ($elapsed >= $decaySeconds) {
            return 0;
        }

        return $decaySeconds - $elapsed;
    }

    private static function record(string $key, bool $increment = false, int $decaySeconds = 900): ?array
    {
        $data = self::all();
        $now = time();

        if (! isset($data[$key])) {
            if (! $increment) {
                return null;
            }

            $data[$key] = [
                'count' => 1,
                'first_attempt_at' => $now,
                'last_attempt_at' => $now,
            ];
            self::save($data);

            return $data[$key];
        }

        if (($now - $data[$key]['first_attempt_at']) >= $decaySeconds) {
            unset($data[$key]);
            self::save($data);

            if (! $increment) {
                return null;
            }

            return self::record($key, true);
        }

        if ($increment) {
            $data[$key]['count']++;
            $data[$key]['last_attempt_at'] = $now;
            self::save($data);
        }

        return $data[$key];
    }

    private static function all(): array
    {
        $path = self::path();
        if (! is_file($path)) {
            return [];
        }

        $content = file_get_contents($path);
        if ($content === false || $content === '') {
            return [];
        }

        $decoded = json_decode($content, true);

        return is_array($decoded) ? $decoded : [];
    }

    private static function save(array $data): void
    {
        $path = self::path();
        $directory = dirname($path);
        if (! is_dir($directory)) {
            mkdir($directory, 0775, true);
        }

        file_put_contents($path, json_encode($data, JSON_UNESCAPED_SLASHES), LOCK_EX);
    }

    private static function path(): string
    {
        return dirname(__DIR__) . '/storage/cache/rate_limit.json';
    }
}
