<?php

declare(strict_types=1);

namespace SiteFort\Includes;

use SiteFort\Modules\Firewall\RateLimiter\SlidingWindowStore;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

if ( ! class_exists( SlidingWindowStore::class ) ) {
	require_once SITEFORT_PLUGIN_DIR . 'modules/Firewall/RateLimiter/SlidingWindowStore.php';
}

class RestRateLimiter {
	private const CLEANUP_HOOK              = 'sitefort_rest_rate_limit_cleanup';
	private const CLEANUP_RETENTION_MINUTES = 60;
	private const HIT_TYPES                 = [
		'console_cmd',
		'console_cmd_status',
		'console_scan',
		'scanner_wakeup',
	];

	private static bool $hooks_registered = false;

	public static function register_hooks(): void {
		if ( self::$hooks_registered ) {
			return;
		}

		self::$hooks_registered = true;
		add_action( self::CLEANUP_HOOK, [ __CLASS__, 'cleanup' ] );
		if ( ! wp_next_scheduled( self::CLEANUP_HOOK ) ) {
			wp_schedule_event( time() + HOUR_IN_SECONDS, 'hourly', self::CLEANUP_HOOK );
		}
	}

	public static function check( string $hit_type, int $limit ): ?\WP_Error {
		$hit_type = sanitize_key( $hit_type );
		$limit    = max( 1, $limit );
		if ( '' === $hit_type ) {
			return null;
		}
		if ( ! in_array( $hit_type, self::HIT_TYPES, true ) || strlen( $hit_type ) > 20 ) {
			return new \WP_Error(
				'rate_limit_invalid_scope',
				'Invalid rate limit scope.',
				[ 'status' => 500 ]
			);
		}

		try {
			$ip_hash = hash( 'sha256', IpDetector::get_ip() );
			$store   = new SlidingWindowStore();
			$store->increment( $ip_hash, $hit_type );
			$hits = $store->get_count( $ip_hash, $hit_type, 1 );
		} catch ( \Throwable $e ) {
			return null;
		}

		if ( $hits > $limit ) {
			return new \WP_Error(
				'rate_limited',
				'Too many requests.',
				[ 'status' => 429 ]
			);
		}

		return null;
	}

	public static function cleanup(): void {
		try {
			( new SlidingWindowStore() )->purge_hit_types( self::HIT_TYPES, self::CLEANUP_RETENTION_MINUTES );
		} catch ( \Throwable $e ) {
			return;
		}
	}
}
