from __future__ import annotations

import asyncio

from pygitweb.change_queue import ChangeQueue


class TestChangeQueue:
	def test_notify_wakes_waiter_with_project_name(self) -> None:
		async def scenario() -> tuple[int, str | None, int]:
			queue: ChangeQueue = ChangeQueue()
			waiter: asyncio.Task[str | None] = asyncio.create_task(queue.wait_for_changes(["alpha"], timeout=2.0))
			await asyncio.sleep(0)
			woken: int = await queue.notify("alpha")
			result: str | None = await waiter
			leftover: int = await queue.waiter_count()
			return woken, result, leftover

		woken, result, leftover = asyncio.run(scenario())
		assert woken == 1
		assert result == "alpha"
		assert leftover == 0

	def test_wait_returns_none_on_timeout(self) -> None:
		async def scenario() -> tuple[str | None, int]:
			queue: ChangeQueue = ChangeQueue()
			outcome: str | None = await queue.wait_for_changes(["alpha"], timeout=0.05)
			leftover: int = await queue.waiter_count("alpha")
			return outcome, leftover

		outcome, leftover = asyncio.run(scenario())
		assert outcome is None
		assert leftover == 0

	def test_notify_only_wakes_subscribed_waiters(self) -> None:
		async def scenario() -> tuple[int, str | None]:
			queue: ChangeQueue = ChangeQueue()
			waiter: asyncio.Task[str | None] = asyncio.create_task(queue.wait_for_changes(["alpha"], timeout=0.2))
			await asyncio.sleep(0)
			woken: int = await queue.notify("beta")
			outcome: str | None = await waiter
			return woken, outcome

		woken, outcome = asyncio.run(scenario())
		assert woken == 0
		assert outcome is None

	def test_wait_for_multiple_projects_wakes_on_any(self) -> None:
		async def scenario() -> tuple[str | None, int]:
			queue: ChangeQueue = ChangeQueue()
			waiter: asyncio.Task[str | None] = asyncio.create_task(
				queue.wait_for_changes(["alpha", "group/beta"], timeout=2.0)
			)
			await asyncio.sleep(0)
			await queue.notify("group/beta")
			outcome: str | None = await waiter
			leftover: int = await queue.waiter_count()
			return outcome, leftover

		outcome, leftover = asyncio.run(scenario())
		assert outcome == "group/beta"
		assert leftover == 0

	def test_notify_returns_zero_when_no_waiters(self) -> None:
		async def scenario() -> int:
			queue: ChangeQueue = ChangeQueue()
			return await queue.notify("alpha")

		assert asyncio.run(scenario()) == 0

	def test_wait_returns_none_when_shutdown_event_set(self) -> None:
		async def scenario() -> str | None:
			queue: ChangeQueue = ChangeQueue()
			sd: asyncio.Event = asyncio.Event()
			sd.set()
			return await queue.wait_for_changes(["alpha"], timeout=2.0, shutdown_event=sd)

		assert asyncio.run(scenario()) is None

	def test_wait_returns_none_when_shutdown_beats_notify(self) -> None:
		async def scenario() -> str | None:
			queue: ChangeQueue = ChangeQueue()
			sd: asyncio.Event = asyncio.Event()
			waiter: asyncio.Task[str | None] = asyncio.create_task(
				queue.wait_for_changes(["alpha"], timeout=2.0, shutdown_event=sd)
			)
			await asyncio.sleep(0)
			sd.set()
			return await waiter

		assert asyncio.run(scenario()) is None