"use strict";
// SPDX-FileCopyrightText: 2024 - 2025 Gnuxie <Gnuxie@protonmail.com>
//
// SPDX-License-Identifier: Apache-2.0
Object.defineProperty(exports, "__esModule", { value: true });
exports.LazyLeakyBucket = void 0;
/**
 * A lazy version of the bucket to be used when the throuhgput is really
 * low most of the time, so doesn't warrant constant filling/leaking.
 *
 * This won't be good to use in a high throughput situation because
 * of the way it will spam calling for the current time.
 */
class LazyLeakyBucket {
    capacity;
    timescale;
    buckets = new Map();
    leakDelta;
    isDisposed = false;
    leakCycleTimeout = null;
    constructor(capacity, timescale) {
        this.capacity = capacity;
        this.timescale = timescale;
        this.leakDelta = this.timescale / this.capacity;
        this.startLeakCycle();
    }
    getAllTokens() {
        const map = new Map();
        for (const key of this.buckets.keys()) {
            map.set(key, this.getTokenCount(key));
        }
        return map;
    }
    leak(now, key, entry) {
        const elapsed = now.getTime() - entry.lastLeak.getTime();
        const tokensToRemove = Math.floor(elapsed / this.timescale);
        entry.tokens = Math.max(entry.tokens - tokensToRemove, 0);
        entry.lastLeak = new Date(entry.lastLeak.getTime() + tokensToRemove * this.leakDelta);
        if (entry.tokens < 1) {
            this.buckets.delete(key);
        }
    }
    addToken(key) {
        const now = new Date();
        const entry = this.buckets.get(key);
        if (entry === undefined) {
            this.buckets.set(key, {
                tokens: 1,
                lastLeak: now,
            });
            return 1;
        }
        entry.tokens += 1;
        this.leak(now, key, entry);
        return entry.tokens;
    }
    getTokenCount(key) {
        const now = new Date();
        const entry = this.buckets.get(key);
        if (entry === undefined) {
            return 0;
        }
        this.leak(now, key, entry);
        return entry.tokens;
    }
    leakAll() {
        const now = new Date();
        for (const [key, entry] of this.buckets.entries()) {
            this.leak(now, key, entry);
        }
    }
    /**
     * Periodically leak all of the buckets to prevent memory leaks from leftover
     * keys.
     */
    startLeakCycle() {
        if (this.isDisposed) {
            return;
        }
        this.leakCycleTimeout = setTimeout(() => {
            this.leakAll();
            this.startLeakCycle();
        }, this.timescale);
    }
    stop() {
        this.isDisposed = true;
        if (this.leakCycleTimeout) {
            clearTimeout(this.leakCycleTimeout);
        }
    }
}
exports.LazyLeakyBucket = LazyLeakyBucket;
//# sourceMappingURL=LeakyBucket.js.map