"""check_process: 查看/终止 shell·run_python `background=true` 启动的后台进程。

host in-process 工具(不进 CONTAINER_TOOLS):状态文件在宿主侧 user_root/.zcbot_procs,
docker 模式的容器探测/回收走宿主 docker CLI —— 两种 backend 一个工具通吃。
"""
from __future__ import annotations

import subprocess
import time
from pathlib import Path
from typing import Optional

from core import procs

from .base import Tool, compact_tool_output


class CheckProcessTool(Tool):
    name = "check_process"
    description = (
        "Check status / output of background processes started with background=true "
        "(shell or run_python), or kill one. "
        "Call without proc_id to list this task's background processes. "
        "With proc_id: returns status + tail of the process log. "
        "If still running: do NOT poll in a tight loop — do other useful work, or end "
        "the turn and tell the user to ask later ('跑完了吗'). "
        "action='kill' force-terminates the process."
    )
    parameters = {
        "type": "object",
        "properties": {
            "proc_id": {
                "type": "string",
                "description": "后台进程 id(启动时返回的 p 开头短串)。缺省 = 列出本 task 全部后台进程。",
            },
            "action": {
                "type": "string",
                "enum": ["status", "kill"],
                "description": "status(默认)= 查状态+日志尾部;kill = 强制终止该进程。",
            },
            "tail_bytes": {
                "type": "integer",
                "description": "返回日志尾部的最大字节数,默认 4000,上限 20000。",
            },
        },
        "required": [],
    }

    def __init__(
        self,
        *,
        task_id: str,
        base_dir: Optional[Path] = None,
        user_root: Optional[Path] = None,
    ) -> None:
        super().__init__(base_dir, user_root=user_root)
        self.task_id = str(task_id)

    @property
    def _anchor(self) -> Path:
        # 常规部署有 user_root;CLI --working-dir 指外部目录时退 base_dir
        return self.user_root or self.base_dir

    def execute(
        self,
        proc_id: Optional[str] = None,
        action: str = "status",
        tail_bytes: int = 4000,
    ) -> str:
        if not proc_id:
            items = procs.list_procs(self._anchor, self.task_id)
            if not items:
                return "本 task 没有后台进程。用 shell/run_python 的 background=true 可启动。"
            return "后台进程列表:\n" + "\n".join(procs.format_proc_line(m) for m in items)

        d = procs.proc_dir(self._anchor, self.task_id, proc_id.strip())
        if d is None or not d.is_dir():
            return f"[Error] 后台进程不存在: {proc_id!r}(用 check_process 不带参数列出现有进程)"
        meta = procs.read_meta(d)
        if meta is None:
            return f"[Error] 后台进程元数据损坏: {proc_id!r}"

        if action == "kill":
            msg = procs.kill_proc(meta, d)
            return f"[check_process] {proc_id}: {msg}"

        st, ec = procs.status_of(meta, d)
        tail_bytes = max(500, min(int(tail_bytes or 4000), 20_000))
        tail = procs.tail_log(d, max_bytes=tail_bytes)

        # docker 模式:已结束但专用容器还在空转 → 顺手回收(幂等,sweep 也会兜底)
        if st != "running" and meta.get("backend") == "docker":
            name = str(meta.get("container") or "")
            if name:
                try:
                    subprocess.run(["docker", "rm", "-f", name],
                                   capture_output=True, timeout=30)
                except (OSError, subprocess.TimeoutExpired):
                    pass

        header = f"[check_process] {proc_id} · {meta.get('kind')} · {meta.get('command', '')[:150]}"
        if st == "running":
            elapsed = procs._fmt_elapsed(time.time() - float(meta.get("created_ts") or time.time()))
            body = (
                f"状态: running(已运行 {elapsed},上限 {meta.get('timeout_s')}s)\n"
                f"--- 日志尾部 ---\n{tail}\n"
                f"提示:仍在运行。别循环轮询 —— 先做别的工作,或结束回合告知用户稍后询问进度。"
            )
        elif st == "finished":
            note = " (timeout killed)" if ec == 124 else (" (killed)" if meta.get("killed") else "")
            body = f"状态: finished, exit_code={ec}{note}\n--- 日志尾部 ---\n{tail}"
        else:
            body = (
                f"状态: lost(进程已不在,且未留下退出码 —— 可能宿主重启把它带走了)\n"
                f"--- 日志尾部(截至中断) ---\n{tail}\n"
                f"如需结果请重新以 background=true 启动。"
            )
        return compact_tool_output(f"{header}\n{body}")
