# retoor <retoor@molodetz.nl>

from __future__ import annotations

import json
import logging
from typing import Any, Awaitable, Callable, Optional

logger = logging.getLogger("devii.avatar")

RequestSink = Callable[[str, dict[str, Any]], Awaitable[Any]]

PREFIX = "avatar_"
UNAVAILABLE = {
    "status": "unavailable",
    "message": (
        "No avatar is attached to this session. devii's on-screen actions are only available "
        "in the web interface (devii-terminal with a devii-avatar)."
    ),
}


class AvatarController:
    def __init__(self) -> None:
        self._request: Optional[RequestSink] = None

    def bind(self, request: RequestSink) -> None:
        self._request = request
        logger.debug("Avatar controller bound to a session")

    def unbind(self) -> None:
        self._request = None
        logger.debug("Avatar controller unbound")

    @property
    def attached(self) -> bool:
        return self._request is not None

    async def dispatch(self, name: str, arguments: dict[str, Any]) -> str:
        action = name[len(PREFIX) :] if name.startswith(PREFIX) else name
        if self._request is None:
            return json.dumps(UNAVAILABLE, ensure_ascii=False)
        try:
            result = await self._request(action, arguments)
        except Exception as exc:  # noqa: BLE001 - surfaced to the model as data
            logger.exception("Avatar action %s failed", action)
            return json.dumps(
                {"status": "error", "action": action, "message": str(exc)}
            )

        if isinstance(result, dict) and result.get("error"):
            return json.dumps(
                {"status": "error", "action": action, "message": str(result["error"])},
                ensure_ascii=False,
            )
        return json.dumps(
            {"status": "success", "action": action, "result": result},
            ensure_ascii=False,
        )
