<?php
/**
 * Rate Limiter for High Concurrency
 * Protects against abuse and ensures fair resource allocation
 */
class RateLimiter {
    private static string $storagePath;
    private static array $limits = [
        'default' => ['requests' => 60, 'window' => 60], // 60 requests per minute
        'login' => ['requests' => 5, 'window' => 300], // 5 login attempts per 5 minutes
        'api' => ['requests' => 100, 'window' => 60], // 100 API requests per minute
        'heavy' => ['requests' => 10, 'window' => 60], // 10 heavy operations per minute
    ];
    
    public static function init(): void {
        self::$storagePath = defined('RATELIMIT_PATH') ? RATELIMIT_PATH : (APP_ROOT . '/storage/ratelimit');
        if (!is_dir(self::$storagePath)) {
            mkdir(self::$storagePath, 0755, true);
        }
    }
    
    /**
     * Check if request is allowed
     */
    public static function attempt(string $identifier, string $type = 'default'): bool {
        self::init();
        
        $limit = self::$limits[$type] ?? self::$limits['default'];
        $key = self::getKey($identifier, $type);
        $file = self::$storagePath . '/' . $key . '.json';
        
        $data = self::getData($file);
        $now = time();
        
        // Clean old entries outside the window
        $data = array_filter($data, fn($timestamp) => $timestamp > $now - $limit['window']);
        
        // Check if limit exceeded
        if (count($data) >= $limit['requests']) {
            return false;
        }
        
        // Add current request
        $data[] = $now;
        self::saveData($file, $data);
        
        return true;
    }
    
    /**
     * Get remaining requests
     */
    public static function remaining(string $identifier, string $type = 'default'): int {
        self::init();
        
        $limit = self::$limits[$type] ?? self::$limits['default'];
        $key = self::getKey($identifier, $type);
        $file = self::$storagePath . '/' . $key . '.json';
        
        $data = self::getData($file);
        $now = time();
        
        // Clean old entries outside the window
        $data = array_filter($data, fn($timestamp) => $timestamp > $now - $limit['window']);
        
        return max(0, $limit['requests'] - count($data));
    }
    
    /**
     * Get reset time (when the oldest request will expire)
     */
    public static function resetAt(string $identifier, string $type = 'default'): int {
        self::init();
        
        $limit = self::$limits[$type] ?? self::$limits['default'];
        $key = self::getKey($identifier, $type);
        $file = self::$storagePath . '/' . $key . '.json';
        
        $data = self::getData($file);
        
        if (empty($data)) {
            return 0;
        }
        
        return min($data) + $limit['window'];
    }
    
    /**
     * Clear rate limit for identifier
     */
    public static function clear(string $identifier, string $type = 'default'): void {
        self::init();
        
        $key = self::getKey($identifier, $type);
        $file = self::$storagePath . '/' . $key . '.json';
        
        if (file_exists($file)) {
            @unlink($file);
        }
    }
    
    /**
     * Clean up old rate limit files
     */
    public static function cleanup(): int {
        self::init();
        
        $count = 0;
        $files = glob(self::$storagePath . '/*.json');
        $now = time();
        
        foreach ($files as $file) {
            if ($now - filemtime($file) > 3600) { // Remove files older than 1 hour
                @unlink($file);
                $count++;
            }
        }
        
        return $count;
    }
    
    private static function getKey(string $identifier, string $type): string {
        return md5($identifier . ':' . $type);
    }
    
    private static function getData(string $file): array {
        if (!file_exists($file)) {
            return [];
        }
        
        $content = file_get_contents($file);
        return $content ? json_decode($content, true) : [];
    }
    
    private static function saveData(string $file, array $data): void {
        file_put_contents($file, json_encode($data), LOCK_EX);
    }
    
    /**
     * Set custom limit for a type
     */
    public static function setLimit(string $type, int $requests, int $window): void {
        self::$limits[$type] = ['requests' => $requests, 'window' => $window];
    }
}
