from __future__ import annotations

import asyncio

from beaver.foundation.events import ChannelIdentity, OutboundMessage
from beaver.interfaces.channels.external_connector import ExternalConnectorChannel, _request_id


class FakeSidecarClient:
    def __init__(self) -> None:
        self.sent: list[dict] = []

    async def send(self, payload: dict) -> dict:
        self.sent.append(payload)
        return {"ok": True, "providerMessageId": "provider-1"}


def test_external_connector_channel_sends_with_target_and_request_id() -> None:
    async def run() -> None:
        client = FakeSidecarClient()
        channel = ExternalConnectorChannel(
            channel_id="weixin-main",
            platform_kind="weixin",
            connection_id="conn_1",
            account_id="weixin:me",
            display_name="Weixin Main",
            sidecar_client=client,
        )
        message = OutboundMessage(
            channel="weixin-main",
            content="reply",
            session_id="s1",
            finish_reason="stop",
            message_id="out-msg-1",
            channel_identity=ChannelIdentity(
                channel_id="weixin-main",
                kind="weixin",
                account_id="weixin:me",
                peer_id="peer-1",
                peer_type="dm",
                thread_id=None,
                user_id="sender-1",
                message_id="in-msg-1",
            ),
            metadata={"inbound_metadata": {"contextToken": "ctx-1"}},
        )

        await channel.send(message)

        assert client.sent == [
            {
                "requestId": "out_weixin-main:s1:out-msg-1",
                "connectionId": "conn_1",
                "channelId": "weixin-main",
                "kind": "weixin",
                "target": {"peerId": "peer-1", "peerType": "dm", "threadId": None},
                "content": "reply",
                "metadata": {"inboundMessageId": "in-msg-1", "sessionId": "s1", "contextToken": "ctx-1"},
            }
        ]

    asyncio.run(run())


def test_external_connector_request_id_falls_back_when_message_id_is_none_or_blank() -> None:
    identity = ChannelIdentity(
        channel_id="weixin-main",
        kind="weixin",
        account_id="weixin:me",
        peer_id="peer-1",
        peer_type="dm",
        message_id="in-msg-1",
    )
    first = OutboundMessage(
        channel="weixin-main",
        content="same reply",
        session_id="s1",
        finish_reason="stop",
        message_id=None,  # type: ignore[arg-type]
        channel_identity=identity,
    )
    second = OutboundMessage(
        channel="weixin-main",
        content="same reply",
        session_id="s1",
        finish_reason="stop",
        message_id="",
        channel_identity=identity,
    )

    assert _request_id(first) == _request_id(second)
    assert _request_id(first).startswith("out_weixin-main:s1:")


def test_external_connector_channel_requires_identity() -> None:
    async def run() -> None:
        channel = ExternalConnectorChannel(
            channel_id="weixin-main",
            platform_kind="weixin",
            connection_id="conn_1",
            account_id="weixin:me",
            display_name="Weixin Main",
            sidecar_client=FakeSidecarClient(),
        )
        message = OutboundMessage(channel="weixin-main", content="reply", session_id="s1", finish_reason="stop")

        try:
            await channel.send(message)
        except ValueError as exc:
            assert "channel_identity is required" in str(exc)
        else:
            raise AssertionError("Expected ValueError")

    asyncio.run(run())
