"""Tutu API message channel implementation."""

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

import httpx

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


class TutuApiChannel(EvalChannel):
    """Message channel backed by the Tutu chat API.

    Uses a single ``httpx.AsyncClient`` per instance to reuse TCP connections
    across the many small poll requests during an evaluation run.
    """

    def __init__(self, config: dict[str, Any]):
        self.base_url = config["base_url"].rstrip("/")
        self.token = config["token"]
        self.tenant = config["tenant"]
        self.chat_channel_id = config["chat_channel_id"]
        self.chat_contact_id = config["chat_contact_id"]
        self.sender_type = config.get("sender_type", "WORK_WE_CUSTOMER")
        self.contact_type = config.get("contact_type", "EXTERNAL")
        self._client: Optional[httpx.AsyncClient] = None

    async def _get_client(self) -> httpx.AsyncClient:
        if self._client is None or self._client.is_closed:
            self._client = httpx.AsyncClient(timeout=15.0)
        return self._client

    async def close(self) -> None:
        if self._client is not None and not self._client.is_closed:
            await self._client.aclose()
            self._client = None

    def _build_url(self, path: str) -> str:
        return f"{self.base_url}/api/{self.tenant}/{path}"

    def _build_headers(self, accept: str = "*/*") -> dict[str, str]:
        return {
            "accept": accept,
            "Authorization": f"Bearer {self.token}",
            "Content-Type": "application/json",
        }

    async def health_check(self) -> ChannelHealth:
        """Send a lightweight request to verify connectivity."""
        client = await self._get_client()
        try:
            url = self._build_url("v1/chat/message")
            params = {
                "chatChannelId": self.chat_channel_id,
                "chatContactId": self.chat_contact_id,
                "page": 0,
                "size": 1,
            }
            resp = await client.get(url, headers=self._build_headers(), params=params)
            if resp.status_code == 200:
                return ChannelHealth(ok=True, message="通道正常")
            return ChannelHealth(ok=False, message=f"HTTP {resp.status_code}: {resp.text[:200]}")
        except Exception as exc:
            return ChannelHealth(ok=False, message=f"请求异常: {exc}")

    async def _send(self, content: str, **kwargs: Any) -> SendResult:
        """Send a text message to the configured chat contact."""
        payload = {
            "chatChannelId": self.chat_channel_id,
            "chatContactType": self.contact_type,
            "chatContactId": self.chat_contact_id,
            "msgType": kwargs.get("msg_type", "text"),
            "msgBody": json.dumps({"content": content}, ensure_ascii=False),
            "actualSenderType": self.sender_type,
            "sender": {"type": self.contact_type},
        }
        client = await self._get_client()
        try:
            url = self._build_url("v1/chat/message/sendMsg")
            resp = await client.post(url, headers=self._build_headers(), json=payload)
            if resp.status_code != 200:
                return SendResult(ok=False, error=f"HTTP {resp.status_code}: {resp.text[:500]}")

            data = resp.json()
            # The reply references the sent message via metadata.questionMsgId == sent msgId.
            question_msg_id = data.get("msgId")
            return SendResult(ok=True, question_msg_id=question_msg_id, raw_response=data)
        except (httpx.HTTPError, json.JSONDecodeError) as exc:
            return SendResult(ok=False, error=f"发送异常: {exc}")

    async def _poll_reply(
        self,
        question_msg_id: str,
        timeout: float = 30.0,
        poll_interval: float = 1.0,
    ) -> Optional[Reply]:
        """Poll chat history until a reply matching the questionMsgId arrives."""
        deadline = time.time() + timeout
        seen_ids: set[str] = set()
        client = await self._get_client()

        while time.time() < deadline:
            try:
                url = self._build_url("v1/chat/message")
                params = {
                    "chatChannelId": self.chat_channel_id,
                    "chatContactId": self.chat_contact_id,
                    "page": 0,
                    "size": 20,
                }
                resp = await client.get(url, headers=self._build_headers(), params=params)
                if resp.status_code != 200:
                    raise ChannelTransportError(f"轮询失败: HTTP {resp.status_code}: {resp.text[:500]}")

                data = resp.json()
                records = data.get("data", []) if isinstance(data, dict) else []
                for msg in records:
                    msg_id = msg.get("id") or msg.get("msgId")
                    if not msg_id or msg_id in seen_ids:
                        continue
                    seen_ids.add(msg_id)

                    meta = msg.get("metadata", {})
                    if meta.get("questionMsgId") == question_msg_id:
                        return Reply(
                            question_msg_id=question_msg_id,
                            content=msg.get("msgBody"),
                            sender_name=msg.get("senderName") or msg.get("actualSenderName"),
                            msg_time=msg.get("msgTime"),
                            raw_message=msg,
                        )
            except (httpx.HTTPError, json.JSONDecodeError) as exc:
                raise ChannelTransportError(f"轮询异常: {exc}") from exc

            await asyncio.sleep(poll_interval)

        return None
