import asyncio

from beaver.foundation.events import OutboundMessage
from beaver.interfaces.channels.platforms.qqbot import QQBotAdapter


class FakeSink:
    def __init__(self) -> None:
        self.messages = []

    async def accept_inbound(self, message):
        self.messages.append(message)


class FakeQQBotClient:
    def __init__(self) -> None:
        self.sent = []

    async def send_text(self, *, peer_type: str, peer_id: str, content: str, message_id: str | None):
        self.sent.append(
            {
                "peer_type": peer_type,
                "peer_id": peer_id,
                "content": content,
                "message_id": message_id,
            }
        )


def test_qqbot_normalizes_private_c2c_message() -> None:
    async def run() -> None:
        sink = FakeSink()
        adapter = QQBotAdapter(
            channel_id="qq-main",
            kind="qqbot",
            mode="websocket",
            account_id="qq-bot",
            display_name=None,
            inbound_sink=sink,
            secrets={"appId": "app", "clientSecret": "secret"},
            config={},
            client=FakeQQBotClient(),
        )

        await adapter.handle_event_payload(
            {
                "t": "C2C_MESSAGE_CREATE",
                "d": {
                    "id": "m1",
                    "author": {"user_openid": "u1"},
                    "content": "hello",
                },
            }
        )

        message = sink.messages[0]
        assert message.content == "hello"
        assert message.session_id == "qq-main:qq-bot:u1"
        assert message.channel_identity.peer_type == "dm"
        assert message.channel_identity.user_id == "u1"

    asyncio.run(run())


def test_qqbot_normalizes_group_message() -> None:
    async def run() -> None:
        sink = FakeSink()
        adapter = QQBotAdapter(
            channel_id="qq-main",
            kind="qqbot",
            mode="websocket",
            account_id="qq-bot",
            display_name=None,
            inbound_sink=sink,
            secrets={"appId": "app", "clientSecret": "secret"},
            config={},
            client=FakeQQBotClient(),
        )

        await adapter.handle_event_payload(
            {
                "t": "GROUP_AT_MESSAGE_CREATE",
                "d": {
                    "id": "m2",
                    "group_openid": "g1",
                    "author": {"member_openid": "u1"},
                    "content": "hello group",
                },
            }
        )

        message = sink.messages[0]
        assert message.session_id == "qq-main:qq-bot:g1"
        assert message.channel_identity.peer_type == "group"
        assert message.channel_identity.user_id == "u1"

    asyncio.run(run())


def test_qqbot_sends_reply_with_original_message_id() -> None:
    async def run() -> None:
        sink = FakeSink()
        client = FakeQQBotClient()
        adapter = QQBotAdapter(
            channel_id="qq-main",
            kind="qqbot",
            mode="websocket",
            account_id="qq-bot",
            display_name=None,
            inbound_sink=sink,
            secrets={"appId": "app", "clientSecret": "secret"},
            config={},
            client=client,
        )
        await adapter.handle_event_payload(
            {
                "t": "GROUP_AT_MESSAGE_CREATE",
                "d": {
                    "id": "m2",
                    "group_openid": "g1",
                    "author": {"member_openid": "u1"},
                    "content": "hello group",
                },
            }
        )
        await adapter.send(
            OutboundMessage(
                channel="qq-main",
                content="ok",
                session_id=sink.messages[0].session_id,
                finish_reason="stop",
                channel_identity=sink.messages[0].channel_identity,
            )
        )

        assert client.sent[0] == {
            "peer_type": "group",
            "peer_id": "g1",
            "content": "ok",
            "message_id": "m2",
        }

    asyncio.run(run())
