from __future__ import annotations

import asyncio
import logging
import time

from fastapi import APIRouter

from woc_bridge.config import (
    _require_xdotool,
    _require_qr_capture,
    _require_db_reader,
)
from woc_bridge.db.coordinator import with_db_retry
from woc_bridge.models import (
    QrLoginStartResult,
    QrLoginWaitResult,
    LogoutResponse,
    RestartResponse,
    BridgeError,
    LoginState,
)

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


@router.post("/api/login/qr/start", response_model=QrLoginStartResult)
async def login_qr_start() -> QrLoginStartResult:
    """启动扫码登录：截取二维码区域并返回 data URL。

    channels LoginAdapter 的扫码入口。调用本接口后，调用方应把 qr_data_url
    渲染给用户扫描，然后立即调 GET /api/login/qr/wait 轮询登录结果。

    Returns:
        QrLoginStartResult：含 qr_data_url（data:image/png;base64,...） /
        message / connected=false

    Raises:
        BridgeError(WINDOW_NOT_FOUND): 微信窗口未找到，无法截图（HTTP 503）
        其他异常由全局兜底处理器返回 BRIDGE_INTERNAL_ERROR

    Notes:
        - 激活微信窗口后再截图，确保二维码可见（不被其他窗口遮挡）
        - 二维码有时效（微信约 60s 刷新），调用方应在 qr/wait 超时后
          重新调本接口获取新二维码
        - 若微信已登录，本接口仍会返回二维码截图（调用方应先调
          /api/status 判断 login_state，避免重复登录）
    """
    xdotool = _require_xdotool()
    qr_capture = _require_qr_capture()

    # 激活微信窗口（非阻塞，避免 VNC 无人操作时 --sync 死等）
    await xdotool._activate_window_fast()
    qr_data_url = await qr_capture.capture_qr_code()

    return QrLoginStartResult(
        qr_data_url=qr_data_url,
        message="请使用微信扫描二维码",
        connected=False,
    )


# ---------------------------------------------------------------------------
# 路由：GET /api/login/qr/wait
# ---------------------------------------------------------------------------
@router.get("/api/login/qr/wait", response_model=QrLoginWaitResult)
@with_db_retry
async def login_qr_wait(timeout: int = 30) -> QrLoginWaitResult:
    """轮询登录态，等待扫码登录完成。

    每 2 秒调一次 detect_login_state，直到 logged_in / not_running / 超时。
    长轮询模式（响应在登录成功或超时后才返回），调用方无需在 client 端做
    轮询间隔控制，直接发请求阻塞等待即可。

    Args:
        timeout: 最大等待秒数，默认 30，上限 120（防止长时间占用连接）

    Returns:
        QrLoginWaitResult：含 connected / message / qr_data_url（成功时为空）
        / credentials（成功时含 wxid + nickname，否则为 None）

    Raises:
        BridgeError(INVALID_PARAMS): timeout < 1（HTTP 400）

    Notes:
        - 微信进程未运行（not_running）时立即返回，不继续等待（继续等无意义）
        - 登录成功时尝试从 DB 读 wxid/nickname；DB 不可达时返回空字符串
          （credentials 不为 None，但 wxid 为空）
        - 超时返回 connected=false，message="等待扫码超时"，调用方应重新调
          POST /api/login/qr/start 获取新二维码（旧二维码可能已过期）
    """
    if timeout < 1:
        raise BridgeError(code="INVALID_PARAMS", message="timeout 必须 >= 1")
    if timeout > 120:
        timeout = 120

    xdotool = _require_xdotool()
    db_reader = _require_db_reader()

    deadline = time.monotonic() + timeout
    while time.monotonic() < deadline:
        state = await xdotool.detect_login_state()
        if state == "logged_in":
            # 登录成功：从 DB 读取 wxid / nickname
            try:
                self_info = await asyncio.to_thread(db_reader.get_self_info)
                wxid = self_info.get("wxid", "")
                nickname = self_info.get("nickname", "")
            except Exception:
                wxid = ""
                nickname = ""
            logger.info("login/qr/wait: → 登录成功 wxid=%s nickname=%s", wxid, nickname)
            return QrLoginWaitResult(
                connected=True,
                message="登录成功",
                qr_data_url="",
                credentials={"wxid": wxid, "nickname": nickname},
            )
        if state == "not_running":
            # 微信进程未运行，继续等无意义，直接返回
            logger.warning("login/qr/wait: → 微信进程未运行")
            return QrLoginWaitResult(
                connected=False,
                message="微信进程未运行，无法扫码登录",
                qr_data_url="",
                credentials=None,
            )
        # not_logged_in / logging_in：继续等待
        await asyncio.sleep(2)

    # 超时
    logger.warning("login/qr/wait: → 等待扫码超时（%ds）", timeout)
    return QrLoginWaitResult(
        connected=False,
        message="等待扫码超时",
        qr_data_url="",
        credentials=None,
    )


# ---------------------------------------------------------------------------
# 路由：POST /api/login/logout
# ---------------------------------------------------------------------------
@router.post("/api/login/logout", response_model=LogoutResponse)
async def login_logout() -> LogoutResponse:
    """退出微信登录（channels LifecycleAdapter 调用）。

    通过 UI 操作退出登录，避免直接 kill 进程导致登录态文件未刷盘。
    幂等：未登录时直接返回成功（不抛错），方便调用方无需先查状态再调用。

    流程（仅 logged_in 时执行）：
    1. detect_login_state 确认登录态
    2. xdotool.logout() 执行 UI 操作：激活窗口 → 点击主菜单 → 方向键导航
       到「退出登录」→ 回车 → 确认对话框（具体坐标/次数为估算值，需实测调优）

    Returns:
        LogoutResponse：success=true / message（"已退出登录" 或 "当前未登录，无需退出"）

    Raises:
        BridgeError(LOGOUT_FAILED): 窗口未找到或 UI 操作失败（HTTP 500）

    Notes:
        - 幂等：not_running / not_logged_in 状态下都返回 success=true
        - logout() 的 UI 坐标/方向键次数为估算值，spec 已记录需实测调优
        - 退出后微信进程仍在运行（只是登录态变 not_logged_in），autostart
          不会拉起新进程；如需重新登录走 /api/login/qr/start
        - 不删除任何文件：登录态由微信自身管理，bridge 不破坏数据
    """
    xdotool = _require_xdotool()

    # 记录调用前的登录态（用于返回 message）
    state_before = await xdotool.detect_login_state()

    # 若已登录，执行 UI 退出操作
    if state_before == LoginState.LOGGED_IN.value:
        try:
            await xdotool.logout()
        except BridgeError:
            raise
        except Exception as e:
            raise BridgeError(
                code="LOGOUT_FAILED",
                message=f"退出登录失败: {e}",
            )
        return LogoutResponse(success=True, message="已退出登录")

    # 未登录，幂等返回
    return LogoutResponse(success=True, message="当前未登录，无需退出")


# ---------------------------------------------------------------------------
# 路由：POST /api/wechat/restart
# ---------------------------------------------------------------------------
@router.post("/api/wechat/restart", response_model=RestartResponse)
async def wechat_restart() -> RestartResponse:
    """重启微信进程（不破坏登录态，数据卷保留）。

    流程：
    1. pgrep -x wechat 获取当前所有 PID
    2. 对每个 PID 发 SIGTERM（不强制 SIGKILL，给微信优雅退出机会，避免 DB 写入未刷盘）
    3. 轮询 pgrep -x wechat，等待新 PID 出现（autostart 2 秒后拉起）
    4. 30 秒内未出现新进程抛 RESTART_TIMEOUT

    与 docker stop/start 的区别：
    - 本接口只重启微信进程，不重启容器，X 会话/VNC 连接保持，速度更快（~5s）
    - docker stop 会杀整个容器，VNC 断连，需重新连接

    Returns:
        RestartResponse：success=true / message（"微信已重启" 或 "微信已启动"）
        / pid（新进程 PID）

    Raises:
        BridgeError(RESTART_TIMEOUT): 30 秒内未检测到新进程（HTTP 408）
        BridgeError(BRIDGE_INTERNAL_ERROR): pgrep 等命令异常（HTTP 500）

    Notes:
        - 数据卷保留，登录态不丢（除非 SIGTERM 期间微信主动退出登录）
        - 不提供 stop/start 接口：autostart watchdog 会立即拉起被 stop
          的微信进程，stop 没有意义；启动由 autostart 管理
        - 若 was_running=false（重启前未运行），message="微信已启动"
        - 调用方应在收到 success=true 后调 /api/status 确认 login_state
          恢复到 logged_in（autostart 拉起后微信会自动尝试恢复登录）
    """
    xdotool = _require_xdotool()

    # 记录调用前是否在运行
    was_running = await xdotool.is_wechat_running()

    try:
        new_pid = await xdotool.restart_wechat(timeout_sec=30)
    except BridgeError:
        raise
    except Exception as e:
        # 非 timeout 的意外错误（如 pgrep 不可用）归为内部错误，
        # 不误报为 RESTART_TIMEOUT（调用方会按 timeout 语义重试，无意义）
        raise BridgeError(
            code="BRIDGE_INTERNAL_ERROR",
            message=f"重启微信失败: {e}",
        )

    message = "微信已重启" if was_running else "微信已启动"
    return RestartResponse(success=True, message=message, pid=new_pid)
