/**
 * Browser 并发槽位限流器：standard / real 各自独立队列
 */
const { QUEUE_TIMEOUT_MS } = require('./constants');

class BrowserConcurrencyLimiter {
    /**
     * @param {number} max 最大并发 Browser 数
     */
    constructor(max) {
        this.max = max;
        this.active = 0;
        this.waiters = [];
    }

    /** @returns {{ max: number, active: number, queued: number }} */
    getStats() {
        return { max: this.max, active: this.active, queued: this.waiters.length };
    }

    /**
     * @param {string} taskName
     * @param {number} [timeoutMs]
     */
    async acquire(taskName, timeoutMs = QUEUE_TIMEOUT_MS) {
        if (this.active < this.max) {
            this.active++;
            console.log(`[并发槽位] ${taskName} 立即获取 (${this.active}/${this.max})`);
            return;
        }
        console.log(`[并发槽位] ${taskName} 排队等待 (队列 ${this.waiters.length + 1})`);
        await new Promise((resolve, reject) => {
            const entry = {
                resolve: () => {
                    this.active++;
                    console.log(`[并发槽位] ${taskName} 出队执行 (${this.active}/${this.max})`);
                    resolve();
                },
                reject,
            };
            if (timeoutMs > 0) {
                entry.timer = setTimeout(() => {
                    const idx = this.waiters.indexOf(entry);
                    if (idx >= 0) {
                        this.waiters.splice(idx, 1);
                        reject(new Error('QUEUE_TIMEOUT'));
                    }
                }, timeoutMs);
            }
            this.waiters.push(entry);
        });
    }

    /** @param {string} taskName */
    release(taskName) {
        this.active = Math.max(0, this.active - 1);
        const next = this.waiters.shift();
        if (next) {
            if (next.timer) clearTimeout(next.timer);
            next.resolve();
        } else {
            console.log(`[并发槽位] ${taskName} 释放 (${this.active}/${this.max})`);
        }
    }

    /**
     * @template T
     * @param {string} taskName
     * @param {() => Promise<T>} fn
     * @returns {Promise<T>}
     */
    async run(taskName, fn) {
        await this.acquire(taskName);
        try {
            return await fn();
        } finally {
            this.release(taskName);
        }
    }
}

module.exports = { BrowserConcurrencyLimiter };
