from __future__ import annotations

import asyncio

from fastapi.testclient import TestClient

from beaver.interfaces.channels.connections import (
    ChannelConnectionStore,
    ChannelConnectorRegistry,
    CredentialStore,
    FeishuConnector,
    WeixinConnector,
)
from beaver.interfaces.web.app import create_app
from beaver.services.agent_service import AgentService


class FakeSidecarClient:
    def __init__(self) -> None:
        self.sessions: dict[str, dict] = {}
        self.started: list[dict] = []
        self.logged_out: list[str] = []

    async def start_session(self, payload: dict) -> dict:
        self.started.append(payload)
        session = {
            "sessionId": "cs_1",
            "kind": payload["kind"],
            "status": "qr_ready",
            "qrImage": "data:image/png;base64,abc",
            "accountId": None,
            "displayName": None,
            "metadata": {},
        }
        self.sessions["cs_1"] = session
        return session

    async def get_session(self, session_id: str) -> dict:
        return self.sessions[session_id]

    async def logout(self, connection_id: str) -> dict:
        self.logged_out.append(connection_id)
        return {"ok": True}


class ImmediateConnectedSidecarClient(FakeSidecarClient):
    async def start_session(self, payload: dict) -> dict:
        self.started.append(payload)
        session = {
            "sessionId": "cs_connected",
            "kind": payload["kind"],
            "status": "connected",
            "qrImage": None,
            "accountId": f"{payload['kind']}:me",
            "displayName": "Connected Account",
            "metadata": {"stateRef": "state-1", "appSecret": "secret-1", "tenantAccessToken": "token-1"},
        }
        self.sessions["cs_connected"] = session
        return session


def test_weixin_connector_starts_connector_session(tmp_path, monkeypatch) -> None:
    async def run() -> None:
        monkeypatch.setenv("EXTERNAL_CONNECTOR_CALLBACK_BASE_URL", "http://app-instance-jaychen:8080")
        connection_store = ChannelConnectionStore(tmp_path / "connections.json")
        credential_store = CredentialStore(tmp_path / "credentials.json")
        client = FakeSidecarClient()
        connector = WeixinConnector(
            connection_store=connection_store,
            credential_store=credential_store,
            sidecar_client=client,
            sidecar_base_url="http://external-connector:8787",
        )

        view = await connector.start_session(display_name="Weixin Main", owner_user_id="user-1", options={})

        assert view["sessionId"] == "cs_1"
        assert view["connectionId"].startswith("conn_")
        assert client.started[0]["kind"] == "weixin"
        assert client.started[0]["connectionId"].startswith("conn_")
        assert client.started[0]["callbackBaseUrl"] == "http://app-instance-jaychen:8080"
        assert connection_store.list()[0].kind == "weixin"
        assert connection_store.list()[0].status == "pairing"

    asyncio.run(run())


def test_feishu_connector_start_session_connected_updates_connection(tmp_path) -> None:
    async def run() -> None:
        connection_store = ChannelConnectionStore(tmp_path / "connections.json")
        credential_store = CredentialStore(tmp_path / "credentials.json")
        client = ImmediateConnectedSidecarClient()
        connector = FeishuConnector(
            connection_store=connection_store,
            credential_store=credential_store,
            sidecar_client=client,
            sidecar_base_url="http://external-connector:8787",
        )

        view = await connector.start_session(display_name="Feishu Main", owner_user_id=None, options={})
        connection = connection_store.get(view["connectionId"])

        assert view["status"] == "connected"
        assert connection.status == "connected"
        assert connection.account_id == "feishu:me"
        assert connection.display_name == "Connected Account"
        assert connection.credentials_ref is not None

    asyncio.run(run())


def test_weixin_connector_poll_connected_materializes_external_runtime(tmp_path) -> None:
    async def run() -> None:
        connection_store = ChannelConnectionStore(tmp_path / "connections.json")
        credential_store = CredentialStore(tmp_path / "credentials.json")
        client = FakeSidecarClient()
        connector = WeixinConnector(
            connection_store=connection_store,
            credential_store=credential_store,
            sidecar_client=client,
            sidecar_base_url="http://external-connector:8787",
        )
        await connector.start_session(display_name="Weixin Main", owner_user_id=None, options={})
        connection = connection_store.list()[0]
        client.sessions["cs_1"] = {
            "sessionId": "cs_1",
            "kind": "weixin",
            "status": "connected",
            "accountId": "weixin:me",
            "displayName": "Me",
            "metadata": {"stateRef": "state-1"},
        }

        result = await connector.poll_session("cs_1")
        updated = connection_store.get(connection.connection_id)
        spec = await connector.materialize_runtime(connection.connection_id)

        assert result["status"] == "connected"
        assert updated.status == "connected"
        assert updated.account_id == "weixin:me"
        assert spec.kind == "external_connector"
        assert spec.mode == "http"
        assert spec.config["platformKind"] == "weixin"
        assert spec.config["sidecarBaseUrl"] == "http://external-connector:8787"

    asyncio.run(run())


def test_feishu_connector_uses_feishu_kind(tmp_path) -> None:
    async def run() -> None:
        connection_store = ChannelConnectionStore(tmp_path / "connections.json")
        credential_store = CredentialStore(tmp_path / "credentials.json")
        client = FakeSidecarClient()
        connector = FeishuConnector(
            connection_store=connection_store,
            credential_store=credential_store,
            sidecar_client=client,
            sidecar_base_url="http://external-connector:8787",
        )

        await connector.start_session(display_name="Feishu Main", owner_user_id=None, options={"domain": "feishu"})

        assert client.started[0]["kind"] == "feishu"
        assert client.started[0]["options"] == {"domain": "feishu"}

    asyncio.run(run())


def test_feishu_connector_persists_policy_options_in_runtime_config(tmp_path) -> None:
    async def run() -> None:
        connection_store = ChannelConnectionStore(tmp_path / "connections.json")
        credential_store = CredentialStore(tmp_path / "credentials.json")
        client = FakeSidecarClient()
        connector = FeishuConnector(
            connection_store=connection_store,
            credential_store=credential_store,
            sidecar_client=client,
            sidecar_base_url="http://external-connector:8787",
        )

        await connector.start_session(
            display_name="Feishu Main",
            owner_user_id=None,
            options={
                "domain": "feishu",
                "requireMentionInGroups": True,
                "allowFrom": ["ou_1"],
                "groupAllowFrom": ["oc_1"],
                "maxMessageChars": 1234,
            },
        )
        connection = connection_store.list()[0]

        assert client.started[0]["options"]["requireMentionInGroups"] is True
        assert connection.runtime_config["requireMentionInGroups"] is True
        assert connection.runtime_config["allowFrom"] == ["ou_1"]
        assert connection.runtime_config["groupAllowFrom"] == ["oc_1"]
        assert connection.runtime_config["maxMessageChars"] == 1234

    asyncio.run(run())


def test_feishu_connector_materializes_policy_for_external_runtime(tmp_path) -> None:
    async def run() -> None:
        connection_store = ChannelConnectionStore(tmp_path / "connections.json")
        credential_store = CredentialStore(tmp_path / "credentials.json")
        client = ImmediateConnectedSidecarClient()
        connector = FeishuConnector(
            connection_store=connection_store,
            credential_store=credential_store,
            sidecar_client=client,
            sidecar_base_url="http://external-connector:8787",
        )

        view = await connector.start_session(
            display_name="Feishu Main",
            owner_user_id=None,
            options={"requireMentionInGroups": True, "allowFrom": ["ou_1"], "groupAllowFrom": ["oc_1"]},
        )
        spec = await connector.materialize_runtime(view["connectionId"])

        assert spec.config["platformKind"] == "feishu"
        assert spec.config["requireMentionInGroups"] is True
        assert spec.config["allowFrom"] == ["ou_1"]
        assert spec.config["groupAllowFrom"] == ["oc_1"]

    asyncio.run(run())


def test_connector_session_api_starts_and_polls_connected_session(tmp_path, monkeypatch) -> None:
    monkeypatch.setenv("EXTERNAL_CONNECTOR_TOKEN", "connector-token")
    config_path = tmp_path / "config.json"
    config_path.write_text(
        '{"agents": {"defaults": {"workspace": "%s"}}, "providers": {}}' % str(tmp_path),
        encoding="utf-8",
    )
    service = AgentService(config_path=config_path)
    app = create_app(service=service, manage_service_lifecycle=False)
    client = FakeSidecarClient()

    try:
        with TestClient(app) as http:
            state_dir = tmp_path / "state" / "channel_connections"
            connection_store = ChannelConnectionStore(state_dir / "connections.json")
            credential_store = CredentialStore(state_dir / "credentials.json")
            registry = ChannelConnectorRegistry(connection_store=connection_store, credential_store=credential_store)
            registry.register(
                WeixinConnector(
                    connection_store=connection_store,
                    credential_store=credential_store,
                    sidecar_client=client,
                    sidecar_base_url="http://external-connector:8787",
                )
            )
            app.state.channel_connector_registry = registry

            started = http.post(
                "/api/channel-connector-sessions",
                json={"kind": "weixin", "displayName": "Weixin Main", "options": {}},
            )
            session_id = started.json()["session"]["sessionId"]
            connection_id = started.json()["connection"]["connection_id"]
            client.sessions[session_id] = {
                "sessionId": session_id,
                "kind": "weixin",
                "status": "connected",
                "accountId": "weixin:me",
                "displayName": "Me",
                "metadata": {},
            }
            polled = http.get(f"/api/channel-connector-sessions/{session_id}")

            assert started.status_code == 200
            assert polled.status_code == 200
            assert polled.json()["connection"]["status"] == "connected"
            assert connection_store.get(connection_id).status == "connected"
            assert polled.json()["connection"]["channel_id"] in app.state.channel_runtime.adapters
    finally:
        service.close()


def test_connector_session_api_activates_immediate_connected_session(tmp_path, monkeypatch) -> None:
    monkeypatch.setenv("EXTERNAL_CONNECTOR_TOKEN", "connector-token")
    config_path = tmp_path / "config.json"
    config_path.write_text(
        '{"agents": {"defaults": {"workspace": "%s"}}, "providers": {}}' % str(tmp_path),
        encoding="utf-8",
    )
    service = AgentService(config_path=config_path)
    app = create_app(service=service, manage_service_lifecycle=False)
    client = ImmediateConnectedSidecarClient()

    try:
        with TestClient(app) as http:
            state_dir = tmp_path / "state" / "channel_connections"
            connection_store = ChannelConnectionStore(state_dir / "connections.json")
            credential_store = CredentialStore(state_dir / "credentials.json")
            registry = ChannelConnectorRegistry(connection_store=connection_store, credential_store=credential_store)
            registry.register(
                FeishuConnector(
                    connection_store=connection_store,
                    credential_store=credential_store,
                    sidecar_client=client,
                    sidecar_base_url="http://external-connector:8787",
                )
            )
            app.state.channel_connector_registry = registry

            started = http.post(
                "/api/channel-connector-sessions",
                json={"kind": "feishu", "displayName": "Feishu Main", "options": {}},
            )

            assert started.status_code == 200
            connection = started.json()["connection"]
            assert connection["status"] == "connected"
            assert connection["channel_id"] in app.state.channel_runtime.adapters
            assert started.json()["session"]["metadata"] == {"stateRef": "state-1"}
    finally:
        service.close()
