"""联系人域模型：联系人 / 联系人列表 / 群聊列表 / 群成员。"""

from __future__ import annotations

import asyncio
import enum
import logging
from typing import Optional

from pydantic import BaseModel, Field


logger = logging.getLogger("woc-bridge")


# ---------------------------------------------------------------------------
# 联系人接口
# ---------------------------------------------------------------------------
class Contact(BaseModel):
    """联系人。"""

    wxid: str
    nickname: str = ""
    remark: str = ""
    avatar_url: str = ""
    type: str = Field(
        default="person",
        description="person / group / official / system",
    )

    # 扩展字段：从 contact.db 的 contact 表读取
    alias: Optional[str] = Field(default=None, description="微信号/别名")
    encrypt_username: Optional[str] = Field(default=None, description="加密用户名")
    quan_pin: Optional[str] = Field(default=None, description="全拼")
    pin_yin_initial: Optional[str] = Field(default=None, description="拼音首字母")
    big_head_url: Optional[str] = Field(default=None, description="高清头像 URL")
    small_head_url: Optional[str] = Field(default=None, description="缩略头像 URL")
    description: Optional[str] = Field(default=None, description="个性签名/描述")
    local_type: Optional[int] = Field(default=None, description="联系人类型标记")
    verify_flag: Optional[int] = Field(default=None, description="认证标记")
    delete_flag: Optional[int] = Field(default=None, description="删除标记")
    chat_room_type: Optional[int] = Field(default=None, description="群类型标记")


class ContactsResponse(BaseModel):
    """联系人列表响应。"""

    contacts: list[Contact] = Field(default_factory=list)
    total: int = 0


class GroupsResponse(BaseModel):
    """群聊列表响应。"""

    groups: list[Contact] = Field(default_factory=list)
    total: int = 0


# ---------------------------------------------------------------------------
# 群成员接口
# ---------------------------------------------------------------------------
class GroupMember(BaseModel):
    """群成员。"""

    wxid: str
    nickname: str = ""
    display_name: str = ""
    is_admin: bool = False


class GroupMembersResponse(BaseModel):
    """群成员列表响应。"""

    group_wxid: str
    members: list[GroupMember] = Field(default_factory=list)
    total: int = 0


# ---------------------------------------------------------------------------
# 修改好友备注（experimental）
# ---------------------------------------------------------------------------
class SetRemarkRequest(BaseModel):
    """修改好友备注请求（experimental）。"""

    remark: str = Field(description="新备注名")
    display_name: Optional[str] = Field(
        default=None,
        description="会话显示名（备注/昵称）用于搜索框定位；"
        "不传则从 contact.db 解析（修改前的 remark 优先，其次 nickname，回退 wxid）",
    )


class SetRemarkResponse(BaseModel):
    """修改好友备注响应。"""

    success: bool = Field(default=False)
    error: Optional[str] = Field(default=None, description="失败时的错误描述")
    verified: Optional[bool] = Field(
        default=None,
        description="post_verify 校验结果：True=DB 中 remark 已更新为目标值，"
        "False=超时仍为旧值，None=未执行校验",
    )


# ---------------------------------------------------------------------------
# 添加好友（experimental）
# ---------------------------------------------------------------------------
class AddFriendRequest(BaseModel):
    """添加好友请求（experimental）。"""

    keyword: str = Field(description="搜索关键词（wxid/手机号/微信号）")
    message: str = Field(default="", description="可选好友验证消息")


class AddFriendResponse(BaseModel):
    """添加好友响应。"""

    success: bool = Field(default=False)
    error: Optional[str] = Field(default=None, description="失败时的错误描述")
    verified: Optional[bool] = Field(
        default=None,
        description="post_verify 校验结果：True=检测到好友列表变化或输入框清空，"
        "False=未检测到变化，None=未执行校验",
    )


# ---------------------------------------------------------------------------
# 好友申请自动通过
# ---------------------------------------------------------------------------
class FriendRequestItem(BaseModel):
    """好友申请条目（API 响应用）。"""
    stranger_wxid: str
    nickname: str = ""
    verify_message: str = ""
    scene: str = ""
    create_time: int


# 运行时数据结构：避免 friend_parser.py 与 friend_watcher.py 循环导入
class FriendRequestInfo:
    """好友申请信息（从 fmessage sysmsg XML 解析）。"""

    def __init__(
        self,
        stranger_wxid: str,
        nickname: str,
        verify_message: str,
        scene: str,
        raw_xml: str,
        create_time: int,
        msg_local_id: int,
    ) -> None:
        self.stranger_wxid = stranger_wxid
        self.nickname = nickname
        self.verify_message = verify_message
        self.scene = scene
        self.raw_xml = raw_xml
        self.create_time = create_time
        self.msg_local_id = msg_local_id

    def __repr__(self) -> str:
        return (
            f"FriendRequestInfo(stranger_wxid={self.stranger_wxid!r}, "
            f"nickname={self.nickname!r}, scene={self.scene!r}, "
            f"create_time={self.create_time}, msg_local_id={self.msg_local_id})"
        )


class FriendRequestsResponse(BaseModel):
    """好友申请列表响应。"""
    requests: list[FriendRequestItem] = Field(default_factory=list)
    total: int = 0


class AcceptFriendRequest(BaseModel):
    """手动通过好友申请请求。"""
    stranger_wxid: str = Field(description="申请人 wxid（含 @stranger 后缀）")
    nickname: Optional[str] = Field(default=None, description="申请人昵称（UI 定位用）")


class AcceptFriendResponse(BaseModel):
    """通过好友申请响应。"""
    success: bool = Field(default=False)
    error: Optional[str] = Field(default=None)
    verified: Optional[bool] = Field(
        default=None,
        description="post_verify 校验结果：True=DB 中 @stranger 后缀消失已变为好友，"
        "False=超时仍未通过，None=未执行校验",
    )


class AutoAcceptStatus(BaseModel):
    """自动通过运行状态。"""
    running: bool
    enabled: bool
    processed_count: int = 0
    accepted_count: int = 0
    rejected_count: int = 0
    last_processed_time: Optional[int] = None
    cursor_create_time: Optional[int] = Field(
        default=None, description="当前轮询 create_time 游标（调试用）"
    )
    cursor_local_id: Optional[int] = Field(
        default=None, description="当前轮询 local_id 游标（调试用）"
    )
    db_readable: Optional[bool] = Field(
        default=None, description="DB 是否可读（调试用）"
    )
    breaker_state: Optional[str] = Field(
        default=None, description="熔断器当前状态（调试用）"
    )


class AcceptRuleConfig(BaseModel):
    """自动通过规则配置。"""
    enabled: bool = Field(default=False, description="全局开关")
    accept_all: bool = Field(default=False, description="通过所有申请（忽略以下规则）")
    whitelist_wxids: list[str] = Field(default_factory=list, description="白名单 wxid 列表")
    whitelist_nicknames: list[str] = Field(default_factory=list, description="白名单昵称列表（精确匹配）")
    keywords: list[str] = Field(default_factory=list, description="验证消息关键词列表（子串匹配）")
    blacklist_wxids: list[str] = Field(default_factory=list, description="黑名单 wxid 列表")
    blacklist_nicknames: list[str] = Field(default_factory=list, description="黑名单昵称列表")
    allow_scenes: list[str] = Field(default_factory=list, description="允许的场景（空=不限制）")


class AcceptDecision(enum.Enum):
    """规则匹配决策结果。"""
    ACCEPT = "accept"
    REJECT = "reject"
    SKIP = "skip"


class AcceptRuleEngine:
    """好友申请自动通过规则引擎。

    按"黑名单 → 场景 → 白名单 → 关键词 → SKIP"优先级决策，
    配置热更新受 asyncio.Lock 保护。
    """

    def __init__(self, config: AcceptRuleConfig) -> None:
        self._config = config
        self._lock = asyncio.Lock()

    async def get_config(self) -> AcceptRuleConfig:
        """获取当前规则配置（供 API 查询）。"""
        async with self._lock:
            return self._config

    async def evaluate(self, req: "FriendRequestInfo") -> AcceptDecision:
        async with self._lock:
            cfg = self._config

        logger.info(
            "[规则引擎] 开始评估 wxid=%s nickname=%s scene=%s verify=%s",
            req.stranger_wxid, req.nickname, req.scene,
            repr(req.verify_message[:80]) if req.verify_message else "",
        )
        logger.info(
            "[规则引擎] 当前配置 enabled=%s accept_all=%s allow_scenes=%s keywords=%s "
            "whitelist_wxids=%s whitelist_nicknames=%s blacklist_wxids=%s blacklist_nicknames=%s",
            cfg.enabled, cfg.accept_all, cfg.allow_scenes, cfg.keywords,
            cfg.whitelist_wxids, cfg.whitelist_nicknames,
            cfg.blacklist_wxids, cfg.blacklist_nicknames,
        )

        if not cfg.enabled:
            logger.info("[规则引擎] 全局开关 enabled=false → SKIP")
            return AcceptDecision.SKIP

        if cfg.accept_all:
            logger.info("[规则引擎] accept_all=true → ACCEPT")
            return AcceptDecision.ACCEPT

        # 黑名单优先
        if req.stranger_wxid in cfg.blacklist_wxids:
            logger.info("[规则引擎] wxid 命中黑名单 → REJECT")
            return AcceptDecision.REJECT
        if req.nickname and req.nickname in cfg.blacklist_nicknames:
            logger.info("[规则引擎] nickname 命中黑名单 → REJECT")
            return AcceptDecision.REJECT
        logger.info("[规则引擎] 黑名单未命中")

        # 场景过滤
        if cfg.allow_scenes and req.scene not in cfg.allow_scenes:
            logger.info(
                "[规则引擎] scene=%s 不在允许列表 %s → SKIP",
                req.scene, cfg.allow_scenes,
            )
            return AcceptDecision.SKIP
        logger.info("[规则引擎] 场景检查通过 allow_scenes=%s", cfg.allow_scenes)

        # 白名单
        if req.stranger_wxid in cfg.whitelist_wxids:
            logger.info("[规则引擎] wxid 命中白名单 → ACCEPT")
            return AcceptDecision.ACCEPT
        if req.nickname and req.nickname in cfg.whitelist_nicknames:
            logger.info("[规则引擎] nickname 命中白名单 → ACCEPT")
            return AcceptDecision.ACCEPT
        logger.info("[规则引擎] 白名单未命中")

        # 关键词
        if cfg.keywords:
            for kw in cfg.keywords:
                if kw in req.verify_message:
                    logger.info("[规则引擎] 验证消息命中关键词 '%s' → ACCEPT", kw)
                    return AcceptDecision.ACCEPT
            logger.info(
                "[规则引擎] 关键词未命中 keywords=%s verify=%s",
                cfg.keywords,
                repr(req.verify_message[:80]) if req.verify_message else "",
            )
        else:
            logger.info("[规则引擎] 未配置关键词")

        logger.info("[规则引擎] 无匹配规则 → SKIP")
        return AcceptDecision.SKIP

    async def update_config(self, config: AcceptRuleConfig) -> None:
        async with self._lock:
            self._config = config
