"""Slack IM 适配器 (U12)。

实现 :class:`MessageAdapter` 协议，对接 Slack Events API 与 Slash Commands。

关键设计决策：
- 签名：``v0:{timestamp}:{body}`` 的 HMAC-SHA256，与 ``X-Slack-Signature`` 比对。
- 时间戳窗口 300s（Slack 官方推荐 5 分钟）。
- URL 验证：``url_verification`` 事件抛 :class:`URLVerificationChallenge`。
- Events API 与 Slash Commands 双流程：JSON body → Events API；form-encoded → Slash。
- ``<@U12345>`` 提及标记剥离。
- httpx 客户端懒构造。
"""

from __future__ import annotations

import hashlib
import hmac
import json
import logging
import re
import time
from typing import Any
from urllib.parse import parse_qs
from uuid import uuid4

import httpx

from agentkit.channels.base import (
    ChannelType,
    IncomingMessage,
    MessageAdapter,
    OutgoingMessage,
    URLVerificationChallenge,
    header_get,
)

logger = logging.getLogger(__name__)

# 签名时间戳允许的最大偏移（秒）— Slack 官方推荐 5 分钟
_SIGNATURE_MAX_AGE_SECONDS = 300

# Slack API 端点
_SEND_MESSAGE_URL = "https://slack.com/api/chat.postMessage"

# Slack <@U12345> / <@U12345|name> 提及标记剥离
_MENTION_RE = re.compile(r"<@[^>]+>\s*")


class SignatureVerificationError(Exception):
    """``verification_token`` 校验失败 — 拒绝处理。"""


class SlackMessageAdapter(MessageAdapter):
    """Slack IM 适配器。

    生命周期：
        ``__init__`` → :meth:`verify_signature` → :meth:`receive_message`
        → :meth:`send_message` → :meth:`close`

    Args:
        bot_token: Slack Bot User OAuth Token（``xoxb-`` 开头）。
        signing_secret: Slack 应用 Signing Secret（签名校验）。
        verification_token: 可选旧版 Verification Token（URL 验证时校验）。
    """

    def __init__(
        self,
        bot_token: str,
        signing_secret: str,
        verification_token: str | None = None,
    ) -> None:
        super().__init__()
        self.bot_token = bot_token
        self.signing_secret = signing_secret
        self.verification_token = verification_token

    # ------------------------------------------------------------------
    # 签名验证
    # ------------------------------------------------------------------

    async def verify_signature(self, headers: dict[str, str], body: bytes) -> bool:
        """验证 Slack webhook 签名。

        签名 = ``v0=`` + hmac-sha256(key=signing_secret, msg="v0:{timestamp}:{body}")，
        与 ``X-Slack-Signature`` 头比对。时间戳超过 5 分钟视为重放攻击。

        Args:
            headers: HTTP 请求头（键大小写不敏感查找）。
            body: 原始请求体字节。

        Returns:
            True 表示签名校验通过。
        """
        signature = header_get(headers, "X-Slack-Signature")
        timestamp_str = header_get(headers, "X-Slack-Request-Timestamp")
        if not signature or not timestamp_str:
            return False

        # 时间戳重放保护
        try:
            ts = int(timestamp_str)
        except ValueError:
            return False
        now = time.time()
        if abs(now - ts) > _SIGNATURE_MAX_AGE_SECONDS:
            logger.warning("Slack webhook 时间戳超出 %ds 窗口 — 拒绝", _SIGNATURE_MAX_AGE_SECONDS)
            return False

        # 计算签名：v0={timestamp}:{body}
        base = f"v0:{timestamp_str}:{body.decode('utf-8')}"
        expected = (
            "v0="
            + hmac.new(
                self.signing_secret.encode("utf-8"),
                base.encode("utf-8"),
                hashlib.sha256,
            ).hexdigest()
        )
        return hmac.compare_digest(signature, expected)

    # ------------------------------------------------------------------
    # 消息接收 / 解析
    # ------------------------------------------------------------------

    async def receive_message(self, headers: dict[str, str], body: bytes) -> IncomingMessage:
        """解析 Slack webhook 事件为标准化 :class:`IncomingMessage`。

        - JSON body：Events API 或 URL 验证。
        - form-encoded body：Slash Command。

        Raises:
            URLVerificationChallenge: URL 验证事件。
            SignatureVerificationError: ``verification_token`` 不匹配。
        """
        # 优先尝试 JSON 解析（Events API / URL 验证）
        try:
            data: dict[str, Any] = json.loads(body)
        except (json.JSONDecodeError, UnicodeDecodeError):
            data = {}

        if data:
            return self._parse_event(data)

        # 非 JSON — 视为 Slash Command（form-encoded）
        return self._parse_slash_command(body)

    def _parse_event(self, data: dict[str, Any]) -> IncomingMessage:
        """解析 Events API 事件。"""
        # URL 验证流程
        if data.get("type") == "url_verification":
            challenge = data.get("challenge", "")
            token = data.get("token", "")
            if self.verification_token is not None and not hmac.compare_digest(
                token, self.verification_token
            ):
                raise SignatureVerificationError("verification_token 不匹配")
            raise URLVerificationChallenge(challenge)

        event = data.get("event", {})
        event_id = data.get("event_id", "")
        event_type = event.get("type", "")
        user = str(event.get("user", ""))
        channel = event.get("channel", "")
        text = event.get("text", "")
        ts = event.get("ts", "")

        # 仅支持 message / app_mention 事件
        if event_type not in ("message", "app_mention"):
            return IncomingMessage(
                channel=ChannelType.SLACK,
                platform_message_id=event_id,
                user_id=user,
                chat_id=channel,
                content=f"[unsupported event type: {event_type}]",
                raw_event=data,
                timestamp=ts,
            )

        # 剥离 <@U12345> 提及标记
        text = _MENTION_RE.sub("", text).strip()

        return IncomingMessage(
            channel=ChannelType.SLACK,
            platform_message_id=event_id,
            user_id=user,
            chat_id=channel,
            content=text,
            raw_event=data,
            timestamp=ts,
        )

    def _parse_slash_command(self, body: bytes) -> IncomingMessage:
        """解析 Slash Command（form-encoded body）。"""
        params = parse_qs(body.decode("utf-8"))
        text = params.get("text", [""])[0]
        user_id = params.get("user_id", [""])[0]
        channel_id = params.get("channel_id", [""])[0]
        command = params.get("command", [""])[0]

        return IncomingMessage(
            channel=ChannelType.SLACK,
            platform_message_id=f"slash-{uuid4()}",
            user_id=user_id,
            chat_id=channel_id,
            content=text,
            raw_event={"command": command, "form": {k: v[0] for k, v in params.items()}},
            timestamp=str(time.time()),
        )

    # ------------------------------------------------------------------
    # 消息发送
    # ------------------------------------------------------------------

    async def send_message(self, message: OutgoingMessage) -> bool:
        """向 Slack 发送文本消息（chat.postMessage）。

        Returns:
            True 表示 HTTP 200 且响应 ``ok == true``。
        """
        try:
            client = self._get_client()
            resp = await client.post(
                _SEND_MESSAGE_URL,
                headers={"Authorization": f"Bearer {self.bot_token}"},
                json={"channel": message.chat_id, "text": message.content},
            )
            if resp.status_code != 200:
                logger.error("Slack send_message HTTP %d: %s", resp.status_code, resp.text[:200])
                return False
            data = resp.json()
            if not data.get("ok"):
                logger.error(
                    "Slack send_message 业务失败 ok=%s error=%s",
                    data.get("ok"),
                    data.get("error", "")[:200],
                )
                return False
            return True
        except httpx.HTTPError as exc:
            logger.error("Slack send_message 网络错误: %s", exc)
            return False
