"""OpenClaw message channel.

Connects directly to an OpenClaw instance as an evaluation target.
Uses the OpenClaw chat API to send messages and poll for replies.

Configuration keys (in channel_config, all optional — defaults come from settings):
    base_url        Override OpenClaw upstream URL (defaults to AGENTEVAL_OPENCLAW_UPSTREAM)
    auth_token      Override auth token (defaults to AGENTEVAL_OPENCLAW_AUTH_TOKEN)
    model           Model name for chat completions (default: "doubao-seed-2.0")
    poll_interval   Seconds between polls (default 1.0)
    timeout         Seconds before poll gives up (default 30.0)
"""

import asyncio
import json
import uuid
from typing import Any, Optional

import httpx

from agenteval.channels.base import ChannelHealth, ChannelTransportError, EvalChannel, Reply, SendResult
from agenteval.config import get_settings


class OpenClawChannel(EvalChannel):
    """Message channel backed by an OpenClaw chat API."""

    def __init__(self, config: dict[str, Any]):
        settings = get_settings()
        self.base_url: str = config.get("base_url", settings.openclaw_upstream).rstrip("/")
        self.auth_token: str = config.get("auth_token", settings.openclaw_auth_token)
        self.model: str = config.get("model", "doubao-seed-2.0")
        self._poll_interval: float = float(config.get("poll_interval", 1.0))

        self._client = httpx.AsyncClient(
            headers={
                "Authorization": f"Bearer {self.auth_token}",
                "Content-Type": "application/json",
            },
            timeout=30,
        )

    async def close(self) -> None:
        await self._client.aclose()

    async def health_check(self) -> ChannelHealth:
        try:
            resp = await self._client.get(f"{self.base_url}/api/health")
            resp.raise_for_status()
            return ChannelHealth(ok=True, message=f"OpenClaw {resp.status_code}")
        except Exception as exc:
            return ChannelHealth(ok=False, message=str(exc))

    async def _send(self, content: str, **kwargs: Any) -> SendResult:
        """Send a chat message to OpenClaw."""
        payload = {
            "model": self.model,
            "messages": [{"role": "user", "content": content}],
            "stream": False,
        }
        try:
            resp = await self._client.post(
                f"{self.base_url}/api/v1/chat/completions",
                json=payload,
            )
            resp.raise_for_status()
            data = resp.json()
            # Extract the assistant message ID from the response
            msg_id = data.get("id") or str(uuid.uuid4())
            return SendResult(ok=True, question_msg_id=msg_id, raw_response=data)
        except (httpx.HTTPError, json.JSONDecodeError) as exc:
            return SendResult(ok=False, error=str(exc))

    async def _poll_reply(
        self,
        question_msg_id: str,
        timeout: float = 30.0,
        poll_interval: float = 1.0,
    ) -> Optional[Reply]:
        """Poll the chat completions endpoint until a reply is available.

        Uses the conversation ID from the send response to track the thread.
        """
        interval = poll_interval or self._poll_interval
        deadline = asyncio.get_event_loop().time() + timeout

        # Re-send with the same conversation to get the latest reply
        while asyncio.get_event_loop().time() < deadline:
            try:
                # Get the thread/messages from the conversation
                resp = await self._client.get(
                    f"{self.base_url}/api/v1/chat/completions/{question_msg_id}",
                )
                if resp.status_code == 200:
                    data = resp.json()
                    choice = (data.get("choices") or [{}])[0]
                    reply_content = choice.get("message", {}).get("content", "")
                    if reply_content:
                        return Reply(
                            question_msg_id=question_msg_id,
                            content=reply_content,
                            raw_message={"text": reply_content, "_raw": data},
                        )
                elif resp.status_code != 404:
                    raise ChannelTransportError(f"HTTP {resp.status_code}: {resp.text[:500]}")
            except (httpx.HTTPError, json.JSONDecodeError) as exc:
                raise ChannelTransportError(str(exc)) from exc

            await asyncio.sleep(interval)

        return None
