import assert from 'node:assert/strict';
import test from 'node:test';
import { AsyncSemaphore, PerKeySerialQueue } from '../src/concurrency.js';

test('semaphore caps concurrent operations', async () => {
  const semaphore = new AsyncSemaphore(2);
  let active = 0;
  let peak = 0;
  const operations = Array.from({ length: 6 }, (_, index) => semaphore.run(async () => {
    active += 1;
    peak = Math.max(peak, active);
    await new Promise((resolve) => setTimeout(resolve, 5));
    active -= 1;
    return index;
  }));
  assert.deepEqual(await Promise.all(operations), [0, 1, 2, 3, 4, 5]);
  assert.equal(peak, 2);
});

test('per-key queue serializes one worker but allows different workers concurrently', async () => {
  const queue = new PerKeySerialQueue();
  const order: string[] = [];
  let workerAActive = 0;
  let workerAPeak = 0;
  let globalPeak = 0;
  let globalActive = 0;

  const run = (key: string, label: string, delay: number) => queue.run(key, async () => {
    globalActive += 1;
    globalPeak = Math.max(globalPeak, globalActive);
    if (key === 'a') {
      workerAActive += 1;
      workerAPeak = Math.max(workerAPeak, workerAActive);
    }
    order.push(`start:${label}`);
    await new Promise((resolve) => setTimeout(resolve, delay));
    order.push(`end:${label}`);
    if (key === 'a') workerAActive -= 1;
    globalActive -= 1;
  });

  await Promise.all([run('a', 'a1', 15), run('a', 'a2', 1), run('b', 'b1', 5)]);
  assert.equal(workerAPeak, 1);
  assert.ok(globalPeak >= 2);
  assert.ok(order.indexOf('end:a1') < order.indexOf('start:a2'));
});
