import { describe, it, expect } from 'vitest'
import { Semaphore } from '../src/utils/semaphore.js'

describe('Semaphore', () => {
  it('throws on max < 1', () => {
    expect(() => new Semaphore(0)).toThrow()
  })

  it('exposes configured limit', () => {
    expect(new Semaphore(5).limit).toBe(5)
  })

  it('allows up to max concurrent holders', async () => {
    const sem = new Semaphore(2)
    let running = 0
    let peak = 0

    const work = async () => {
      await sem.acquire()
      running++
      peak = Math.max(peak, running)
      await new Promise((r) => setTimeout(r, 30))
      running--
      sem.release()
    }

    await Promise.all([work(), work(), work(), work()])
    expect(peak).toBeLessThanOrEqual(2)
  })

  it('run() auto-releases on success', async () => {
    const sem = new Semaphore(1)
    const result = await sem.run(async () => 42)
    expect(result).toBe(42)
    expect(sem.active).toBe(0)
  })

  it('run() auto-releases on error', async () => {
    const sem = new Semaphore(1)
    await expect(sem.run(async () => { throw new Error('oops') })).rejects.toThrow('oops')
    expect(sem.active).toBe(0)
  })

  it('tracks active and pending counts', async () => {
    const sem = new Semaphore(1)
    await sem.acquire()
    expect(sem.active).toBe(1)

    // This will queue
    const p = sem.acquire()
    expect(sem.pending).toBe(1)

    sem.release()
    await p
    expect(sem.active).toBe(1)
    expect(sem.pending).toBe(0)

    sem.release()
    expect(sem.active).toBe(0)
  })
})
