# pyright: reportPrivateUsage=false
"""Mock tests: genagent messages (stream, send)."""

from collections.abc import AsyncIterator
from types import SimpleNamespace
from typing import Any
from unittest.mock import patch

from seaart import AsyncClient
from seaart._internal._schemas.genagent.gen_agent_messages import (
    AgentGenerationChunk,
    AgentGenerationEnd,
    AgentGenerationResult,
)

_EMPTY_HEADERS: dict[str, str] = {}


def _agent_chunk(text: str = "Hello") -> SimpleNamespace:
    """Build a SimpleNamespace mimicking an SSE chunk with 'choices'."""
    data: dict[str, Any] = {
        "id": "gen1",
        "object": "chat.completion.chunk",
        "created": 0,
        "model": "gpt-4",
        "choices": [{"index": 0, "delta": {"content": text}}],
        "usage": {},
    }
    return SimpleNamespace(
        event="message",
        data=data,
        headers={"x-conv-id": "conv1", "x-msg-id": "m1", "x-resp-msg-id": "r1"},
        id=None,
        retry=None,
    )


def _agent_end(code: int = 10000) -> SimpleNamespace:
    """Build a SimpleNamespace mimicking an SSE end event with 'status'."""
    data: dict[str, Any] = {
        "status": {"code": code, "msg": "ok", "request_id": "req1"},
        "data": {
            "msg_id": "m1",
            "seg_id": "seg1",
            "create_at": 0,
            "role": "assistant",
        },
    }
    return SimpleNamespace(
        event="end",
        data=data,
        headers=_EMPTY_HEADERS,
        id=None,
        retry=None,
    )


async def _iter_ns(*items: SimpleNamespace) -> AsyncIterator[SimpleNamespace]:
    """Yield SimpleNamespace items as an async iterator."""
    for item in items:
        yield item


# ── Stream ───────────────────────────────────────────────────────────────────


class TestGenAgentStream:
    async def test_stream_yields_chunks(self, client: AsyncClient) -> None:
        def make_stream(*__a: Any, **__k: Any) -> AsyncIterator[SimpleNamespace]:
            return _iter_ns(_agent_chunk("Hi"), _agent_chunk(" there"))

        with patch.object(client, "request_route_stream", side_effect=make_stream):
            items: list[Any] = []
            async for item in client.genagent.messages.stream("test", "conv1"):
                items.append(item)

            assert len(items) == 2
            assert all(isinstance(i, AgentGenerationChunk) for i in items)

    async def test_stream_yields_end(self, client: AsyncClient) -> None:
        def make_stream(*__a: Any, **__k: Any) -> AsyncIterator[SimpleNamespace]:
            return _iter_ns(_agent_chunk("text"), _agent_end())

        with patch.object(client, "request_route_stream", side_effect=make_stream):
            items: list[Any] = []
            async for item in client.genagent.messages.stream("test", "conv1"):
                items.append(item)

            assert len(items) == 2
            assert isinstance(items[0], AgentGenerationChunk)
            assert isinstance(items[1], AgentGenerationEnd)

    async def test_stream_skips_done(self, client: AsyncClient) -> None:
        done_ns = SimpleNamespace(
            event="message", data="[DONE]", headers=_EMPTY_HEADERS, id=None, retry=None,
        )

        def make_stream(*__a: Any, **__k: Any) -> AsyncIterator[SimpleNamespace]:
            return _iter_ns(_agent_chunk("ok"), done_ns)

        with patch.object(client, "request_route_stream", side_effect=make_stream):
            items: list[Any] = []
            async for item in client.genagent.messages.stream("test", "conv1"):
                items.append(item)

            assert len(items) == 1

    async def test_stream_skips_none_data(self, client: AsyncClient) -> None:
        none_ns = SimpleNamespace(
            event="message", data=None, headers=_EMPTY_HEADERS, id=None, retry=None,
        )

        def make_stream(*__a: Any, **__k: Any) -> AsyncIterator[SimpleNamespace]:
            return _iter_ns(none_ns, _agent_chunk("ok"))

        with patch.object(client, "request_route_stream", side_effect=make_stream):
            items: list[Any] = []
            async for item in client.genagent.messages.stream("test", "conv1"):
                items.append(item)

            assert len(items) == 1

    async def test_stream_skips_non_mapping(self, client: AsyncClient) -> None:
        str_ns = SimpleNamespace(
            event="message", data="not a mapping", headers=_EMPTY_HEADERS, id=None, retry=None,
        )

        def make_stream(*__a: Any, **__k: Any) -> AsyncIterator[SimpleNamespace]:
            return _iter_ns(str_ns, _agent_chunk("ok"))

        with patch.object(client, "request_route_stream", side_effect=make_stream):
            items: list[Any] = []
            async for item in client.genagent.messages.stream("test", "conv1"):
                items.append(item)

            assert len(items) == 1


# ── Send ─────────────────────────────────────────────────────────────────────


class TestGenAgentSend:
    async def test_send_collects_text(self, client: AsyncClient) -> None:
        def make_stream(*__a: Any, **__k: Any) -> AsyncIterator[SimpleNamespace]:
            return _iter_ns(_agent_chunk("Hello"), _agent_chunk(" world"), _agent_end())

        with patch.object(client, "request_route_stream", side_effect=make_stream):
            result = await client.genagent.messages.send("test", "conv1")

            assert result is not None
            assert isinstance(result, AgentGenerationResult)
            assert "Hello" in result.text
            assert isinstance(result.value, AgentGenerationEnd)

    async def test_send_returns_none_on_empty_stream(self, client: AsyncClient) -> None:
        def make_stream(*__a: Any, **__k: Any) -> AsyncIterator[SimpleNamespace]:
            return _iter_ns()

        with patch.object(client, "request_route_stream", side_effect=make_stream):
            result = await client.genagent.messages.send("test", "conv1")
            assert result is None

    async def test_send_without_end_event(self, client: AsyncClient) -> None:
        # genagent.send() synthesizes an empty AgentGenerationEnd when no 'end' event received
        # (unlike characters.send() which raises StreamInterruptedError)
        def make_stream(*__a: Any, **__k: Any) -> AsyncIterator[SimpleNamespace]:
            return _iter_ns(_agent_chunk("partial"))

        with patch.object(client, "request_route_stream", side_effect=make_stream):
            result = await client.genagent.messages.send("test", "conv1")

            assert result is not None
            assert isinstance(result.value, AgentGenerationEnd)
