from __future__ import annotations

import asyncio

import pytest

from cmvr_edge_ai.transports.grpc import (
    BidiRequestStream,
    BidiRequestStreamClosed,
)


def test_close_wakes_blocked_sender_without_accepting_its_request() -> None:
    async def exercise():  # type: ignore[no-untyped-def]
        stream = BidiRequestStream[str](capacity=1)
        await stream.send("accepted")
        blocked_sender = asyncio.create_task(stream.send("blocked"))
        await asyncio.sleep(0)
        assert blocked_sender.done() is False

        await stream.close()
        with pytest.raises(BidiRequestStreamClosed, match="closed"):
            await blocked_sender
        drained = [request async for request in stream]
        return drained, stream.stats()

    drained, stats = asyncio.run(exercise())

    assert drained == ["accepted"]
    assert stats.enqueued == 1
    assert stats.blocked_puts == 1
    assert stats.closed is True


def test_bidirectional_request_stream_allows_exactly_one_consumer() -> None:
    async def exercise() -> None:
        stream = BidiRequestStream[str]()
        await stream.send("first")
        first_consumer = stream.__aiter__()
        assert await anext(first_consumer) == "first"

        second_consumer = stream.__aiter__()
        with pytest.raises(RuntimeError, match="exactly one consumer"):
            await anext(second_consumer)

        await stream.close()
        with pytest.raises(StopAsyncIteration):
            await anext(first_consumer)

    asyncio.run(exercise())
