package com.ycwl.basic.utils.ratelimiter;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;


public class SlidingWindowRateLimiter implements IRateLimiter {
    private final Semaphore semaphore;
    private final ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(1);

    public SlidingWindowRateLimiter(int maxRequestsPerSecond) {
        this.semaphore = new Semaphore(maxRequestsPerSecond);
        for (int i = 0; i < maxRequestsPerSecond; i++) {
            semaphore.tryAcquire();
        }
        scheduler.scheduleAtFixedRate(() -> {
            if (semaphore.availablePermits() < maxRequestsPerSecond) {
                semaphore.release(maxRequestsPerSecond - semaphore.availablePermits());
            }
        }, 1, 1, TimeUnit.SECONDS);
    }

    public SlidingWindowRateLimiter(int maxRequests, int perSecond) {
        this.semaphore = new Semaphore(maxRequests);
        for (int i = 0; i < maxRequests; i++) {
            semaphore.tryAcquire();
        }
        scheduler.scheduleAtFixedRate(() -> {
            if (semaphore.availablePermits() < maxRequests) {
                semaphore.release(maxRequests - semaphore.availablePermits());
            }
        }, perSecond, perSecond, TimeUnit.SECONDS);
    }

    @Override
    public void acquire() throws InterruptedException {
        semaphore.acquire();
    }

    @Override
    public void shutdown() {
        scheduler.shutdown();
    }

    @Override
    public boolean tryAcquire() {
        return semaphore.tryAcquire();
    }

    @Override
    public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {
        return semaphore.tryAcquire(timeout, unit);
    }
}