from __future__ import annotations

import asyncio

from beaver.foundation.config.schema import ChannelConfig
from beaver.foundation.events import MessageBus, OutboundMessage
from beaver.interfaces.channels.runtime import ChannelRuntime


class FakeService:
    async def handle_inbound_message(self, inbound):
        return OutboundMessage(channel=inbound.channel, content="ok", session_id=inbound.session_id, finish_reason="stop")


def test_runtime_add_channel_starts_new_channel_after_runtime_start(tmp_path) -> None:
    async def run() -> None:
        runtime = ChannelRuntime(service=FakeService(), workspace=tmp_path, channels={}, bus=MessageBus())
        await runtime.start()
        try:
            await runtime.add_channel(
                "webhook-dev",
                ChannelConfig(enabled=True, kind="webhook", mode="webhook", account_id="acct"),
            )
            assert "webhook-dev" in runtime.adapters
            assert runtime.states["webhook-dev"]["state"] == "running"
        finally:
            await runtime.stop()

    asyncio.run(run())


def test_runtime_add_channel_noops_for_same_config(tmp_path) -> None:
    async def run() -> None:
        cfg = ChannelConfig(enabled=True, kind="webhook", mode="webhook", account_id="acct")
        runtime = ChannelRuntime(service=FakeService(), workspace=tmp_path, channels={}, bus=MessageBus())
        await runtime.start()
        try:
            await runtime.add_channel("webhook-dev", cfg)
            first = runtime.adapters["webhook-dev"]
            await runtime.add_channel("webhook-dev", cfg)
            assert runtime.adapters["webhook-dev"] is first
        finally:
            await runtime.stop()

    asyncio.run(run())


def test_runtime_replacement_failure_keeps_old_channel(tmp_path) -> None:
    async def run() -> None:
        good = ChannelConfig(enabled=True, kind="webhook", mode="webhook", account_id="acct")
        bad = ChannelConfig(enabled=True, kind="missing", mode="http", account_id="acct")
        runtime = ChannelRuntime(service=FakeService(), workspace=tmp_path, channels={}, bus=MessageBus())
        await runtime.start()
        try:
            await runtime.add_channel("webhook-dev", good)
            old = runtime.adapters["webhook-dev"]
            try:
                await runtime.add_channel("webhook-dev", bad)
            except ValueError:
                pass
            else:
                raise AssertionError("Expected ValueError")
            assert runtime.adapters["webhook-dev"] is old
            assert runtime.channel_configs["webhook-dev"] == good
            assert runtime.states["webhook-dev"]["state"] == "running"
        finally:
            await runtime.stop()

    asyncio.run(run())


def test_runtime_remove_channel_stops_and_unregisters(tmp_path) -> None:
    async def run() -> None:
        runtime = ChannelRuntime(service=FakeService(), workspace=tmp_path, channels={}, bus=MessageBus())
        await runtime.start()
        try:
            await runtime.add_channel(
                "webhook-dev",
                ChannelConfig(enabled=True, kind="webhook", mode="webhook", account_id="acct"),
            )
            await runtime.remove_channel("webhook-dev")
            assert "webhook-dev" not in runtime.adapters
            assert "webhook-dev" not in runtime.manager.channels
            assert runtime.states["webhook-dev"]["state"] == "removed"
        finally:
            await runtime.stop()

    asyncio.run(run())


def test_runtime_builds_external_connector_channel(tmp_path, monkeypatch) -> None:
    async def run() -> None:
        monkeypatch.setenv("EXTERNAL_CONNECTOR_TOKEN", "connector-token")
        runtime = ChannelRuntime(service=FakeService(), workspace=tmp_path, channels={}, bus=MessageBus())
        await runtime.start()
        try:
            await runtime.add_channel(
                "weixin-main",
                ChannelConfig(
                    enabled=True,
                    kind="external_connector",
                    mode="http",
                    account_id="weixin:me",
                    display_name="Weixin Main",
                    config={
                        "platformKind": "weixin",
                        "connectionId": "conn_1",
                        "sidecarBaseUrl": "http://external-connector:8787",
                    },
                ),
            )
            adapter = runtime.adapters["weixin-main"]
            assert adapter.kind == "external_connector"
            assert adapter.mode == "http"
            assert getattr(adapter, "platform_kind") == "weixin"
        finally:
            await runtime.stop()

    asyncio.run(run())
