from __future__ import annotations

from typing import TYPE_CHECKING

from mgf.livepush import SlotCap

if TYPE_CHECKING:
    from tests.conftest import FakeRedis


async def test_acquire_until_cap_then_refuse(fake_redis: FakeRedis) -> None:
    cap = SlotCap(fake_redis, cap=2, key_prefix="t:")
    assert await cap.acquire("k") is True
    assert await cap.acquire("k") is True
    assert await cap.acquire("k") is False  # 3rd exceeds cap=2
    assert fake_redis.store["t:k"] == 2  # over-cap INCR rolled back


async def test_release_frees_a_slot(fake_redis: FakeRedis) -> None:
    cap = SlotCap(fake_redis, cap=1, key_prefix="t:")
    assert await cap.acquire("k") is True
    assert await cap.acquire("k") is False
    await cap.release("k")
    assert await cap.acquire("k") is True


async def test_release_floors_at_zero(fake_redis: FakeRedis) -> None:
    cap = SlotCap(fake_redis, key_prefix="t:")
    await cap.release("k")  # decr with no prior acquire → -1 → reset to 0
    assert fake_redis.store["t:k"] == 0


async def test_acquire_fails_open_on_redis_error(fake_redis: FakeRedis) -> None:
    fake_redis.fail = True
    cap = SlotCap(fake_redis, cap=1)
    assert await cap.acquire("k") is True  # fail-OPEN, never raises


async def test_callbacks_fire(fake_redis: FakeRedis) -> None:
    acquired: list[str] = []
    released: list[str] = []
    cap = SlotCap(
        fake_redis,
        key_prefix="t:",
        on_acquire=acquired.append,
        on_release=released.append,
    )
    await cap.acquire("k")
    await cap.release("k")
    assert acquired == ["k"]
    assert released == ["k"]
