<?php
declare(strict_types=1);

namespace App\Security;

use App\Services\SecurityEventMonitor;

final class RequestRateLimiter
{
    public static function enforce(string $method, string $path): void
    {
        if (!self::enabled()) return;
        $limit = self::limitFor($method, $path);
        if ($limit === null) return;

        try {
            $base = defined('BASE_PATH') ? BASE_PATH : dirname(__DIR__, 2);
            $directory = $base . '/storage/private/rate-limits';
            if (!is_dir($directory) && !mkdir($directory, 0770, true) && !is_dir($directory)) return;
            $window = 60;
            $bucket = intdiv(time(), $window);
            $ip = (string)($_SERVER['REMOTE_ADDR'] ?? 'unknown');
            $key = hash_hmac('sha256', $method . '|' . $path . '|' . $ip . '|' . $bucket, (string)($_ENV['RATE_LIMIT_HASH_SALT'] ?? 'axiom-local-rate-limit'));
            $file = $directory . '/' . $key . '.json';
            $handle = fopen($file, 'c+');
            if ($handle === false) return;
            try {
                if (!flock($handle, LOCK_EX)) return;
                $raw = stream_get_contents($handle);
                $state = is_string($raw) ? json_decode($raw, true) : null;
                $count = is_array($state) ? (int)($state['count'] ?? 0) + 1 : 1;
                ftruncate($handle, 0); rewind($handle);
                fwrite($handle, json_encode(['count' => $count, 'expires' => ($bucket + 1) * $window]));
                fflush($handle);
                @chmod($file, 0600);
            } finally {
                flock($handle, LOCK_UN); fclose($handle);
            }
            if ($count > $limit) {
                header('Retry-After: ' . max(1, (($bucket + 1) * $window) - time()));
                SecurityEventMonitor::record('request.rate_limited', 'warning', ['route' => $path]);
                http_response_code(429);
                header('Content-Type: text/plain; charset=utf-8');
                exit('Trop de requêtes. Réessayez dans un instant.');
            }
        } catch (\Throwable $error) {
            error_log('[AXIOM RATE LIMIT] ' . $error->getMessage());
        }
    }

    private static function enabled(): bool
    {
        $production = strtolower((string)($_ENV['APP_ENV'] ?? 'production')) === 'production';
        $value = filter_var($_ENV['RATE_LIMIT_ENABLED'] ?? $production, FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE);
        return $value ?? $production;
    }

    private static function limitFor(string $method, string $path): ?int
    {
        if ($path === '/login' && $method === 'POST') return self::envLimit('RATE_LIMIT_LOGIN_PER_MINUTE', 20);
        if (str_starts_with($path, '/api/')) return self::envLimit('RATE_LIMIT_API_PER_MINUTE', 120);
        if (str_contains($path, '/mises-a-jour') || str_contains($path, '/sauvegardes')) return self::envLimit('RATE_LIMIT_ADMIN_PER_MINUTE', 60);
        return null;
    }

    private static function envLimit(string $name, int $default): int
    {
        $value = filter_var($_ENV[$name] ?? $default, FILTER_VALIDATE_INT);
        return max(5, min(5000, $value === false ? $default : $value));
    }
}
