from __future__ import annotations

import asyncio
import logging
import os

from fastapi import APIRouter

from woc_bridge.config import _require_send_queue, _require_xdotool, _require_db_reader
from woc_bridge.db.coordinator import with_db_retry
from woc_bridge.models import (
    BridgeError,
    MomentPublishRequest,
    MomentPublishResponse,
    MomentShareArticleRequest,
    MomentShareArticleResponse,
    MomentsTimelineResponse,
    MomentLikeRequest,
    MomentLikeResponse,
    MomentCommentRequest,
    MomentCommentResponse,
    MomentDeleteRequest,
    MomentDeleteResponse,
    MomentPublishImageRequest,
    MomentPublishImageResponse,
)

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


# ---------------------------------------------------------------------------
# 路由：GET /api/moments/timeline
# ---------------------------------------------------------------------------
@router.get("/api/moments/timeline", response_model=MomentsTimelineResponse)
@with_db_retry
async def get_moments_timeline(
    cursor: int = 0,
    limit: int = 20,
) -> MomentsTimelineResponse:
    """读取朋友圈时间线。

    WeChat 4.x 朋友圈 schema 未公开，本接口做容错探测：
    1. 探测 moment/sns DB 文件
    2. 探测朋友圈表名与列名
    3. 按 create_time 降序返回

    因 schema 不确定，响应中含 status 字段标识探测结果：
    - ok：正常读取
    - db_not_found：未找到朋友圈 DB（可能该账号从未发过/看过朋友圈）
    - table_not_found / no_columns / no_time_column：DB 存在但结构不匹配
    - db_encrypted：DB 加密且无法解密
    - query_error：查询异常

    Args:
        cursor: 游标，首次传 0；取 create_time < cursor 的更旧朋友圈
        limit: 最多返回条数，1~50，默认 20

    Returns:
        MomentsTimelineResponse：含 moments / next_cursor / has_more / status

    Raises:
        BridgeError(INVALID_PARAMS): limit 越界（HTTP 400）
        BridgeError(DB_ENCRYPTED): DB 加密且 key 提取失败（HTTP 503，
            由 with_db_retry 触发重试）

    Notes:
        - 不抛 DB_NOT_FOUND：朋友圈 DB 不存在属正常情况（新账号），
          通过 status=db_not_found 告知客户端
        - content 仅为文字内容；图片/视频等媒体字段暂未解析
        - author_wxid 可能为空（DB 中无对应列或值为空）
    """
    if limit < 1 or limit > 50:
        raise BridgeError(
            code="INVALID_PARAMS",
            message=f"limit 必须在 1~50 之间，收到 {limit}",
        )
    if cursor < 0:
        raise BridgeError(
            code="INVALID_PARAMS",
            message=f"cursor 必须 >= 0，收到 {cursor}",
        )

    db_reader = _require_db_reader()
    result = await asyncio.to_thread(
        db_reader.get_moments_timeline, cursor, limit
    )
    moment_count = len(result.get("moments", []))
    logger.info(
        "moments/timeline: cursor=%d limit=%d → 返回 %d 条, status=%s",
        cursor, limit, moment_count, result.get("status"),
    )
    return MomentsTimelineResponse(**result)


# ---------------------------------------------------------------------------
# 路由：POST /api/moments/publish
# ---------------------------------------------------------------------------
@router.post("/api/moments/publish", response_model=MomentPublishResponse)
async def publish_moment(req: MomentPublishRequest) -> MomentPublishResponse:
    """发表纯文字朋友圈。

    通过 xdotool 模拟 UI 操作发表朋友圈：激活窗口 → 点击朋友圈入口 →
    点相机 → 选发表文字 → 粘贴内容 → 发表 → 返回主界面。经 send_queue
    串行执行，避免与发消息等 UI 操作竞态。

    Args:
        req: MomentPublishRequest（content 朋友圈文字内容）

    Returns:
        MomentPublishResponse：success=true / local_moment_id
        / placeholder=false（真实执行 UI 发表操作）

    Raises:
        BridgeError(INVALID_PARAMS): content 为空（HTTP 400）
        BridgeError(WECHAT_NOT_LOGGED_IN): 当前未登录（HTTP 401）
        BridgeError(WINDOW_NOT_FOUND): 微信窗口未找到（HTTP 503）
        BridgeError(SEND_FAILED): UI 操作失败或超时（HTTP 500）

    Notes:
        - 不走 with_db_retry：发朋友圈是纯 UI 操作，不读 DB
        - 经 send_queue 串行执行：朋友圈与发消息共享同一 X 会话，
          必须串行避免窗口焦点竞态
        - UI 路径（朋友圈入口/相机/发表按钮坐标）基于微信 4.0 Linux 默认
          布局估算，需在目标分辨率实测调优
        - local_moment_id 由 bridge 本地生成，不对应微信原生朋友圈 ID，
          仅用于客户端幂等去重
    """
    xdotool = _require_xdotool()
    send_queue = _require_send_queue()

    # 校验请求体
    if not req.content:
        raise BridgeError(code="INVALID_PARAMS", message="content 不能为空")

    # 检查登录态
    login_state = await xdotool.detect_login_state()
    if login_state != "logged_in":
        raise BridgeError(
            code="WECHAT_NOT_LOGGED_IN",
            message=f"当前登录态为 {login_state}，无法发表朋友圈",
        )

    # 经发送队列串行执行（避免与发消息 UI 竞态）
    try:
        local_moment_id = await send_queue.enqueue(
            lambda: xdotool.publish_moment(req.content)
        )
    except BridgeError:
        raise
    except Exception as e:
        raise BridgeError(
            code="SEND_FAILED",
            message=f"发表朋友圈失败: {e}",
        )

    logger.info(
        "moments/publish: content_len=%d → 成功 local_moment_id=%s",
        len(req.content), local_moment_id,
    )
    return MomentPublishResponse(
        success=True,
        local_moment_id=local_moment_id,
        placeholder=False,
        error=None,
    )


# ---------------------------------------------------------------------------
# 路由：POST /api/moments/share_article
# ---------------------------------------------------------------------------
@router.post("/api/moments/share_article", response_model=MomentShareArticleResponse)
async def share_article_moment(req: MomentShareArticleRequest) -> MomentShareArticleResponse:
    """分享公众号文章到朋友圈。

    通过搜索公众号名称进入会话，点击第 N 篇推送文章卡片打开文章页，
    再经「分享到朋友圈」UI 路径生成带卡片预览的朋友圈。经 send_queue
    串行执行，避免与发消息等 UI 操作竞态。

    Args:
        req: MomentShareArticleRequest（public_account + article_index + 可选 comment）

    Returns:
        MomentShareArticleResponse：success=true / local_moment_id
        / placeholder=false（真实执行 UI 分享操作）

    Raises:
        BridgeError(INVALID_PARAMS): public_account 为空或 article_index < 1（HTTP 400）
        BridgeError(WECHAT_NOT_LOGGED_IN): 当前未登录（HTTP 401）
        BridgeError(WINDOW_NOT_FOUND): 微信窗口未找到（HTTP 503）
        BridgeError(SEND_FAILED): UI 操作失败或超时（HTTP 500）

    Notes:
        - 不走 with_db_retry：分享是纯 UI 操作，不读 DB
        - 经 send_queue 串行执行：与发消息共享同一 X 会话，必须串行避免
          窗口焦点竞态
        - UI 路径（文章卡片位置、「⋯」菜单、分享弹窗坐标）基于微信 4.0
          Linux 默认布局估算，需在目标分辨率实测调优
        - article_index=1 表示公众号会话顶部最新一篇推送文章
        - local_moment_id 由 bridge 本地生成，不对应微信原生朋友圈 ID，
          仅用于客户端幂等去重
    """
    xdotool = _require_xdotool()
    send_queue = _require_send_queue()

    # 校验请求体
    if not req.public_account:
        raise BridgeError(code="INVALID_PARAMS", message="public_account 不能为空")
    if req.article_index < 1:
        raise BridgeError(
            code="INVALID_PARAMS",
            message=f"article_index 必须 >= 1，收到 {req.article_index}",
        )

    # 检查登录态
    login_state = await xdotool.detect_login_state()
    if login_state != "logged_in":
        raise BridgeError(
            code="WECHAT_NOT_LOGGED_IN",
            message=f"当前登录态为 {login_state}，无法分享文章",
        )

    # 经发送队列串行执行（避免与发消息 UI 竞态）
    try:
        local_moment_id = await send_queue.enqueue(
            lambda: xdotool.share_article_moment(
                req.public_account,
                article_index=req.article_index,
                comment=req.comment,
            )
        )
    except BridgeError:
        raise
    except Exception as e:
        raise BridgeError(
            code="SEND_FAILED",
            message=f"分享文章失败: {e}",
        )

    logger.info(
        "moments/share_article: public_account=%s index=%d comment_len=%d → 成功 local_moment_id=%s",
        req.public_account, req.article_index,
        len(req.comment) if req.comment else 0,
        local_moment_id,
    )
    return MomentShareArticleResponse(
        success=True,
        local_moment_id=local_moment_id,
        placeholder=False,
        error=None,
    )


# ---------------------------------------------------------------------------
# 路由：POST /api/moments/like (experimental)
# ---------------------------------------------------------------------------
@router.post("/api/moments/like", response_model=MomentLikeResponse)
async def moment_like(req: MomentLikeRequest) -> MomentLikeResponse:
    """给朋友圈点赞（experimental）。

    自动进入朋友圈页面后执行点赞操作。

    Args:
        req: MomentLikeRequest（moment_index，1=最新）

    Returns:
        MomentLikeResponse

    Raises:
        BridgeError(INVALID_PARAMS): moment_index < 1（HTTP 400）
        BridgeError(WECHAT_NOT_LOGGED_IN): 未登录（HTTP 401）
        BridgeError(WINDOW_NOT_FOUND / SEND_FAILED)

    Notes:
        - experimental：朋友圈"赞/评论"按钮坐标为估算值
        - 自动进入朋友圈页面，无需调用方预先进入
    """
    if req.moment_index < 1:
        raise BridgeError(
            code="INVALID_PARAMS",
            message=f"moment_index 必须 >= 1，收到 {req.moment_index}",
        )

    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.moment_like(req.moment_index))
    except BridgeError:
        raise
    except Exception as e:
        raise BridgeError(
            code="SEND_FAILED",
            message=f"点赞失败: {e}",
        )

    logger.info("moments/like: index=%d → UI 操作完成", req.moment_index)
    return MomentLikeResponse(success=True, error=None)


# ---------------------------------------------------------------------------
# 路由：POST /api/moments/comment (experimental)
# ---------------------------------------------------------------------------
@router.post("/api/moments/comment", response_model=MomentCommentResponse)
async def moment_comment(req: MomentCommentRequest) -> MomentCommentResponse:
    """评论朋友圈（experimental）。

    自动进入朋友圈页面后执行评论操作。

    Args:
        req: MomentCommentRequest

    Returns:
        MomentCommentResponse

    Raises:
        BridgeError(INVALID_PARAMS): comment 为空或 moment_index < 1（HTTP 400）
        BridgeError(WECHAT_NOT_LOGGED_IN): 未登录（HTTP 401）
        BridgeError(WINDOW_NOT_FOUND / SEND_FAILED)

    Notes:
        - experimental：坐标为估算值
        - 自动进入朋友圈页面，无需调用方预先进入
    """
    if not req.comment:
        raise BridgeError(code="INVALID_PARAMS", message="comment 不能为空")
    if req.moment_index < 1:
        raise BridgeError(
            code="INVALID_PARAMS",
            message=f"moment_index 必须 >= 1，收到 {req.moment_index}",
        )

    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.moment_comment(req.comment, req.moment_index)
        )
    except BridgeError:
        raise
    except Exception as e:
        raise BridgeError(
            code="SEND_FAILED",
            message=f"评论失败: {e}",
        )

    logger.info(
        "moments/comment: index=%d comment_len=%d → UI 操作完成",
        req.moment_index, len(req.comment),
    )
    return MomentCommentResponse(success=True, error=None)


# ---------------------------------------------------------------------------
# 路由：POST /api/moments/delete (experimental)
# ---------------------------------------------------------------------------
@router.post("/api/moments/delete", response_model=MomentDeleteResponse)
async def moment_delete(req: MomentDeleteRequest) -> MomentDeleteResponse:
    """删除自己的朋友圈（experimental）。

    自动进入朋友圈页面后执行删除操作，避免在聊天界面误删消息。
    moment_index 应指向自己发的朋友圈。

    Args:
        req: MomentDeleteRequest

    Returns:
        MomentDeleteResponse

    Raises:
        BridgeError(INVALID_PARAMS): moment_index < 1（HTTP 400）
        BridgeError(WECHAT_NOT_LOGGED_IN): 未登录（HTTP 401）
        BridgeError(WINDOW_NOT_FOUND / SEND_FAILED)

    Notes:
        - experimental：长按位置与菜单项坐标为估算值
        - 自动进入朋友圈页面，避免在聊天界面误删消息
        - 只能删除自己发的朋友圈；长按别人的朋友圈不会出现"删除"项
    """
    if req.moment_index < 1:
        raise BridgeError(
            code="INVALID_PARAMS",
            message=f"moment_index 必须 >= 1，收到 {req.moment_index}",
        )

    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.moment_delete(req.moment_index))
    except BridgeError:
        raise
    except Exception as e:
        raise BridgeError(
            code="SEND_FAILED",
            message=f"删除朋友圈失败: {e}",
        )

    logger.info("moments/delete: index=%d → UI 操作完成", req.moment_index)
    return MomentDeleteResponse(success=True, error=None)


# ---------------------------------------------------------------------------
# 路由：POST /api/moments/publish_image (experimental)
# ---------------------------------------------------------------------------
@router.post("/api/moments/publish_image", response_model=MomentPublishImageResponse)
async def publish_moment_with_image(
    req: MomentPublishImageRequest,
) -> MomentPublishImageResponse:
    """发表带图片的朋友圈（experimental）。

    UI 自动化路径：进入朋友圈 → 点相机 → 选"发表图片" → 文件选择器
    粘贴图片路径 → 可选输入文字 → 点"发表" → 返回主界面。

    Args:
        req: MomentPublishImageRequest

    Returns:
        MomentPublishImageResponse

    Raises:
        BridgeError(INVALID_PARAMS): image_path 为空或文件不存在（HTTP 400）
        BridgeError(WECHAT_NOT_LOGGED_IN): 未登录（HTTP 401）
        BridgeError(WINDOW_NOT_FOUND / SEND_FAILED)

    Notes:
        - experimental：相机按钮、文件选择器、发表按钮坐标均为估算值
        - image_path 必须是容器内可访问的绝对路径
    """
    if not req.image_path:
        raise BridgeError(code="INVALID_PARAMS", message="image_path 不能为空")
    if not os.path.isfile(req.image_path) or not os.access(req.image_path, os.R_OK):
        raise BridgeError(
            code="INVALID_PARAMS",
            message=f"图片不存在或不可读: {req.image_path}",
        )

    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:
        local_moment_id = await send_queue.enqueue(
            lambda: xdotool.publish_moment_with_image(req.image_path, req.content)
        )
    except BridgeError:
        raise
    except Exception as e:
        raise BridgeError(
            code="SEND_FAILED",
            message=f"发表图片朋友圈失败: {e}",
        )

    logger.info(
        "moments/publish_image: image=%s content_len=%d → 成功 local_moment_id=%s",
        req.image_path, len(req.content) if req.content else 0, local_moment_id,
    )
    return MomentPublishImageResponse(
        success=True,
        local_moment_id=local_moment_id,
        error=None,
    )
