from __future__ import annotations

import os
from collections.abc import AsyncIterator, Mapping

import structlog

from adapter.matrix.store import get_room_meta
from core.chat import ChatManager
from core.store import StateStore
from sdk.interface import (
    Attachment,
    MessageChunk,
    MessageResponse,
    PlatformClient,
    PlatformError,
    User,
    UserSettings,
)

logger = structlog.get_logger(__name__)


def _ws_debug_enabled() -> bool:
    value = os.environ.get("SURFACES_DEBUG_WS", "")
    return value.strip().lower() in {"1", "true", "yes", "on"}


class RoutedPlatformClient(PlatformClient):
    def __init__(
        self,
        *,
        chat_mgr: ChatManager,
        store: StateStore,
        delegates: Mapping[str, PlatformClient],
    ) -> None:
        if not delegates:
            raise ValueError("RoutedPlatformClient requires at least one delegate")
        self._chat_mgr = chat_mgr
        self._store = store
        self._delegates = dict(delegates)
        self._default_client = next(iter(self._delegates.values()))
        self._prototype_state = getattr(self._default_client, "_prototype_state", None)

    async def get_or_create_user(
        self,
        external_id: str,
        platform: str,
        display_name: str | None = None,
    ) -> User:
        return await self._default_client.get_or_create_user(
            external_id=external_id,
            platform=platform,
            display_name=display_name,
        )

    async def send_message(
        self,
        user_id: str,
        chat_id: str,
        text: str,
        attachments: list[Attachment] | None = None,
    ) -> MessageResponse:
        delegate, platform_chat_id = await self._resolve_delegate(user_id, chat_id)
        return await delegate.send_message(user_id, platform_chat_id, text, attachments)

    async def stream_message(
        self,
        user_id: str,
        chat_id: str,
        text: str,
        attachments: list[Attachment] | None = None,
    ) -> AsyncIterator[MessageChunk]:
        delegate, platform_chat_id = await self._resolve_delegate(user_id, chat_id)
        async for chunk in delegate.stream_message(user_id, platform_chat_id, text, attachments):
            yield chunk

    async def get_settings(self, user_id: str) -> UserSettings:
        return await self._default_client.get_settings(user_id)

    async def update_settings(self, user_id: str, action) -> None:
        await self._default_client.update_settings(user_id, action)

    async def close(self) -> None:
        for delegate in self._delegates.values():
            close = getattr(delegate, "close", None)
            if callable(close):
                await close()

    async def _resolve_delegate(
        self, user_id: str, local_chat_id: str
    ) -> tuple[PlatformClient, str]:
        chat = await self._chat_mgr.get(local_chat_id, user_id)
        if chat is None:
            raise PlatformError(
                f"unknown matrix chat id: {local_chat_id}",
                code="MATRIX_CHAT_NOT_FOUND",
            )

        room_meta = await get_room_meta(self._store, chat.surface_ref)
        if room_meta is None:
            raise PlatformError(
                f"matrix room is not bound: {chat.surface_ref}",
                code="MATRIX_ROOM_NOT_BOUND",
            )

        agent_id = room_meta.get("agent_id")
        platform_chat_id = room_meta.get("platform_chat_id")
        if not agent_id or not platform_chat_id:
            raise PlatformError(
                f"matrix room routing is incomplete: {chat.surface_ref}",
                code="MATRIX_ROUTE_INCOMPLETE",
            )

        delegate = self._delegates.get(str(agent_id))
        if delegate is None:
            raise PlatformError(
                f"unknown matrix agent id: {agent_id}",
                code="MATRIX_AGENT_NOT_FOUND",
            )

        if _ws_debug_enabled():
            logger.warning(
                "matrix_route_resolved",
                user_id=user_id,
                local_chat_id=local_chat_id,
                surface_ref=chat.surface_ref,
                agent_id=str(agent_id),
                platform_chat_id=str(platform_chat_id),
                delegate_type=type(delegate).__name__,
            )

        return delegate, str(platform_chat_id)
