#include <caracal/rate_limiter.hpp>
#include <catch2/catch_test_macros.hpp>
#include <chrono>
#include <iostream>

using caracal::RateLimiter;
using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::chrono::steady_clock;

template <typename F>
milliseconds measure_time(F lambda) {
  auto start = steady_clock::now();
  lambda();
  auto stop = steady_clock::now();
  return duration_cast<milliseconds>(stop - start);
}

TEST_CASE("RateLimiter") {
  SECTION("750 packets at 500pps should take at-least 1.5s") {
    RateLimiter rl{500};
    auto delta = measure_time([&rl]() {
      for (auto i = 0; i < 750; i++) {
        rl.wait();
      }
    });
    // NOTE: We use `.count()` to allow Catch2 to show
    // the values if the assertion fails.
    REQUIRE(delta.count() >= milliseconds{1250}.count());
    REQUIRE(delta.count() <= milliseconds{2000}.count());
  }

  SECTION("750 packets at 500pps should take at-least 1.5s (steps = 10)") {
    RateLimiter rl{500, 10};
    auto delta = measure_time([&rl]() {
      for (auto i = 0; i < 75; i++) {
        rl.wait();
      }
    });
    REQUIRE(delta.count() >= milliseconds{1250}.count());
    REQUIRE(delta.count() <= milliseconds{2000}.count());
  }

  SECTION("50k packets at 100k pps should take at-least 0.5s") {
    RateLimiter rl{100000};
    auto delta = measure_time([&rl]() {
      for (auto i = 0; i < 50000; i++) {
        rl.wait();
      }
    });
    REQUIRE(delta.count() >= milliseconds{450}.count());
    REQUIRE(delta.count() <= milliseconds{1000}.count());
  }

  SECTION("50k packets at 100k pps should take at-least 0.5s (steps = 100)") {
    RateLimiter rl{100000, 100};
    auto delta = measure_time([&rl]() {
      for (auto i = 0; i < 500; i++) {
        rl.wait();
      }
    });
    REQUIRE(delta.count() >= milliseconds{450}.count());
    REQUIRE(delta.count() <= milliseconds{1000}.count());
  }

  SECTION("Invalid arguments") { REQUIRE_THROWS(RateLimiter{0}); }
}
