"""WeChat 进程看门狗：10s 检查间隔，连续 2 次失败触发 autofix。

autofix 策略：尝试重启 WeChat 进程（通过 backend.activate_window 或 pgrep+kill）。
失败仅告警不抛异常，避免拖垮 bridge 主流程。
"""

from __future__ import annotations

import asyncio
import logging
import time
from typing import Optional

from woc_bridge.ui.backends.base import BackendProtocol

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


class WeChatWatchdog:
    """WeChat 进程看门狗。

    Attributes:
        backend: L1 Backend 实例（用于 pgrep/kill 等子进程操作）
        interval: 检查间隔秒数，默认 10.0
        fail_threshold: 连续失败触发 autofix 的阈值，默认 2
    """

    def __init__(
        self,
        backend: BackendProtocol,
        interval: float = 10.0,
        fail_threshold: int = 2,
    ) -> None:
        self.backend = backend
        self.interval = interval
        self.fail_threshold = fail_threshold
        self._fail_count = 0
        self._task: Optional[asyncio.Task] = None
        self._stopped = False

    async def start(self) -> None:
        """启动看门狗后台任务。"""
        if self._task is not None and not self._task.done():
            return
        self._stopped = False
        self._task = asyncio.create_task(self._run())
        logger.info("[watchdog] started (interval=%.1fs)", self.interval)

    async def stop(self) -> None:
        """停止看门狗。"""
        self._stopped = True
        if self._task is not None and not self._task.done():
            self._task.cancel()
            try:
                await asyncio.wait_for(self._task, timeout=2.0)
            except (asyncio.TimeoutError, asyncio.CancelledError):
                pass
        self._task = None
        logger.info("[watchdog] stopped")

    async def _run(self) -> None:
        """主循环：每 interval 秒检查一次。"""
        while not self._stopped:
            try:
                await asyncio.sleep(self.interval)
                await self._check()
            except asyncio.CancelledError:
                break
            except Exception as exc:
                logger.warning("[watchdog] check exception: %s", exc)

    async def _check(self) -> None:
        """执行一次检查：WeChat 运行态 + X11 可用性。"""
        wechat_ok = await self._is_wechat_running()
        x11_ok = await self._is_x11_available()

        if wechat_ok and x11_ok:
            if self._fail_count > 0:
                logger.info(
                    "[watchdog] recovered after %d failures", self._fail_count
                )
            self._fail_count = 0
            return

        self._fail_count += 1
        logger.warning(
            "[watchdog] check failed (wechat=%s x11=%s) fail_count=%d/%d",
            wechat_ok, x11_ok, self._fail_count, self.fail_threshold,
        )

        if self._fail_count >= self.fail_threshold:
            logger.error(
                "[watchdog] fail threshold reached, trigger autofix"
            )
            await self._autofix_wechat()
            # autofix 后重置计数，等下一轮检查
            self._fail_count = 0

    async def _is_wechat_running(self) -> bool:
        """检查 WeChat 进程是否运行（用 pgrep -x wechat）。"""
        try:
            proc = await asyncio.create_subprocess_exec(
                "pgrep", "-x", "wechat",
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE,
            )
            try:
                stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=3.0)
            except asyncio.TimeoutError:
                proc.kill()
                await proc.wait()
                return False
            return proc.returncode == 0 and bool(stdout.decode(errors="ignore").strip())
        except Exception as exc:
            logger.debug("[watchdog] pgrep wechat exception: %s", exc)
            return False

    async def _is_x11_available(self) -> bool:
        """检查 X11 是否可用（用 xdpyinfo）。"""
        try:
            proc = await asyncio.create_subprocess_exec(
                "xdpyinfo",
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE,
            )
            try:
                await asyncio.wait_for(proc.communicate(), timeout=3.0)
            except asyncio.TimeoutError:
                proc.kill()
                await proc.wait()
                return False
            return proc.returncode == 0
        except Exception as exc:
            logger.debug("[watchdog] xdpyinfo exception: %s", exc)
            return False

    async def _autofix_wechat(self) -> None:
        """自动修复：尝试 kill WeChat 让 autostart 拉起。

        不直接启动 WeChat（避免与 autostart 竞态），仅 kill 旧进程。
        所有子进程超时均执行 proc.kill() 收尸，避免僵尸进程泄漏。
        """
        logger.warning("[watchdog] autofix: killing wechat for restart")
        try:
            # 获取所有 wechat PID
            proc = await asyncio.create_subprocess_exec(
                "pgrep", "-x", "wechat",
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE,
            )
            try:
                stdout, _ = await asyncio.wait_for(
                    proc.communicate(), timeout=3.0
                )
            except asyncio.TimeoutError:
                proc.kill()
                await proc.wait()
                logger.warning("[watchdog] autofix pgrep timeout")
                return
            pids = []
            for line in stdout.decode(errors="ignore").splitlines():
                line = line.strip()
                if line:
                    try:
                        pids.append(int(line))
                    except ValueError:
                        pass
            # kill 所有 wechat 进程
            for pid in pids:
                try:
                    kill_proc = await asyncio.create_subprocess_exec(
                        "kill", "-TERM", str(pid),
                        stdout=asyncio.subprocess.PIPE,
                        stderr=asyncio.subprocess.PIPE,
                    )
                    try:
                        await asyncio.wait_for(
                            kill_proc.communicate(), timeout=3.0
                        )
                    except asyncio.TimeoutError:
                        kill_proc.kill()
                        await kill_proc.wait()
                    logger.info(
                        "[watchdog] autofix killed wechat pid=%d", pid
                    )
                except Exception as exc:
                    logger.warning(
                        "[watchdog] autofix kill pid=%d failed: %s", pid, exc
                    )
        except Exception as exc:
            logger.error("[watchdog] autofix exception: %s", exc)
