"""Oczekiwanie na prompt: kontrakt what_next bez blokowania event loopu.

Testujemy sam helper, nie NextPromptManager — jego import startuje wątek
transkrypcji i serwer HTTP.
"""

import asyncio
import queue
import threading

import pytest

from lib.speech_to_text.prompt_wait import wait_for_item


def test_zwraca_element_ktory_juz_czeka():
    source = queue.Queue()
    source.put("mów dalej")

    assert asyncio.run(wait_for_item(source, timeout=5)) == "mów dalej"


def test_zwraca_element_wrzucony_z_innego_watku_w_trakcie_czekania():
    source = queue.Queue()

    async def scenario():
        threading.Timer(0.05, source.put, args=("nagranie",)).start()
        return await wait_for_item(source, timeout=5)

    assert asyncio.run(scenario()) == "nagranie"


def test_po_timeoucie_rzuca_empty():
    # queue.Empty to sygnał, po którym what_next zwraca instrukcję przedłużenia
    # konwersacji, zamiast wisieć w nieskończoność
    with pytest.raises(queue.Empty):
        asyncio.run(wait_for_item(queue.Queue(), timeout=0.1))


def test_timeout_nie_przekracza_zadanego_czasu():
    async def scenario():
        loop = asyncio.get_running_loop()
        started = loop.time()
        with pytest.raises(queue.Empty):
            await wait_for_item(queue.Queue(), timeout=0.2, poll_interval_s=0.5)
        return loop.time() - started

    # poll dłuższy od timeoutu nie może wydłużyć oczekiwania
    assert asyncio.run(scenario()) < 0.4


def test_nie_zajmuje_event_loopu_w_trakcie_czekania():
    async def scenario():
        ticks = 0

        async def ticker():
            nonlocal ticks
            while True:
                ticks += 1
                await asyncio.sleep(0.01)

        tick_task = asyncio.create_task(ticker())
        with pytest.raises(queue.Empty):
            await wait_for_item(queue.Queue(), timeout=0.2, poll_interval_s=0.02)
        tick_task.cancel()
        return ticks

    assert asyncio.run(scenario()) > 1


def test_anulowane_oczekiwanie_nie_zabiera_promptu():
    async def scenario():
        source = queue.Queue()
        task = asyncio.create_task(wait_for_item(source, timeout=5, poll_interval_s=0.5))

        await asyncio.sleep(0.05)   # zadanie siedzi teraz w pauzie między odpytaniami
        source.put("wypowiedź użytkownika")
        task.cancel()

        with pytest.raises(asyncio.CancelledError):
            await task

        return source

    # prompt musi zostać w kolejce dla następnego what_next
    assert asyncio.run(scenario()).get_nowait() == "wypowiedź użytkownika"
