import { describe, expect, test } from "bun:test"
import { Lock } from "../../src/util"

function tick() {
  return new Promise<void>((r) => queueMicrotask(r))
}

async function flush(n = 5) {
  for (let i = 0; i < n; i++) await tick()
}

describe("util.lock", () => {
  test("writer exclusivity: blocks reads and other writes while held", async () => {
    const key = "lock:" + Math.random().toString(36).slice(2)

    const state = {
      writer2: false,
      reader: false,
      writers: 0,
    }

    // Acquire writer1
    using writer1 = await Lock.write(key)
    state.writers++
    expect(state.writers).toBe(1)

    // Start writer2 candidate (should block)
    const writer2Task = (async () => {
      const w = await Lock.write(key)
      state.writers++
      expect(state.writers).toBe(1)
      state.writer2 = true
      // Hold for a tick so reader cannot slip in
      await tick()
      return w
    })()

    // Start reader candidate (should block)
    const readerTask = (async () => {
      const r = await Lock.read(key)
      state.reader = true
      return r
    })()

    // Flush microtasks and assert neither acquired
    await flush()
    expect(state.writer2).toBe(false)
    expect(state.reader).toBe(false)

    // Release writer1
    writer1[Symbol.dispose]()
    state.writers--

    // writer2 should acquire next
    const writer2 = await writer2Task
    expect(state.writer2).toBe(true)

    // Reader still blocked while writer2 held
    await flush()
    expect(state.reader).toBe(false)

    // Release writer2
    writer2[Symbol.dispose]()
    state.writers--

    // Reader should now acquire
    const reader = await readerTask
    expect(state.reader).toBe(true)

    reader[Symbol.dispose]()
  })
})
