<?php

declare(strict_types=1);

namespace Gls\GlsPoland\Soap\Client\Caller;

use Gls\GlsPoland\Soap\Client\Type\RequestInterface;
use Gls\GlsPoland\Soap\Client\Type\ResultInterface;

final class RateLimitingCaller implements CallerInterface
{
    private $caller;
    private $maxCallsPerSecond;
    private $minIntervalMicros;
    private $lastCallAtMicros;

    public function __construct(CallerInterface $caller, int $maxCallsPerSecond)
    {
        if ($maxCallsPerSecond <= 0) {
            throw new \InvalidArgumentException('The max calls per second must be greater than zero.');
        }

        $this->caller = $caller;
        $this->maxCallsPerSecond = $maxCallsPerSecond;
        $this->minIntervalMicros = (int) floor(1000000 / $maxCallsPerSecond);
        $this->lastCallAtMicros = null;
    }

    public function call(string $method, RequestInterface $request): ResultInterface
    {
        $this->throttle();

        return $this->caller->call($method, $request);
    }

    private function throttle(): void
    {
        $now = (int) floor(microtime(true) * 1000000);

        if (null === $this->lastCallAtMicros) {
            $this->lastCallAtMicros = $now;

            return;
        }

        $elapsed = $now - $this->lastCallAtMicros;
        $sleepFor = $this->minIntervalMicros - $elapsed;
        if ($sleepFor > 0) {
            usleep($sleepFor);
            $now = (int) floor(microtime(true) * 1000000);
        }

        $this->lastCallAtMicros = $now;
    }
}
