<?php

namespace Velis\RateLimiter;

/**
 * @author Szymon Janaczek <szymon.janaczek@singu.com>
 * @phpstan-type RouteConfig = array{max_attempts: int, decay_seconds: int, leak_rate: int}
 */
final class RateLimitRouteWrapper implements RateLimitRouteWrapperInterface
{
    public function __construct(
        private readonly LeakyBucketRateLimiterInterface $rateLimiter,
        private readonly array $pathsConfiguration,
    ) {
    }

    /**
     * @throws TooManyAttemptsException
     */
    public function checkUri(string $currentUri): void
    {
        foreach (array_reverse($this->pathsConfiguration) as $pathPattern => $routeConfigurationString) {
            if (!$this->doesRouteMatch($currentUri, $pathPattern)) {
                continue;
            }

            $routeConfiguration = $this->parseRouteConfig($routeConfigurationString);
            $this->attemptUri($pathPattern, $routeConfiguration);

            return;
        }
    }

    public function doesRouteMatch(string $currentUri, string $pathPattern): bool
    {
        $currentUri = mb_strtolower(rtrim($currentUri, '/'));
        $pathPattern = mb_strtolower(rtrim($pathPattern, '/'));

        if (str_contains($pathPattern, '/**')) {
            $pathPattern = str_replace('**', '.*', $pathPattern);
        }

        if (str_contains($pathPattern, '/*')) {
            $pathPattern = str_replace('*', '[^/]+', $pathPattern);
        }

        return $currentUri === $pathPattern || preg_match('#^' . $pathPattern . '$#', $currentUri);
    }

    /**
     * @param RouteConfig $routeConfiguration
     * @throws TooManyAttemptsException
     */
    public function attemptUri(string $pathPattern, array $routeConfiguration): void
    {
        $this
            ->rateLimiter
            ->attemptByIdentifier(
                rateLimit: $routeConfiguration['max_attempts'],
                leakRate: $routeConfiguration['leak_rate'],
                key: $pathPattern,
                decaySeconds: $routeConfiguration['decay_seconds']
            )
        ;
    }

    /**
     * @param string $configurationString format: `max attempts per period,decay seconds,leak rate`, example: `100,60,1`
     * @return RouteConfig
     */
    public function parseRouteConfig(string $configurationString): array
    {
        $config = explode(',', $configurationString);

        return [
            'max_attempts' => (int) $config[0] ?: 1,
            'decay_seconds' => (int) $config[1] ?: 1,
            'leak_rate' => (float) $config[2] ?: 1,
        ];
    }
}
