"""Offline tests for priority_limit_async_func_call's cross-worker global
concurrency mode (concurrency_group + shared-storage gating).

Single-process shared storage exercises the exact code paths used under
gunicorn multi-worker mode (namespaces and keyed locks degrade to in-process
primitives).
"""

import asyncio

import pytest

from lightrag.kg import shared_storage as ss

# Accessed via the module (not ``from``-imports) on purpose: another test in
# the suite reloads lightrag.utils in place, which would leave from-imported
# class references (e.g. QueueFullError) pointing at the pre-reload class and
# break pytest.raises identity checks when run in the same xdist worker.
from lightrag import utils as lr_utils

pytestmark = pytest.mark.offline


GROUP = "llm:test"


@pytest.fixture(autouse=True)
def clean_shared_storage():
    ss.finalize_share_data()
    yield
    ss.finalize_share_data()


def _init(limits=None):
    ss.initialize_share_data(1, global_concurrency_limits=limits)


async def _wait_until(predicate, timeout=5.0, interval=0.01):
    deadline = asyncio.get_running_loop().time() + timeout
    while not predicate():
        if asyncio.get_running_loop().time() > deadline:
            raise AssertionError("condition not met within timeout")
        await asyncio.sleep(interval)


async def _wait_drained(wrapped, timeout=5.0, interval=0.01):
    """Poll until the wrapper's accounting fully quiesces to zero.

    Asserts the live_queued exactly-once invariant at its strongest point:
    once every task has drained, the logical reservation counter
    (``queued`` == live_queued), the physical queue, and task_states
    (``in_flight``) must all be back to zero. A leaked reservation
    (up-drift) leaves ``queued`` stuck above zero forever; a leaked
    task_states entry leaves ``in_flight`` stuck. Returns the final stats.
    """
    deadline = asyncio.get_running_loop().time() + timeout
    while True:
        stats = await wrapped.get_queue_stats()
        if (
            stats["queued"] == 0
            and stats["in_flight"] == 0
            and stats.get("physical_queued", 0) == 0
        ):
            return stats
        if asyncio.get_running_loop().time() > deadline:
            raise AssertionError(f"queue did not drain to zero: {stats}")
        await asyncio.sleep(interval)


# ---------------------------------------------------------------------------
# Core: global cap on in-flight executions
# ---------------------------------------------------------------------------


async def test_global_limit_caps_inflight_executions():
    _init({GROUP: 3})
    inflight = 0
    peak = 0

    async def slow_func(value):
        nonlocal inflight, peak
        inflight += 1
        peak = max(peak, inflight)
        await asyncio.sleep(0.05)
        inflight -= 1
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        10, queue_name="cap test", concurrency_group=GROUP
    )(slow_func)
    try:
        results = await asyncio.gather(*(wrapped(i) for i in range(10)))
        assert sorted(results) == list(range(10))
        assert peak <= 3
        # Slots all returned after completion.
        assert await ss.global_concurrency_in_use(GROUP) == 0
    finally:
        await wrapped.shutdown()


async def test_limit_shared_across_wrappers_of_same_group():
    """Two wrappers (≈ two processes / instances) share one global gate."""
    _init({GROUP: 1})
    inflight = 0
    peak = 0

    async def slow_func(value):
        nonlocal inflight, peak
        inflight += 1
        peak = max(peak, inflight)
        await asyncio.sleep(0.05)
        inflight -= 1
        return value

    wrapped_a = lr_utils.priority_limit_async_func_call(
        4, queue_name="share a", concurrency_group=GROUP
    )(slow_func)
    wrapped_b = lr_utils.priority_limit_async_func_call(
        4, queue_name="share b", concurrency_group=GROUP
    )(slow_func)
    try:
        results = await asyncio.gather(
            *(wrapped_a(i) for i in range(3)), *(wrapped_b(i) for i in range(3))
        )
        assert len(results) == 6
        assert peak == 1
    finally:
        await wrapped_a.shutdown()
        await wrapped_b.shutdown()


# ---------------------------------------------------------------------------
# Unconfigured / standalone paths stay untouched
# ---------------------------------------------------------------------------


async def test_no_global_limit_keeps_original_path(monkeypatch):
    _init()  # initialized, but no limits configured

    calls = []
    original_plain = ss.try_acquire_global_slot
    original_tracked = ss.try_acquire_global_slot_tracked

    async def spy_plain(group):
        calls.append(group)
        return await original_plain(group)

    async def spy_tracked(group):
        calls.append(group)
        return await original_tracked(group)

    monkeypatch.setattr(ss, "try_acquire_global_slot", spy_plain)
    monkeypatch.setattr(ss, "try_acquire_global_slot_tracked", spy_tracked)

    async def fast_func(value):
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2, queue_name="plain test", concurrency_group=GROUP
    )(fast_func)
    try:
        assert await asyncio.gather(*(wrapped(i) for i in range(5))) == list(range(5))
        assert calls == []  # slot acquisition never touched
        stats = await wrapped.get_queue_stats()
        assert "physical_queued" not in stats  # bounded-queue (default) mode
    finally:
        await wrapped.shutdown()


async def test_standalone_group_none_never_touches_shared_storage(monkeypatch):
    # Shared storage NOT initialized at all (library-external usage).
    def boom(*_args, **_kwargs):
        raise AssertionError("shared_storage API must not be called")

    monkeypatch.setattr(ss, "try_acquire_global_slot", boom)
    monkeypatch.setattr(ss, "try_acquire_global_slot_tracked", boom)
    monkeypatch.setattr(ss, "publish_queue_stats", boom)
    monkeypatch.setattr(ss, "aggregate_queue_stats", boom)

    async def fast_func(value):
        return value

    wrapped = lr_utils.priority_limit_async_func_call(2, queue_name="standalone test")(
        fast_func
    )
    try:
        assert await wrapped("ok") == "ok"
        stats = await wrapped.get_aggregated_queue_stats()
        assert stats["completed_total"] == 1
    finally:
        await wrapped.shutdown()


# ---------------------------------------------------------------------------
# Slot waiting: cancellation, stuck detection, priority
# ---------------------------------------------------------------------------


async def test_user_timeout_while_waiting_for_slot_never_calls_func():
    _init({GROUP: 1})
    external = await ss.try_acquire_global_slot(GROUP)  # saturate the gate
    calls = []

    async def func(value):
        calls.append(value)
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2,
        queue_name="slot wait test",
        concurrency_group=GROUP,
        cleanup_timeout=0.1,
    )(func)
    try:
        with pytest.raises(TimeoutError, match="User timeout"):
            await wrapped("never", _timeout=0.3)
        assert calls == []
        # The lease count is unchanged — the cancelled task never held one.
        assert await ss.global_concurrency_in_use(GROUP) == 1

        stats = await wrapped.get_queue_stats()
        assert stats["running"] == 0  # never misjudged as running/stuck
        assert stats["cancelled_total"] == 1

        # Once the external holder releases, new requests get the slot.
        await ss.release_global_slot(GROUP, external)
        assert await wrapped("now") == "now"
        assert calls == ["now"]
    finally:
        await wrapped.shutdown()


async def test_priority_overtakes_within_process():
    _init({GROUP: 1})
    external = await ss.try_acquire_global_slot(GROUP)
    order = []

    async def func(value):
        order.append(value)
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2, queue_name="priority test", concurrency_group=GROUP
    )(func)
    try:
        low = [asyncio.create_task(wrapped(f"low{i}", _priority=10)) for i in range(3)]
        await asyncio.sleep(0.05)
        high = asyncio.create_task(wrapped("high", _priority=1))
        await asyncio.sleep(0.05)

        await ss.release_global_slot(GROUP, external)
        await asyncio.gather(high, *low)
        assert order[0] == "high"
    finally:
        await wrapped.shutdown()


# ---------------------------------------------------------------------------
# Slot pump: one acquirer per process, no speculative slot grabs
# ---------------------------------------------------------------------------


async def test_pump_acquires_only_for_live_work(monkeypatch):
    """Idle workers must not grab slots speculatively: with one task and
    many local workers, exactly one slot acquisition succeeds (the herd
    used to acquire-and-release, inflating global_in_use and resetting the
    process's waiter seniority)."""
    _init({GROUP: 4})
    successes = 0
    original = ss.try_acquire_global_slot_tracked

    async def counting(group):
        nonlocal successes
        lease, is_priority = await original(group)
        if lease is not None:
            successes += 1
        return lease, is_priority

    monkeypatch.setattr(ss, "try_acquire_global_slot_tracked", counting)

    async def func(value):
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        4, queue_name="pump only-live test", concurrency_group=GROUP
    )(func)
    try:
        assert await wrapped("only") == "only"
        await asyncio.sleep(0.3)  # idle period: nothing else may be acquired
        assert successes == 1
        assert await ss.global_concurrency_in_use(GROUP) == 0
    finally:
        await wrapped.shutdown()


async def test_pump_never_holds_slots_without_free_worker():
    """A slot is requested only when an idle local worker can run the task
    immediately — scarce global capacity is never parked behind a busy
    process."""
    _init({GROUP: 4})
    release = asyncio.Event()
    started = asyncio.Event()

    async def gated(value):
        started.set()
        await release.wait()
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        1, queue_name="pump no-hoard test", concurrency_group=GROUP
    )(gated)
    try:
        first = asyncio.create_task(wrapped("a"))
        await started.wait()
        others = [asyncio.create_task(wrapped(f"b{i}")) for i in range(3)]
        await asyncio.sleep(0.3)
        # Only the running task holds a slot; the queued tasks wait for the
        # single local worker, not for global slots.
        assert await ss.global_concurrency_in_use(GROUP) == 1
        release.set()
        assert await first == "a"
        assert sorted(await asyncio.gather(*others)) == ["b0", "b1", "b2"]
    finally:
        await wrapped.shutdown()


# ---------------------------------------------------------------------------
# Admission semantics (live_queued)
# ---------------------------------------------------------------------------


async def test_admission_counts_only_live_queued_tasks():
    """max_queue_size limits QUEUED tasks; running tasks don't consume it."""
    _init({GROUP: 1})
    release = asyncio.Event()
    started = asyncio.Event()

    async def gated(value):
        started.set()
        await release.wait()
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2,
        queue_name="admission test",
        concurrency_group=GROUP,
        max_queue_size=2,
    )(gated)
    try:
        running = asyncio.create_task(wrapped("running"))
        await started.wait()  # now running (holds the only slot), not queued

        queued = [asyncio.create_task(wrapped(f"q{i}")) for i in range(2)]

        async def _queued_count():
            return (await wrapped.get_queue_stats())["queued"]

        deadline = asyncio.get_running_loop().time() + 5.0
        while await _queued_count() != 2:
            assert asyncio.get_running_loop().time() < deadline
            await asyncio.sleep(0.01)

        stats = await wrapped.get_queue_stats()
        assert stats["queued"] == 2  # both fit: running not counted

        # The third queued request exceeds capacity -> QueueFullError.
        with pytest.raises(lr_utils.QueueFullError):
            await wrapped("overflow", _queue_timeout=0.2)

        release.set()
        assert await running == "running"
        assert sorted(await asyncio.gather(*queued)) == ["q0", "q1"]
    finally:
        await wrapped.shutdown()


async def test_zombies_do_not_consume_admission_capacity():
    _init({GROUP: 1})
    external = await ss.try_acquire_global_slot(GROUP)

    async def func(value):
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2,
        queue_name="zombie admission test",
        concurrency_group=GROUP,
        max_queue_size=2,
        cleanup_timeout=0.05,
    )(func)
    try:
        # Fill the queue, then let both requests time out -> zombies.
        for i in range(2):
            with pytest.raises(TimeoutError):
                await wrapped(f"dead{i}", _timeout=0.1)

        stats = await wrapped.get_queue_stats()
        assert stats["queued"] == 0  # logical count excludes zombies
        assert stats["physical_queued"] == 2  # tuples still in the queue

        # New requests are admitted despite the zombie tuples...
        new_tasks = [
            asyncio.create_task(wrapped(f"new{i}", _queue_timeout=1.0))
            for i in range(2)
        ]
        await asyncio.sleep(0.05)
        assert all(not t.done() for t in new_tasks)  # admitted, waiting for slot

        # ...and run fine once a slot frees up; zombies are drained without
        # ever calling the provider for them.
        await ss.release_global_slot(GROUP, external)
        assert sorted(await asyncio.gather(*new_tasks)) == ["new0", "new1"]
    finally:
        await wrapped.shutdown(graceful=False)


async def test_max_queue_size_zero_means_unlimited_admission():
    _init({GROUP: 2})

    async def func(value):
        await asyncio.sleep(0.01)
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2,
        queue_name="unlimited admission test",
        concurrency_group=GROUP,
        max_queue_size=0,
    )(func)
    try:
        results = await asyncio.gather(*(wrapped(i) for i in range(20)))
        assert len(results) == 20
    finally:
        await wrapped.shutdown()


async def test_shutdown_wakes_admission_waiters():
    _init({GROUP: 1})
    await ss.try_acquire_global_slot(GROUP)  # saturate so nothing dequeues

    async def func(value):
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2,
        queue_name="shutdown wake test",
        concurrency_group=GROUP,
        max_queue_size=1,
    )(func)
    try:
        filler = asyncio.create_task(wrapped("filler"))
        await asyncio.sleep(0.05)
        # No _queue_timeout: sleeps on the admission condition.
        waiter = asyncio.create_task(wrapped("waiter"))
        await asyncio.sleep(0.05)
        assert not waiter.done()

        shutdown_task = asyncio.create_task(
            wrapped.shutdown(graceful=False, timeout=0.1)
        )
        with pytest.raises(RuntimeError, match="Queue is shutting down"):
            await asyncio.wait_for(waiter, timeout=2.0)
        await shutdown_task
        with pytest.raises(asyncio.CancelledError):
            await filler
    finally:
        pass


# ---------------------------------------------------------------------------
# Failure policy: fail-closed acquire, safe release, best-effort stats
# ---------------------------------------------------------------------------


async def test_fail_closed_acquire_keeps_task_queued_until_recovery(monkeypatch):
    _init({GROUP: 2})

    fail = True
    original = ss.try_acquire_global_slot_tracked

    async def flaky(group):
        if fail:
            raise RuntimeError("manager down")
        return await original(group)

    monkeypatch.setattr(ss, "try_acquire_global_slot_tracked", flaky)

    async def func(value):
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2, queue_name="fail closed test", concurrency_group=GROUP
    )(func)
    try:
        task = asyncio.create_task(wrapped("delayed"))
        await asyncio.sleep(0.3)
        assert not task.done()  # never let through while acquire fails

        fail = False
        assert await asyncio.wait_for(task, timeout=2.0) == "delayed"
    finally:
        await wrapped.shutdown()


async def test_release_failure_preserves_result_and_retries_later(monkeypatch):
    _init({GROUP: 1})

    fail_release = True
    original_release = ss.release_global_slot

    async def flaky_release(group, lease_id):
        if fail_release:
            raise RuntimeError("release boom")
        return await original_release(group, lease_id)

    monkeypatch.setattr(ss, "release_global_slot", flaky_release)

    async def func(value):
        if value == "explode":
            raise ValueError("business error")
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2, queue_name="release fail test", concurrency_group=GROUP
    )(func)
    try:
        # Result passes through even though the lease release failed; the
        # lease is parked for retry (still occupying its slot until then).
        assert await wrapped("ok") == "ok"
        assert await ss.global_concurrency_in_use(GROUP) >= 1
        fail_release = False
        await wrapped.run_maintenance()  # health-check retry frees the slot
        assert await ss.global_concurrency_in_use(GROUP) == 0

        # Same for a failing call: the original business exception is never
        # masked by the release failure.
        fail_release = True
        with pytest.raises(ValueError, match="business error"):
            await wrapped("explode")
        assert await ss.global_concurrency_in_use(GROUP) >= 1
        fail_release = False
        await wrapped.run_maintenance()
        assert await ss.global_concurrency_in_use(GROUP) == 0
    finally:
        await wrapped.shutdown()


async def test_stats_failures_never_break_calls(monkeypatch):
    _init({GROUP: 2})

    def boom(*_args, **_kwargs):
        raise RuntimeError("stats backend down")

    monkeypatch.setattr(ss, "publish_queue_stats", boom)
    monkeypatch.setattr(ss, "aggregate_queue_stats", boom)
    monkeypatch.setattr(ss, "reconcile_global_slots", boom)

    async def func(value):
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2, queue_name="best effort test", concurrency_group=GROUP
    )(func)
    try:
        assert await wrapped("fine") == "fine"
        await wrapped.run_maintenance()  # must not raise
        # Aggregation failure falls back to the local snapshot.
        stats = await wrapped.get_aggregated_queue_stats()
        assert stats["completed_total"] == 1
        assert "reporting_workers" not in stats
    finally:
        await wrapped.shutdown()


# ---------------------------------------------------------------------------
# Zombie drain & physical queue compaction
# ---------------------------------------------------------------------------


async def test_worker_drains_zombie_backlog_then_runs_live_task():
    _init({GROUP: 1})
    external = await ss.try_acquire_global_slot(GROUP)
    calls = []

    async def func(value):
        calls.append(value)
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2,
        queue_name="drain test",
        concurrency_group=GROUP,
        max_queue_size=0,
        cleanup_timeout=0.05,
    )(func)
    try:
        # Pile up zombies well past the per-slot drain limit (16).
        zombies = [wrapped(f"dead{i}", _priority=1, _timeout=0.05) for i in range(40)]
        results = await asyncio.gather(*zombies, return_exceptions=True)
        assert all(isinstance(r, TimeoutError) for r in results)

        live = asyncio.create_task(wrapped("live", _priority=5))
        await asyncio.sleep(0.05)
        await ss.release_global_slot(GROUP, external)

        # The worker drains zombies in bounded batches (releasing the slot
        # in between) and the live task still completes; no zombie ever
        # reached the provider.
        assert await asyncio.wait_for(live, timeout=5.0) == "live"
        assert calls == ["live"]

        # live_queued invariant: after the 40 zombies drained and the live
        # task completed, the reservation counter and task_states must be
        # fully back to zero (no leaked reservation, no orphaned tuple).
        await _wait_drained(wrapped)
    finally:
        await wrapped.shutdown(graceful=False)


async def test_compaction_bounds_physical_queue_and_keeps_join_working(monkeypatch):
    _init({GROUP: 1})

    async def acquire_always_fails(_group):
        raise RuntimeError("manager down")  # permanent fail-closed

    monkeypatch.setattr(ss, "try_acquire_global_slot_tracked", acquire_always_fails)

    async def func(value):
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2,
        queue_name="compaction test",
        concurrency_group=GROUP,
        max_queue_size=0,  # threshold = 64
        cleanup_timeout=0.05,
    )(func)
    try:
        zombies = [wrapped(f"dead{i}", _timeout=0.05) for i in range(80)]
        results = await asyncio.gather(*zombies, return_exceptions=True)
        assert all(isinstance(r, TimeoutError) for r in results)

        stats = await wrapped.get_queue_stats()
        assert stats["physical_queued"] == 80
        assert stats["queued"] == 0
        # All 80 timed-out tasks already returned their reservations and
        # popped task_states in their finally blocks; only the physical
        # tuples linger.
        assert stats["in_flight"] == 0

        await wrapped.run_maintenance()  # triggers compaction
        stats = await wrapped.get_queue_stats()
        assert stats["physical_queued"] == 0
        # Compaction must not perturb the logical accounting: live_queued
        # and task_states stay at zero (it only drains dead physical tuples).
        assert stats["queued"] == 0
        assert stats["in_flight"] == 0

        # task_done bookkeeping must be exact: graceful shutdown relies on
        # queue.join() and must complete promptly (not hit the drain timeout).
        await asyncio.wait_for(wrapped.shutdown(graceful=True, timeout=5.0), 10.0)
    finally:
        pass


async def test_compaction_keeps_live_tasks(monkeypatch):
    _init({GROUP: 1})
    external = await ss.try_acquire_global_slot(GROUP)

    async def func(value):
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2,
        queue_name="compaction live test",
        concurrency_group=GROUP,
        max_queue_size=0,
        cleanup_timeout=0.05,
    )(func)
    try:
        zombies = [wrapped(f"dead{i}", _timeout=0.05) for i in range(70)]
        await asyncio.gather(*zombies, return_exceptions=True)
        live = asyncio.create_task(wrapped("live"))
        await asyncio.sleep(0.05)

        await wrapped.run_maintenance()
        stats = await wrapped.get_queue_stats()
        assert stats["queued"] == 1  # live survived compaction
        assert stats["physical_queued"] == 1
        # live_queued invariant at a held steady state: the external slot
        # blocks pickup, so the one live task is queued-not-started
        # (running == 0) and is the only task_states entry (in_flight == 1).
        # live_queued must therefore equal in_flight - running, i.e. exactly
        # the count of not-yet-started tasks, with no phantom reservation.
        assert stats["running"] == 0
        assert stats["in_flight"] == 1
        assert stats["queued"] == stats["in_flight"] - stats["running"]

        await ss.release_global_slot(GROUP, external)
        assert await asyncio.wait_for(live, timeout=5.0) == "live"

        # And once it runs and drains, accounting returns fully to zero.
        await _wait_drained(wrapped)
    finally:
        await wrapped.shutdown(graceful=False)


# ---------------------------------------------------------------------------
# Stats publishing & aggregation through the wrapper
# ---------------------------------------------------------------------------


async def test_aggregated_stats_schema_and_global_fields():
    _init({GROUP: 2})

    async def func(value):
        if value == "fail":
            raise ValueError("x")
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        3, queue_name="agg schema test", concurrency_group=GROUP
    )(func)
    try:
        await wrapped("a")
        await wrapped("b")
        with pytest.raises(ValueError):
            await wrapped("fail")

        # The worker releases its lease in a finally block right after the
        # future resolves; wait for the release before checking in_use.
        async def _in_use_zero():
            return await ss.global_concurrency_in_use(GROUP) == 0

        deadline = asyncio.get_running_loop().time() + 2.0
        while not await _in_use_zero():
            assert asyncio.get_running_loop().time() < deadline
            await asyncio.sleep(0.01)

        stats = await wrapped.get_aggregated_queue_stats()
        # Original flat schema intact (webui/health compatibility).
        for field in (
            "queue_name",
            "max_async",
            "max_queue_size",
            "queued",
            "running",
            "in_flight",
            "worker_count",
            "initialized",
            "submitted_total",
            "completed_total",
            "failed_total",
            "cancelled_total",
            "rejected_total",
        ):
            assert field in stats
        assert stats["completed_total"] == 2
        assert stats["failed_total"] == 1
        # Aggregation extras.
        assert stats["reporting_workers"] == 1
        assert list(stats["per_worker"])
        assert stats["global_limit"] == 2
        assert stats["global_in_use"] == 0
    finally:
        await wrapped.shutdown()


async def test_shutdown_clears_waiter_record():
    _init({GROUP: 1})
    await ss.try_acquire_global_slot(GROUP)  # saturate so workers poll

    async def func(value):
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2, queue_name="waiter cleanup test", concurrency_group=GROUP
    )(func)
    pending = asyncio.create_task(wrapped("stuck"))
    try:
        # Wait until a polling worker registers this process as a waiter.
        deadline = asyncio.get_running_loop().time() + 5.0
        while not await ss.global_slot_waiters(GROUP):
            assert asyncio.get_running_loop().time() < deadline
            await asyncio.sleep(0.01)
    finally:
        await wrapped.shutdown(graceful=False, timeout=0.1)
    with pytest.raises(asyncio.CancelledError):
        await pending
    # No-longer-polling process must not linger in the longest-waiter seat.
    assert await ss.global_slot_waiters(GROUP) == []


async def test_aggregated_stats_report_slot_waiters():
    _init({GROUP: 1})
    external = await ss.try_acquire_global_slot(GROUP)

    async def func(value):
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2, queue_name="waiter stats test", concurrency_group=GROUP
    )(func)
    task = asyncio.create_task(wrapped("waiting"))
    try:
        deadline = asyncio.get_running_loop().time() + 5.0
        while not await ss.global_slot_waiters(GROUP):
            assert asyncio.get_running_loop().time() < deadline
            await asyncio.sleep(0.01)

        stats = await wrapped.get_aggregated_queue_stats()
        assert stats["global_waiting_workers"] == 1
        assert stats["global_longest_wait"] >= 0.0

        await ss.release_global_slot(GROUP, external)
        assert await asyncio.wait_for(task, timeout=5.0) == "waiting"
    finally:
        await wrapped.shutdown()


async def test_maintenance_renews_held_leases(monkeypatch):
    monkeypatch.setattr(ss, "_heartbeat_ttl", 0.2)
    monkeypatch.setattr(ss, "_suspect_grace", 0.2)
    _init({GROUP: 1})
    release = asyncio.Event()
    started = asyncio.Event()

    async def long_func(value):
        started.set()
        await release.wait()
        return value

    wrapped = lr_utils.priority_limit_async_func_call(
        2, queue_name="renew test", concurrency_group=GROUP
    )(long_func)
    try:
        task = asyncio.create_task(wrapped("long"))
        await started.wait()

        # Run well past the heartbeat TTL, renewing like the health check.
        for _ in range(5):
            await asyncio.sleep(0.1)
            await wrapped.run_maintenance()

        # The legal long task's lease was never reclaimed.
        assert await ss.global_concurrency_in_use(GROUP) == 1
        release.set()
        assert await task == "long"
    finally:
        await wrapped.shutdown()
