# retoor <retoor@molodetz.nl>

from __future__ import annotations

import json
import logging
from typing import Any

from devplacepy.services.devii.errors import ToolInputError

from .broker import InteractionBroker
from .capabilities import interactions_enabled
from . import prefs
from .schema import sanitize_text

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


class InteractionController:
    def __init__(
        self,
        broker: InteractionBroker,
        owner_kind: str = "guest",
        owner_id: str = "",
    ) -> None:
        self._broker = broker
        self._owner_kind = owner_kind
        self._owner_id = owner_id

    @property
    def broker(self) -> InteractionBroker:
        return self._broker

    async def dispatch(self, name: str, arguments: dict[str, Any]) -> str:
        if name == "interactions_get":
            return self._pref_get()
        if name == "interactions_set":
            return self._pref_set(arguments or {})
        if name in ("ui_prompt", "ui_notify") and not interactions_enabled(
            self._owner_kind, self._owner_id
        ):
            raise ToolInputError(
                "Interactive widgets are disabled for this account. "
                "Enable them with interactions_set or on the profile, or use a short numbered menu."
            )
        if name == "ui_prompt":
            return await self._prompt(arguments)
        if name == "ui_cancel":
            return await self._cancel(arguments)
        if name == "ui_notify":
            return await self._notify(arguments)
        raise ToolInputError(f"Unknown interaction tool: {name}")

    def _require_user(self) -> None:
        if self._owner_kind != "user" or not self._owner_id:
            raise ToolInputError(
                "Interactive-widget preferences are only available for signed-in users."
            )

    def _pref_get(self) -> str:
        self._require_user()
        data = prefs.snapshot(self._owner_kind, self._owner_id)
        return json.dumps({"status": "success", **data}, ensure_ascii=False)

    def _pref_set(self, arguments: dict[str, Any]) -> str:
        self._require_user()
        reset = arguments.get("reset")
        if isinstance(reset, bool):
            do_reset = reset
        else:
            do_reset = str(reset or "").strip().lower() in ("true", "1", "yes", "on")
        if do_reset:
            data = prefs.set_user_pref(self._owner_id, None)
            return json.dumps({"status": "success", **data}, ensure_ascii=False)
        if "enabled" not in arguments or arguments.get("enabled") is None:
            raise ToolInputError(
                "Pass enabled=true/false to override, or reset=true to inherit the admin default."
            )
        raw = arguments.get("enabled")
        if isinstance(raw, bool):
            enabled = raw
        else:
            enabled = str(raw).strip().lower() in ("true", "1", "yes", "on")
        data = prefs.set_user_pref(self._owner_id, enabled)
        return json.dumps({"status": "success", **data}, ensure_ascii=False)

    async def _prompt(self, arguments: dict[str, Any]) -> str:
        ctx = self._broker.channel_context()
        if "ui_prompt" not in ctx.tools and self._broker.open_id():
            raise ToolInputError(
                "An interaction is already open. Wait for it, or call ui_cancel first."
            )
        if "ui_prompt" not in ctx.tools:
            raise ToolInputError(
                "ui_prompt is not available. Enable interactive widgets with interactions_set "
                "or use a short numbered markdown menu."
            )
        result = await self._broker.prompt(arguments or {})
        return json.dumps(result.to_dict(), ensure_ascii=False)

    async def _cancel(self, arguments: dict[str, Any]) -> str:
        interaction_id = sanitize_text(arguments.get("id", ""), 64)
        reason = sanitize_text(arguments.get("reason", "cancelled"), 40) or "cancelled"
        result = await self._broker.cancel(interaction_id, reason=reason)
        return json.dumps(result.to_dict(), ensure_ascii=False)

    async def _notify(self, arguments: dict[str, Any]) -> str:
        text = sanitize_text(arguments.get("text", ""), 200)
        if not text:
            raise ToolInputError("'text' is required for ui_notify.")
        ctx = self._broker.channel_context()
        if not ctx.capabilities.get("ephemeral_status"):
            return json.dumps(
                {
                    "status": "skipped",
                    "channel_id": ctx.channel_id,
                    "message": "ephemeral status is not supported on this channel",
                },
                ensure_ascii=False,
            )
        emit = getattr(self._broker, "_emit", None)
        if emit is not None:
            await emit({"type": "status", "text": text})
        return json.dumps(
            {
                "status": "ok",
                "channel_id": ctx.channel_id,
                "text": text,
            },
            ensure_ascii=False,
        )
