"""
异步 API 测试（AsyncSession + asynccontextmanager）。
"""

from __future__ import annotations

from unittest.mock import patch

import httpx
import pytest
import respx

from cmdc import CMDC
from cmdc.types import Event


BASE_URL = "http://localhost:4000"
API_KEY = "sk-async-test"


class TestAsyncSession:
    @pytest.mark.asyncio
    @respx.mock
    async def test_create_session_async(self) -> None:
        respx.post(f"{BASE_URL}/v1/sessions").mock(
            return_value=httpx.Response(
                201, json={"sessionId": "async-sess", "status": "created"}
            )
        )

        client = CMDC(base_url=BASE_URL, api_key=API_KEY)
        session = await client.create_session_async(model="deepseek:deepseek-chat")

        assert session.session_id == "async-sess"
        await client.aclose()

    @pytest.mark.asyncio
    @respx.mock
    async def test_async_context_manager(self) -> None:
        respx.post(f"{BASE_URL}/v1/sessions").mock(
            return_value=httpx.Response(
                201, json={"sessionId": "async-cm", "status": "created"}
            )
        )
        respx.delete(f"{BASE_URL}/v1/sessions/async-cm").mock(
            return_value=httpx.Response(
                200, json={"sessionId": "async-cm", "status": "deleted"}
            )
        )

        async with CMDC(base_url=BASE_URL, api_key=API_KEY) as client:
            async with client.asession(model="deepseek:deepseek-chat") as session:
                assert session.session_id == "async-cm"

    @pytest.mark.asyncio
    @respx.mock
    async def test_async_approve(self) -> None:
        respx.post(f"{BASE_URL}/v1/sessions").mock(
            return_value=httpx.Response(
                201, json={"sessionId": "async-ctrl", "status": "created"}
            )
        )
        route = respx.post(f"{BASE_URL}/v1/sessions/async-ctrl/approve").mock(
            return_value=httpx.Response(
                200,
                json={"ok": True, "action": "approve", "approvalId": "apr_002"},
            )
        )

        async with CMDC(base_url=BASE_URL, api_key=API_KEY) as client:
            session = await client.create_session_async(model="deepseek:deepseek-chat")
            await session.approve("apr_002")

        assert route.called

    @pytest.mark.asyncio
    @respx.mock
    async def test_async_aprompt(self) -> None:
        """验证 aprompt 返回 async generator 并正确 yield 事件。"""
        respx.post(f"{BASE_URL}/v1/sessions").mock(
            return_value=httpx.Response(
                201, json={"sessionId": "async-stream", "status": "created"}
            )
        )
        respx.post(f"{BASE_URL}/v1/sessions/async-stream/prompt").mock(
            return_value=httpx.Response(
                202,
                json={
                    "requestId": "req-async",
                    "sessionId": "async-stream",
                    "queued": False,
                },
            )
        )

        mock_events = [
            Event(type="agent_start", data={}),
            Event(type="message_delta", data={"delta": "async reply"}),
            Event(type="agent_end", data={
                "messageCount": 2,
                "lastMessage": {"content": "async reply", "role": "assistant"},
                "tokenUsage": {"promptTokens": 5, "completionTokens": 2, "totalTokens": 7},
            }),
        ]

        async def fake_stream_events(_policy):
            for e in mock_events:
                yield e

        async with CMDC(base_url=BASE_URL, api_key=API_KEY) as client:
            session = await client.create_session_async(model="deepseek:deepseek-chat")

            with patch.object(session, "_stream_events", side_effect=fake_stream_events):
                gen = await session.aprompt("你好")
                collected = []
                async for event in gen:
                    collected.append(event)

        assert len(collected) == 3
        assert collected[0].type == "agent_start"
        assert collected[1].delta == "async reply"
        assert collected[2].type == "agent_end"
