from __future__ import annotations

import typing
from concurrent.futures import Future

if typing.TYPE_CHECKING:
    import asyncio

    from ._async._ws import ASGIWebSocketExtension


class ThreadASGIWebSocketExtension:
    """Synchronous WebSocket extension wrapping an async ASGIWebSocketExtension.

    Delegates all operations to the async extension on a background event loop,
    blocking the calling thread via concurrent.futures.Future.
    """

    def __init__(self, async_ext: ASGIWebSocketExtension, loop: asyncio.AbstractEventLoop) -> None:
        self._async_ext = async_ext
        self._loop = loop

    @property
    def closed(self) -> bool:
        return self._async_ext.closed

    def next_payload(self) -> str | bytes | None:
        """Block until the next message arrives from the ASGI WebSocket app."""
        future: Future[str | bytes | None] = Future()

        async def _do() -> None:
            try:
                result = await self._async_ext.next_payload()
                future.set_result(result)
            except Exception as e:
                future.set_exception(e)

        self._loop.call_soon_threadsafe(lambda: self._loop.create_task(_do()))
        return future.result()

    def send_payload(self, buf: str | bytes) -> None:
        """Send a message to the ASGI WebSocket app."""
        future: Future[None] = Future()

        async def _do() -> None:
            try:
                await self._async_ext.send_payload(buf)
                future.set_result(None)
            except Exception as e:
                future.set_exception(e)

        self._loop.call_soon_threadsafe(lambda: self._loop.create_task(_do()))
        future.result()

    def close(self) -> None:
        """Close the WebSocket and clean up."""
        future: Future[None] = Future()

        async def _do() -> None:
            try:
                await self._async_ext.close()
                future.set_result(None)
            except Exception as e:
                future.set_exception(e)

        self._loop.call_soon_threadsafe(lambda: self._loop.create_task(_do()))
        future.result()
