<?php

namespace SHServ\Tools;

class RateLimiter {
	protected int $maxRequests;
	protected int $windowSeconds;
	protected string $storagePath;

	public function __construct(int $maxRequests = 60, int $windowSeconds = 60, ?string $storagePath = null) {
		$this -> maxRequests = $maxRequests;
		$this -> windowSeconds = $windowSeconds;
		$this -> storagePath = $storagePath ?? dirname(__DIR__, 2) . '/Cache/rate-limit.json';
		$this -> ensureStorage();
	}

	protected function ensureStorage(): void {
		$dir = dirname($this -> storagePath);
		if (!is_dir($dir)) {
			mkdir($dir, 0750, true);
		}
		if (!file_exists($this -> storagePath)) {
			file_put_contents($this -> storagePath, '{}');
			chmod($this -> storagePath, 0640);
		}
	}

	public function check(String $key): bool {
		if (extension_loaded('apcu') && apcu_enabled()) {
			return $this -> checkApcu($key);
		}
		return $this -> checkFile($key);
	}

	protected function checkApcu(String $key): bool {
		$cacheKey = 'rate_limit:' . $key;
		$requests = apcu_fetch($cacheKey);
		if ($requests === false) {
			$requests = [];
		}
		$now = time();
		$requests = array_filter($requests, function($ts) use ($now) {
			return $now - $ts < $this -> windowSeconds;
		});
		if (count($requests) >= $this -> maxRequests) {
			apcu_store($cacheKey, array_values($requests), $this -> windowSeconds);
			return false;
		}
		$requests[] = $now;
		apcu_store($cacheKey, array_values($requests), $this -> windowSeconds);
		return true;
	}

	protected function checkFile(String $key): bool {
		$fp = fopen($this -> storagePath, 'c+');
		if (!$fp) {
			return true;
		}
		if (!flock($fp, LOCK_EX)) {
			fclose($fp);
			return true;
		}

		$data = json_decode(stream_get_contents($fp), true) ?: [];
		$now = time();
		if (!isset($data[$key])) {
			$data[$key] = [];
		}
		$data[$key] = array_values(array_filter($data[$key], function($ts) use ($now) {
			return $now - $ts < $this -> windowSeconds;
		}));

		$allowed = count($data[$key]) < $this -> maxRequests;
		if ($allowed) {
			$data[$key][] = $now;
		}

		ftruncate($fp, 0);
		rewind($fp);
		fwrite($fp, json_encode($data));
		fflush($fp);
		flock($fp, LOCK_UN);
		fclose($fp);

		return $allowed;
	}

	public function clear(): void {
		if (extension_loaded('apcu') && apcu_enabled()) {
			apcu_delete('rate_limit:');
			return;
		}
		$fp = fopen($this -> storagePath, 'c+');
		if (!$fp) {
			return;
		}
		if (flock($fp, LOCK_EX)) {
			ftruncate($fp, 0);
			rewind($fp);
			fwrite($fp, '{}');
			fflush($fp);
			flock($fp, LOCK_UN);
		}
		fclose($fp);
	}
}
