<?php
declare(strict_types=1);

namespace Celena\Core\Security;

/**
 * Файловый rate-limiter (без зависимостей). Простой счётчик попыток с TTL.
 * Для крупных проектов можно подменить на Redis-реализацию через bind() в контейнере.
 */
final class RateLimiter
{
    public function __construct(private readonly string $storageDir)
    {
        if (!is_dir($this->storageDir)) {
            @mkdir($this->storageDir, 0755, true);
        }
    }

    /**
     * Попытаться выполнить действие. Возвращает true если разрешено.
     */
    public function attempt(string $key, int $maxAttempts, int $windowSeconds): bool
    {
        $file = $this->path($key);
        $now = time();
        $data = $this->load($file);
        if ($data === null || $data['expires_at'] < $now) {
            $data = ['count' => 0, 'expires_at' => $now + $windowSeconds];
        }
        $data['count']++;
        $this->save($file, $data);
        return $data['count'] <= $maxAttempts;
    }

    public function attemptsLeft(string $key, int $maxAttempts): int
    {
        $data = $this->load($this->path($key));
        if ($data === null || $data['expires_at'] < time()) {
            return $maxAttempts;
        }
        return max(0, $maxAttempts - $data['count']);
    }

    public function reset(string $key): void
    {
        @unlink($this->path($key));
    }

    public function retryAfter(string $key): int
    {
        $data = $this->load($this->path($key));
        if ($data === null) {
            return 0;
        }
        return max(0, $data['expires_at'] - time());
    }

    private function path(string $key): string
    {
        return $this->storageDir . '/' . sha1($key) . '.rl';
    }

    /** @return array{count:int, expires_at:int}|null */
    private function load(string $file): ?array
    {
        if (!is_file($file)) {
            return null;
        }
        $raw = @file_get_contents($file);
        if ($raw === false) {
            return null;
        }
        $data = json_decode($raw, true);
        return is_array($data) && isset($data['count'], $data['expires_at']) ? $data : null;
    }

    private function save(string $file, array $data): void
    {
        @file_put_contents($file, json_encode($data), LOCK_EX);
    }
}
