"""Ask human tool: request clarification from the user via control plane."""

from __future__ import annotations

import asyncio
import json
import uuid
from typing import Any, Awaitable, Callable

from loguru import logger

from madpod_agent.agent.tools.base import Tool
from madpod_agent.bus.events import OutboundMessage

_DEFAULT_TIMEOUT = 300.0  # 5 minutes
_MAX_TIMEOUT = 600.0  # 10 minutes


class AskHumanTool(Tool):
    """Ask the user a question and wait for their response.

    Sends an intervention message via the control plane and blocks
    until the user responds or a timeout is reached.
    """

    def __init__(
        self,
        send_callback: Callable[[OutboundMessage], Awaitable[None]] | None = None,
        response_future_factory: Callable[[str], asyncio.Future[str]] | None = None,
        default_channel: str = "",
        default_chat_id: str = "",
    ):
        self._send_callback = send_callback
        self._response_future_factory = response_future_factory
        self._default_channel = default_channel
        self._default_chat_id = default_chat_id

    def set_send_callback(self, callback: Callable[[OutboundMessage], Awaitable[None]]) -> None:
        self._send_callback = callback

    def set_response_future_factory(self, factory: Callable[[str], asyncio.Future[str]]) -> None:
        self._response_future_factory = factory

    def set_context(self, channel: str, chat_id: str) -> None:
        self._default_channel = channel
        self._default_chat_id = chat_id

    @property
    def name(self) -> str:
        return "ask_human"

    @property
    def description(self) -> str:
        return (
            "Ask the user a question and wait for their response. "
            "Use when you need clarification, approval, or additional information "
            "that you cannot determine on your own. The user will be notified "
            "and can respond in their own time (up to timeout)."
        )

    @property
    def parameters(self) -> dict[str, Any]:
        return {
            "type": "object",
            "properties": {
                "question": {
                    "type": "string",
                    "description": "The question to ask the user",
                },
                "options": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Optional: preset answer choices for the user",
                },
                "context": {
                    "type": "string",
                    "description": "Optional: background context to help user understand the question",
                },
                "timeout": {
                    "type": "number",
                    "description": "Seconds to wait for response (default: 300, max: 600)",
                },
            },
            "required": ["question"],
        }

    async def execute(
        self,
        question: str,
        options: list[str] | None = None,
        context: str | None = None,
        timeout: float | None = None,
        **kwargs: Any,
    ) -> str:
        if not self._send_callback:
            return json.dumps({"error": "Message sending not configured — cannot ask user"})

        channel = self._default_channel
        chat_id = self._default_chat_id
        if not channel or not chat_id:
            return json.dumps({"error": "No target channel/chat configured"})

        resolved_timeout = min(timeout or _DEFAULT_TIMEOUT, _MAX_TIMEOUT)
        request_id = uuid.uuid4().hex

        # Build the question message
        content_parts = [f"**Question:** {question}"]
        if context:
            content_parts.insert(0, f"*Context:* {context}")
        if options:
            options_text = "\n".join(f"  {i+1}. {opt}" for i, opt in enumerate(options))
            content_parts.append(f"\n**Options:**\n{options_text}")
        content_parts.append(f"\n_Please respond to continue. (Timeout: {int(resolved_timeout)}s)_")

        content = "\n\n".join(content_parts)

        metadata: dict[str, Any] = {
            "message_id": request_id,
            "message_kind": "intervention",
            "intervention_type": "ask_human",
            "requires_response": True,
        }
        if options:
            metadata["options"] = options

        msg = OutboundMessage(
            channel=channel,
            chat_id=chat_id,
            content=content,
            media=[],
            metadata=metadata,
        )

        try:
            await self._send_callback(msg)
            logger.info("Sent ask_human question: {} (request_id={})", question[:80], request_id)
        except Exception as exc:
            return json.dumps({"error": f"Failed to send question: {exc}"})

        # Wait for response if a future factory is provided
        if self._response_future_factory:
            try:
                future = self._response_future_factory(request_id)
                response = await asyncio.wait_for(future, timeout=resolved_timeout)
                logger.info("Received human response for request_id={}", request_id)
                return json.dumps({
                    "ok": True,
                    "response": response,
                    "request_id": request_id,
                })
            except asyncio.TimeoutError:
                return json.dumps({
                    "ok": False,
                    "error": "User did not respond within timeout",
                    "request_id": request_id,
                    "timeout_seconds": resolved_timeout,
                })
            except Exception as exc:
                return json.dumps({"error": f"Failed waiting for response: {exc}"})

        # No future factory — fire-and-forget mode
        return json.dumps({
            "ok": True,
            "message": "Question sent to user. Response will arrive as a new message.",
            "request_id": request_id,
        })
