from __future__ import annotations

import asyncio
import logging

from fastapi import APIRouter

from woc_bridge.config import _require_db_reader, _require_xdotool, _require_send_queue
from woc_bridge.db.coordinator import _check_db_readable, with_db_retry
from woc_bridge.routes.send import _resolve_display_name
from woc_bridge.models import (
    BridgeError,
    Contact,
    ContactsResponse,
    GroupsResponse,
    GroupMembersResponse,
    SetRemarkRequest,
    SetRemarkResponse,
    AddFriendRequest,
    AddFriendResponse,
)

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


# ---------------------------------------------------------------------------
# 路由：GET /api/contacts
# ---------------------------------------------------------------------------
@router.get("/api/contacts", response_model=ContactsResponse)
@with_db_retry
async def get_contacts(keyword: str = "", limit: int = 50) -> ContactsResponse:
    """联系人列表查询（channels SessionAdapter / WizardAdapter 使用）。

    Args:
        keyword: 模糊匹配 wxid / nickname / remark，空串返回全部
        limit: 1~200，默认 50

    Returns:
        ContactsResponse：含 contacts 列表 / total

    Raises:
        BridgeError(INVALID_PARAMS): limit 越界（HTTP 400）
        BridgeError(DB_NOT_FOUND): 未找到微信消息 DB（HTTP 500）
        BridgeError(DB_ENCRYPTED): DB 已加密（HTTP 503）

    Notes:
        - 查询走 cp 快照 + sqlite3 同步阻塞，asyncio.to_thread 包装
        - keyword 为空时仍走索引，不会全表扫描
    """
    if limit < 1 or limit > 200:
        raise BridgeError(
            code="INVALID_PARAMS",
            message=f"limit 必须在 1~200 之间，收到 {limit}",
        )

    db_reader = _require_db_reader()

    # DB 加密时返回 DB_ENCRYPTED，而非空数据
    await _check_db_readable()

    result = await asyncio.to_thread(db_reader.get_contacts, keyword, limit)
    contacts_list = result.get("contacts", [])
    logger.info(
        "contacts: keyword='%s' limit=%d → 返回 %d 条, total=%d",
        keyword, limit, len(contacts_list), result.get("total", 0),
    )
    return ContactsResponse(
        contacts=[Contact(**c) for c in contacts_list],
        total=result.get("total", 0),
    )


# ---------------------------------------------------------------------------
# 路由：GET /api/contacts/{wxid}
# ---------------------------------------------------------------------------
@router.get("/api/contacts/{wxid}", response_model=Contact)
@with_db_retry
async def get_contact_detail(wxid: str) -> Contact:
    """单条联系人详情查询。

    Args:
        wxid: 路径参数，联系人 wxid（如 wxid_xxx 或 gh_xxx 公众号）

    Returns:
        Contact：单条联系人记录

    Raises:
        BridgeError(DB_NOT_FOUND): 未找到微信消息 DB（HTTP 500）
        BridgeError(DB_ENCRYPTED): DB 已加密（HTTP 503）
        BridgeError(CONTACT_NOT_FOUND): 指定 wxid 不存在（HTTP 404）

    Notes:
        - 不存在的 wxid 返回 CONTACT_NOT_FOUND（404），与 DB 不可达错误区分
        - 群聊 username（xxx@chatroom）也可用本接口查（DbReader 视为联系人）
    """
    db_reader = _require_db_reader()

    # DB 加密时返回 DB_ENCRYPTED，而非空数据
    await _check_db_readable()

    result = await asyncio.to_thread(db_reader.get_contact_detail, wxid)
    if result is None:
        raise BridgeError(
            code="CONTACT_NOT_FOUND",
            message=f"未找到联系人: {wxid}",
        )
    return Contact(**result)


# ---------------------------------------------------------------------------
# 路由：GET /api/groups
# ---------------------------------------------------------------------------
@router.get("/api/groups", response_model=GroupsResponse)
@with_db_retry
async def get_groups(limit: int = 50) -> GroupsResponse:
    """群聊列表查询。

    Args:
        limit: 1~200，默认 50

    Returns:
        GroupsResponse：含 groups 列表（每项为 Contact 结构）/ total

    Raises:
        BridgeError(INVALID_PARAMS): limit 越界（HTTP 400）
        BridgeError(DB_NOT_FOUND): 未找到微信消息 DB（HTTP 500）
        BridgeError(DB_ENCRYPTED): DB 已加密（HTTP 503）

    Notes:
        - 群聊判定条件：username LIKE '%@chatroom'
        - 返回结构复用 Contact（群也存于 contact 表，字段相同）
    """
    if limit < 1 or limit > 200:
        raise BridgeError(
            code="INVALID_PARAMS",
            message=f"limit 必须在 1~200 之间，收到 {limit}",
        )

    db_reader = _require_db_reader()

    # DB 加密时返回 DB_ENCRYPTED，而非空数据
    await _check_db_readable()

    result = await asyncio.to_thread(db_reader.get_groups, limit)
    groups_list = result.get("groups", [])
    logger.info(
        "groups: limit=%d → 返回 %d 个群, total=%d",
        limit, len(groups_list), result.get("total", 0),
    )
    return GroupsResponse(
        groups=[Contact(**g) for g in groups_list],
        total=result.get("total", 0),
    )


# ---------------------------------------------------------------------------
# 路由：GET /api/groups/{wxid}/members
# ---------------------------------------------------------------------------
@router.get("/api/groups/{wxid}/members", response_model=GroupMembersResponse)
@with_db_retry
async def get_group_members(wxid: str) -> GroupMembersResponse:
    """群成员列表查询。

    Args:
        wxid: 路径参数，群聊 wxid（形如 xxxxx@chatroom）

    Returns:
        GroupMembersResponse：含 members 列表 / total / group_wxid

    Raises:
        BridgeError(DB_NOT_FOUND): 未找到微信消息 DB（HTTP 500）
        BridgeError(DB_ENCRYPTED): DB 已加密（HTTP 503）

    Notes:
        - 群成员信息存于 group_members 表，DbReader 联表查询
        - 若群 wxid 不存在或无成员记录，返回空列表而非错误
    """
    db_reader = _require_db_reader()

    # DB 加密时返回 DB_ENCRYPTED，而非空数据
    await _check_db_readable()

    result = await asyncio.to_thread(db_reader.get_group_members, wxid)
    return GroupMembersResponse(**result)


# ---------------------------------------------------------------------------
# 路由：POST /api/contacts/{wxid}/remark (experimental)
# ---------------------------------------------------------------------------
@router.post("/api/contacts/{wxid}/remark", response_model=SetRemarkResponse)
async def set_contact_remark(wxid: str, req: SetRemarkRequest) -> SetRemarkResponse:
    """修改好友备注名（experimental）。

    UI 自动化路径：定位会话 → 点右上角"..."→ 备注 → 输入 → 完成。

    Args:
        wxid: 路径参数，好友 wxid
        req: SetRemarkRequest（wxid 应与路径参数一致，req.wxid 优先）

    Returns:
        SetRemarkResponse

    Raises:
        BridgeError(INVALID_PARAMS): wxid/remark 为空（HTTP 400）
        BridgeError(WECHAT_NOT_LOGGED_IN): 未登录（HTTP 401）
        BridgeError(WINDOW_NOT_FOUND / SEND_FAILED)

    Notes:
        - experimental：菜单项与按钮坐标均为估算值
        - 修改是否真正生效无法校验（无 UI 元素检测）
    """
    # 路径参数 wxid 为权威标识（body 中不再含 wxid 字段）
    if not wxid:
        raise BridgeError(code="INVALID_PARAMS", message="wxid 不能为空")
    if not req.remark:
        raise BridgeError(code="INVALID_PARAMS", message="remark 不能为空")

    xdotool = _require_xdotool()
    send_queue = _require_send_queue()
    db_reader = _require_db_reader()

    login_state = await xdotool.detect_login_state()
    if login_state != "logged_in":
        raise BridgeError(
            code="WECHAT_NOT_LOGGED_IN",
            message=f"当前登录态为 {login_state}，无法修改备注",
        )

    # 解析 display_name（优先 req.display_name，其次 DB 查旧 remark/nickname，回退 wxid）
    display_name = await _resolve_display_name(
        db_reader, wxid, req.display_name
    )

    try:
        await send_queue.enqueue(
            lambda: xdotool.set_contact_remark(wxid, req.remark, display_name)
        )
    except BridgeError:
        raise
    except Exception as e:
        raise BridgeError(
            code="SEND_FAILED",
            message=f"修改备注失败: {e}",
        )

    logger.info(
        "contacts/remark: wxid=%s remark=%s → UI 操作完成",
        wxid, req.remark,
    )
    return SetRemarkResponse(success=True, error=None)


# ---------------------------------------------------------------------------
# 路由：POST /api/friends/add (experimental)
# ---------------------------------------------------------------------------
@router.post("/api/friends/add", response_model=AddFriendResponse)
async def add_friend(req: AddFriendRequest) -> AddFriendResponse:
    """搜索并添加好友（experimental）。

    UI 自动化路径：激活窗口 → Ctrl+F 搜索关键词 → 点"添加联系人" →
    可选填验证消息 → 点"发送"提交好友请求。

    Args:
        req: AddFriendRequest

    Returns:
        AddFriendResponse

    Raises:
        BridgeError(INVALID_PARAMS): keyword 为空（HTTP 400）
        BridgeError(WECHAT_NOT_LOGGED_IN): 未登录（HTTP 401）
        BridgeError(WINDOW_NOT_FOUND / SEND_FAILED)

    Notes:
        - experimental：搜索结果与按钮坐标均为估算值
        - 无法校验对方是否已收到请求
    """
    if not req.keyword:
        raise BridgeError(code="INVALID_PARAMS", message="keyword 不能为空")

    xdotool = _require_xdotool()
    send_queue = _require_send_queue()

    login_state = await xdotool.detect_login_state()
    if login_state != "logged_in":
        raise BridgeError(
            code="WECHAT_NOT_LOGGED_IN",
            message=f"当前登录态为 {login_state}，无法添加好友",
        )

    try:
        await send_queue.enqueue(
            lambda: xdotool.add_friend(req.keyword, req.message)
        )
    except BridgeError:
        raise
    except Exception as e:
        raise BridgeError(
            code="SEND_FAILED",
            message=f"添加好友失败: {e}",
        )

    logger.info(
        "friends/add: keyword=%s message_len=%d → UI 操作完成",
        req.keyword, len(req.message) if req.message else 0,
    )
    return AddFriendResponse(success=True, error=None)
