#pragma once

#include <util/generic/vector.h>
#include <util/datetime/base.h>

namespace NSrvKernel {
    class TRateLimiter {
    public:
        TRateLimiter() noexcept
            : Cur_(0)
            , RpsLimit_(0.)
        {
        }

        void SetLimit(double rpsLimit) noexcept {
            RpsLimit_ = rpsLimit;

            const TInstant now = TInstant::Now();
            const size_t sz = Min<size_t>(rpsLimit * 3, 10000);

            Times_.resize(sz);

            for (size_t i = 0; i < sz; ++i) {
                Times_[i] = now - TDuration::Seconds((sz - i) / rpsLimit);
            }

            Cur_ = 0;
        }

        double CalculateRate(TInstant reqStart) noexcept {
            if (Times_.empty()) {
                return 0;
            }

            reqStart = Max(reqStart, Times_[(Cur_ + Times_.size() - 1) % Times_.size()]);
            double currentRps = (double) TDuration::Seconds(Times_.size()).GetValue() /
                Max(reqStart - Times_[Cur_], TDuration::MicroSeconds(1)).GetValue();

            if (currentRps <= RpsLimit_) {
                Times_[Cur_] = reqStart;
                Cur_ = (Cur_ + 1) % Times_.size();
            }

            return currentRps;
        }

        bool ShouldAccept(TInstant reqStart) noexcept {
            return CalculateRate(reqStart) <= RpsLimit_;
        }

    private:
        TVector<TInstant> Times_;
        size_t Cur_;
        double RpsLimit_;
    };
}
