"""Context-aware memory stream wrappers.

anyio memory streams do not propagate ``contextvars.Context`` across task
boundaries.  These thin wrappers capture the sender's context at ``send()``
time and expose it on the receive side via ``last_context``, so consumers
can restore it with ``ctx.run(handler, item)``.

The iteration interface is unchanged (yields ``T``, not tuples), keeping
these wrappers duck-type compatible with plain ``MemoryObjectSendStream``
and ``MemoryObjectReceiveStream``.
"""

from __future__ import annotations

import contextvars
from types import TracebackType
from typing import Any, Generic, TypeVar

import anyio
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream

T = TypeVar("T")

# Internal payload carried through the underlying raw stream.
_Envelope = tuple[contextvars.Context, T]


class ContextSendStream(Generic[T]):
    """Send-side wrapper that snapshots ``contextvars.copy_context()`` on every ``send()``."""

    __slots__ = ("_inner",)

    def __init__(self, inner: MemoryObjectSendStream[_Envelope[T]]) -> None:
        self._inner = inner

    async def send(self, item: T) -> None:
        await self._inner.send((contextvars.copy_context(), item))

    def close(self) -> None:
        self._inner.close()

    async def aclose(self) -> None:
        await self._inner.aclose()

    def clone(self) -> ContextSendStream[T]:  # pragma: no cover
        return ContextSendStream(self._inner.clone())

    async def __aenter__(self) -> ContextSendStream[T]:
        return self

    async def __aexit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> bool | None:
        await self.aclose()
        return None


class ContextReceiveStream(Generic[T]):
    """Receive-side wrapper that yields ``T`` and stores the sender's context in ``last_context``."""

    __slots__ = ("_inner", "last_context")

    def __init__(self, inner: MemoryObjectReceiveStream[_Envelope[T]]) -> None:
        self._inner = inner
        self.last_context: contextvars.Context | None = None

    async def receive(self) -> T:
        ctx, item = await self._inner.receive()
        self.last_context = ctx
        return item

    def close(self) -> None:
        self._inner.close()

    async def aclose(self) -> None:
        await self._inner.aclose()

    def clone(self) -> ContextReceiveStream[T]:  # pragma: no cover
        return ContextReceiveStream(self._inner.clone())

    def __aiter__(self) -> ContextReceiveStream[T]:
        return self

    async def __anext__(self) -> T:
        try:
            return await self.receive()
        except anyio.EndOfStream:
            raise StopAsyncIteration

    async def __aenter__(self) -> ContextReceiveStream[T]:
        return self

    async def __aexit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> bool | None:
        await self.aclose()
        return None


class create_context_streams(
    tuple[ContextSendStream[T], ContextReceiveStream[T]],
):
    """Create context-aware memory object streams.

    Supports ``create_context_streams[T](n)`` bracket syntax,
    matching anyio's ``create_memory_object_stream`` API style.
    """

    def __new__(cls, max_buffer_size: float = 0) -> tuple[ContextSendStream[T], ContextReceiveStream[T]]:  # type: ignore[type-var]
        raw_send: MemoryObjectSendStream[Any]
        raw_receive: MemoryObjectReceiveStream[Any]
        raw_send, raw_receive = anyio.create_memory_object_stream(max_buffer_size)
        return (ContextSendStream(raw_send), ContextReceiveStream(raw_receive))
