from __future__ import annotations

import asyncio

import pytest

from cmvr_edge_ai.core import (
    EdgeQueue,
    EdgeQueueClosed,
    EdgeQueueFull,
    OverflowPolicy,
    PutResult,
)


def test_drop_oldest_keeps_the_freshest_items_and_updates_stats() -> None:
    async def exercise():  # type: ignore[no-untyped-def]
        queue = EdgeQueue[int](2, OverflowPolicy.DROP_OLDEST, name="camera")
        assert await queue.put(1) is PutResult.ENQUEUED
        assert await queue.put(2) is PutResult.ENQUEUED
        assert await queue.put(3) is PutResult.DROPPED_OLDEST
        assert [await queue.get(), await queue.get()] == [2, 3]
        await queue.close()
        with pytest.raises(EdgeQueueClosed, match="camera"):
            await queue.get()
        return queue.stats()

    stats = asyncio.run(exercise())

    assert stats.enqueued == 3
    assert stats.dequeued == 2
    assert stats.dropped_oldest == 1
    assert stats.high_watermark == 2
    assert stats.closed is True


def test_drop_newest_preserves_queued_items() -> None:
    async def exercise():  # type: ignore[no-untyped-def]
        queue = EdgeQueue[str](2, "drop_newest")
        await queue.put("first")
        await queue.put("second")
        result = await queue.put("third")
        items = [await queue.get(), await queue.get()]
        return result, items, queue.stats()

    result, items, stats = asyncio.run(exercise())

    assert result is PutResult.DROPPED_NEWEST
    assert items == ["first", "second"]
    assert stats.enqueued == 2
    assert stats.dropped_newest == 1


def test_reject_policy_raises_without_mutating_contents() -> None:
    async def exercise():  # type: ignore[no-untyped-def]
        queue = EdgeQueue[str](1, "reject", name="control")
        await queue.put("accepted")
        with pytest.raises(EdgeQueueFull, match="control"):
            await queue.put("rejected")
        return await queue.get(), queue.stats()

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

    assert item == "accepted"
    assert stats.enqueued == 1
    assert stats.rejected == 1


def test_block_policy_releases_producer_after_consumer_makes_space() -> None:
    async def exercise():  # type: ignore[no-untyped-def]
        queue = EdgeQueue[str](1, "block")
        await queue.put("first")
        pending_put = asyncio.create_task(queue.put("second"))
        await asyncio.sleep(0)
        assert pending_put.done() is False

        first = await queue.get()
        put_result = await asyncio.wait_for(pending_put, timeout=1)
        second = await queue.get()
        return first, second, put_result, queue.stats()

    first, second, put_result, stats = asyncio.run(exercise())

    assert (first, second) == ("first", "second")
    assert put_result is PutResult.ENQUEUED
    assert stats.blocked_puts == 1
