"""Integration tests: characters resource (including chats, messages, settings, datacards, replies)."""

from collections.abc import AsyncIterator
from types import SimpleNamespace

import pytest_asyncio

from seaart import AsyncClient
from seaart._internal._schemas.characters.characters_messages import (
    StreamItem,
    VoiceStreamItem,
)


# ── Characters (top-level) ──────────────────────────────────────────────────

class TestCharacters:
    async def test_detail(self, client: AsyncClient, character_id: str) -> None:
        result = await client.characters.detail(character_id)
        assert result.status.code == 10000
        assert result.value is not None

    async def test_models(self, client: AsyncClient) -> None:
        result = await client.characters.models()
        assert result.status.code == 10000

    async def test_follow_and_unfollow(self, client: AsyncClient, character_id: str) -> None:
        follow = await client.characters.follow(character_id)
        assert follow.status.code == 10000

        collect_id = follow.value.id
        if collect_id:
            unfollow = await client.characters.unfollow(collect_id)
            assert unfollow.status.code == 10000


# ── Characters > Settings (global) ─────────────────────────────────────────

class TestCharactersSettings:
    async def test_update(self, client: AsyncClient) -> None:
        result = await client.characters.settings.update()
        assert result.status.code == 10000

    async def test_set_default(self, client: AsyncClient, character_id: str) -> None:
        result = await client.characters.settings.set_default(character_id)
        assert result.status.code == 10000


# ── Characters > DataCards ──────────────────────────────────────────────────

class TestDataCards:
    async def test_my(self, client: AsyncClient) -> None:
        result = await client.characters.datacards.my()
        assert result.status.code == 10000

    async def test_create(self, client: AsyncClient) -> None:
        result = await client.characters.datacards.create(
            username="integration_test_card",
        )
        assert result.status.code == 10000


# ── Characters > Replies ────────────────────────────────────────────────────

class TestReplies:
    async def test_recommend(self, client: AsyncClient, character_id: str) -> None:
        result = await client.characters.replies.recommend(character_id)
        assert result.status.code == 10000


# ── Characters > Chat lifecycle ─────────────────────────────────────────────

class TestChatLifecycle:
    """Full lifecycle: create → start → settings → send → messages.list → chats.list → delete."""

    @pytest_asyncio.fixture
    async def session(self, client: AsyncClient, character_id: str) -> AsyncIterator[str]:
        """Create a chat session and clean up after."""
        created = await client.characters.chats.create(character_id)
        session_id = created.value.session_id
        yield session_id
        await client.characters.chats.delete(session_id)

    async def test_create_and_delete(self, client: AsyncClient, character_id: str) -> None:
        created = await client.characters.chats.create(character_id)
        assert created.status.code == 10000
        session_id = created.value.session_id
        assert session_id

        deleted = await client.characters.chats.delete(session_id)
        assert deleted.status.code == 10000

    async def test_start(self, client: AsyncClient, session: str) -> None:
        result = await client.characters.chats.start(session)
        assert result.status.code == 10000

    async def test_chat_settings_update(self, client: AsyncClient, character_id: str) -> None:
        result = await client.characters.chats.settings.update(character_id)
        assert result.status.code == 10000

    async def test_list(self, client: AsyncClient, character_id: str) -> None:
        result = await client.characters.chats.list(character_id, page_size=5)
        assert result.status.code == 10000

    async def test_send(self, client: AsyncClient, session: str) -> None:
        result = await client.characters.chats.send(
            content="Hello! This is an integration test.",
            session_id=session,
        )
        assert result.text is not None
        assert len(result.text) > 0

    async def test_stream(self, client: AsyncClient, session: str) -> None:
        chunks: list[StreamItem] = []
        async for item in client.characters.chats.stream(
            content="Hi, say something short.",
            session_id=session,
        ):
            chunks.append(item)
        assert len(chunks) > 0

    async def test_stream_raw(self, client: AsyncClient, session: str) -> None:
        events: list[SimpleNamespace] = []
        async for ns in client.characters.chats.stream_raw(
            content="Hi, say one word.",
            session_id=session,
        ):
            events.append(ns)
        assert len(events) > 0

    async def test_messages_list(self, client: AsyncClient, session: str) -> None:
        # Send a message first so there's something to list
        await client.characters.chats.send(
            content="message for listing",
            session_id=session,
        )
        result = await client.characters.chats.messages.list(
            session_id=session,
            page_size=5,
        )
        assert result.status.code == 10000

    async def test_delete_all(self, client: AsyncClient, character_id: str) -> None:
        # Create a throwaway session
        created = await client.characters.chats.create(character_id)
        assert created.status.code == 10000
        result = await client.characters.chats.delete_all(character_id)
        assert result.status.code == 10000


# ── Tourist chat ────────────────────────────────────────────────────────────

class TestTouristChat:
    async def test_stream_tourist(self, tourist_client: AsyncClient, character_id: str) -> None:
        created = await tourist_client.characters.chats.create(character_id)
        session_id = created.value.session_id

        chunks: list[StreamItem] = []
        async for item in tourist_client.characters.chats.stream_tourist(
            content="Hello tourist test",
            session_id=session_id,
        ):
            chunks.append(item)
        assert len(chunks) > 0

        await tourist_client.characters.chats.delete(session_id)

    async def test_send_tourist(self, tourist_client: AsyncClient, character_id: str) -> None:
        created = await tourist_client.characters.chats.create(character_id)
        session_id = created.value.session_id

        result = await tourist_client.characters.chats.send_tourist(
            content="Hello tourist test",
            session_id=session_id,
        )
        assert result.text is not None

        await tourist_client.characters.chats.delete(session_id)


# ── Voice (TTS) ─────────────────────────────────────────────────────────────

class TestVoice:
    async def test_stream_voice(self, client: AsyncClient, character_id: str) -> None:
        """Send a message, get the msg_id, then stream voice."""
        created = await client.characters.chats.create(character_id)
        session_id = created.value.session_id

        try:
            response = await client.characters.chats.send(
                content="Say hello briefly.",
                session_id=session_id,
            )
            msg_id = response.value.msg_id

            voice_chunks: list[VoiceStreamItem] = []
            async for chunk in client.characters.chats.stream_voice(msg_id=msg_id):
                voice_chunks.append(chunk)
            # Voice may or may not be available depending on character config
        finally:
            await client.characters.chats.delete(session_id)

    async def test_get_audio(self, client: AsyncClient, character_id: str) -> None:
        created = await client.characters.chats.create(character_id)
        session_id = created.value.session_id

        try:
            response = await client.characters.chats.send(
                content="Say hi.",
                session_id=session_id,
            )
            msg_id = response.value.msg_id

            audio = await client.characters.chats.get_audio(msg_id=msg_id)
            # audio can be None if character doesn't support voice
            if audio is not None:
                assert isinstance(audio, bytes)
                assert len(audio) > 0
        finally:
            await client.characters.chats.delete(session_id)
