"""QQ gateway channel based on qq-botpy."""

from __future__ import annotations

import asyncio
from collections import deque
import logging
from typing import Any

from memnixa.channels.base import BaseChannel, InboundChannelMessage
from memnixa.config import QQChannelConfig

LOGGER = logging.getLogger(__name__)

try:
    import botpy
except ImportError:  # pragma: no cover
    botpy = None


def _make_bot_class(channel: "QQChannel") -> "type[botpy.Client]":
    intents = botpy.Intents(public_messages=True, direct_message=True)

    class _Bot(botpy.Client):
        def __init__(self) -> None:
            super().__init__(intents=intents, ext_handlers=False)

        async def on_ready(self) -> None:
            LOGGER.info("QQ bot ready")

        async def on_c2c_message_create(self, message: Any) -> None:
            await channel._on_message(message, is_group=False)

        async def on_group_at_message_create(self, message: Any) -> None:
            await channel._on_message(message, is_group=True)

        async def on_direct_message_create(self, message: Any) -> None:
            await channel._on_message(message, is_group=False)

    return _Bot


class QQChannel(BaseChannel):
    """QQ text channel for the gateway runtime."""

    name = "qq"

    def __init__(self, config: QQChannelConfig, inbound_handler) -> None:
        super().__init__(config, inbound_handler)
        self.config = config
        self._client: Any = None
        self._processed_ids: deque[str] = deque(maxlen=1000)
        self._msg_seq = 1

    async def start(self) -> None:
        """Connect to QQ and keep the bot running."""
        if botpy is None:
            raise RuntimeError("QQ support requires the `qq-botpy` package.")
        if not self.config.app_id or not self.config.secret:
            raise ValueError("QQ channel requires app_id and secret.")

        self._running = True
        bot_class = _make_bot_class(self)
        self._client = bot_class()

        while self._running:
            try:
                await self._client.start(
                    appid=self.config.app_id,
                    secret=self.config.secret,
                )
            except Exception:  # pragma: no cover
                LOGGER.exception("QQ bot client failed")
            if self._running:
                await asyncio.sleep(5)

    async def stop(self) -> None:
        """Stop the QQ client."""
        self._running = False
        if self._client is not None:
            await self._client.close()

    async def _on_message(self, message: Any, *, is_group: bool) -> None:
        message_id = str(message.id)
        if message_id in self._processed_ids:
            return
        self._processed_ids.append(message_id)

        content = str(message.content or "").strip()
        if not content:
            return

        if is_group:
            chat_id = str(message.group_openid)
            sender_id = str(message.author.member_openid)
            reply_target = chat_id
            reply_target_type = "group"
        else:
            sender_id = str(
                getattr(message.author, "id", None)
                or getattr(message.author, "user_openid", None)
                or "unknown"
            )
            chat_id = sender_id
            reply_target = sender_id
            reply_target_type = "c2c"

        await self.emit(
            InboundChannelMessage(
                channel=self.name,
                chat_id=chat_id,
                sender_id=sender_id,
                body=content,
                raw_body=message.content,
                message_id=message_id,
                reply_target=reply_target,
                reply_target_type=reply_target_type,
                metadata={"is_group": is_group},
            )
        )

    async def send_text(
        self,
        reply_target: str,
        content: str,
        *,
        reply_target_type: str,
        metadata: dict[str, Any] | None = None,
    ) -> None:
        if self._client is None:
            raise RuntimeError("QQ client is not initialized.")

        self._msg_seq += 1
        kwargs: dict[str, Any] = {
            "msg_type": 2,
            "markdown": {"content": content},
            "msg_seq": self._msg_seq,
        }
        message_id = (metadata or {}).get("message_id")
        if message_id:
            kwargs["msg_id"] = message_id

        if reply_target_type == "group":
            await self._client.api.post_group_message(
                group_openid=reply_target,
                **kwargs,
            )
            return
        await self._client.api.post_c2c_message(
            openid=reply_target,
            **kwargs,
        )
