"""消息适配器 ABC — 所有渠道适配器的基类。

与 KBAdapter 的 authenticate()/close() 生命周期方法对齐：
verify_signature() 对应 authenticate()。
"""

from __future__ import annotations

import abc
from dataclasses import dataclass, field
from enum import Enum
from typing import Any

import httpx


class ChannelType(str, Enum):
    """支持的消息平台渠道。"""

    FEISHU = "feishu"
    DINGTALK = "dingtalk"
    WECOM = "wecom"
    SLACK = "slack"


class MessageDirection(str, Enum):
    """消息流向。"""

    INBOUND = "inbound"
    OUTBOUND = "outbound"


@dataclass
class IncomingMessage:
    """标准化入站消息 — 所有渠道适配器将平台特定格式转换为此结构。"""

    channel: ChannelType
    platform_message_id: str
    user_id: str  # 平台用户 ID
    chat_id: str  # 群组/会话 ID
    content: str  # 消息文本
    raw_event: dict[str, Any] = field(default_factory=dict)  # 原始事件
    timestamp: str = ""


@dataclass
class OutgoingMessage:
    """标准化出站消息。"""

    channel: ChannelType
    chat_id: str
    content: str
    reply_to_message_id: str | None = None


class URLVerificationChallenge(Exception):
    """URL 验证事件 — webhook 端点需返回 ``{"challenge": ...}`` 响应。

    飞书 / Slack 在配置 webhook 时发送一次 ``url_verification`` 事件，
    要求服务端原样返回 ``challenge`` 字段以验证 URL 可达。
    """

    def __init__(self, challenge: str) -> None:
        super().__init__(f"URL verification challenge: {challenge}")
        self.challenge = challenge


class MessageAdapter(abc.ABC):
    """消息适配器 ABC。

    生命周期：
        __init__ → verify_signature() → receive_message() → send_message() → close()

    子类必须实现 verify_signature / receive_message / send_message 抽象方法。
    ``_get_client`` / ``close`` 已在基类提供（懒构造 httpx 客户端 + 释放资源），
    子类只需在 ``__init__`` 中调用 ``super().__init__()``。

    verify_signature 失败时调用方应拒绝处理（webhook 端点 fail-closed：
    Redis 不可用或签名校验失败均返回 503/401，不可跳过 nonce dedup 直接处理消息）。
    """

    def __init__(self) -> None:
        # 懒加载 httpx 客户端 — 避免未使用的适配器持有连接池
        self._client: httpx.AsyncClient | None = None

    def _get_client(self) -> httpx.AsyncClient:
        """懒构造 httpx 客户端（子类共享此实现）。"""
        if self._client is None:
            self._client = httpx.AsyncClient(timeout=10.0)
        return self._client

    @abc.abstractmethod
    async def verify_signature(self, headers: dict[str, str], body: bytes) -> bool:
        """验证平台签名/token。返回 True 表示请求可信。"""

    @abc.abstractmethod
    async def receive_message(self, headers: dict[str, str], body: bytes) -> IncomingMessage:
        """从 webhook 请求中解析标准化消息。"""

    @abc.abstractmethod
    async def send_message(self, message: OutgoingMessage) -> bool:
        """向平台发送消息。返回 True 表示发送成功。"""

    async def close(self) -> None:
        """关闭 httpx 客户端（如已创建）。子类可覆盖以释放额外资源。"""
        if self._client is not None:
            await self._client.aclose()
            self._client = None


# ---------------------------------------------------------------------------
# 辅助函数
# ---------------------------------------------------------------------------


def header_get(headers: dict[str, str], name: str) -> str | None:
    """大小写不敏感的 header 查找。"""
    # 直接命中
    if name in headers:
        return headers[name]
    lower = name.lower()
    for k, v in headers.items():
        if k.lower() == lower:
            return v
    return None
