"""
self_todo_executor.py — TODO 自修复执行器
  - 轮询 health_todos.jsonl 中 status=pending 的条目
  - 执行 fix_action（启动脚本）
  - 成功→标记 completed
  - 失败→ escalation 到 LLM（通过 xmpp_bot 通知）

MoFin self_todo_executor.py 的 AgentsMeeting 适配版。
"""
import json, os, sys, time, subprocess, shlex, urllib.request
from datetime import datetime

try:
    sys.stdout.reconfigure(encoding="utf-8")
    sys.stderr.reconfigure(encoding="utf-8")
except Exception:
    pass

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
GATEWAY_DIR = os.path.dirname(SCRIPT_DIR)
BASE = os.path.dirname(GATEWAY_DIR)
TEMP = os.path.join(BASE, "gateway", "temp")
LOGS = os.path.join(BASE, "gateway", "logs")
PYTHON = r"C:\Users\hmo\AppData\Local\Programs\Python\Python310\python.exe"

TODO_FILE = os.path.join(TEMP, "health_todos.jsonl")
EXECUTOR_LOG = os.path.join(LOGS, "todo_executor.log")


def log(msg):
    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    line = f"{ts} [executor] {msg}"
    print(line, flush=True)
    with open(EXECUTOR_LOG, "a", encoding="utf-8") as f:
        f.write(line + "\n")


def read_pending_todos():
    """读取 health_todos.jsonl 中所有 status=pending 的条目。"""
    todos = []
    if not os.path.exists(TODO_FILE):
        return todos
    try:
        with open(TODO_FILE, "r", encoding="utf-8") as f:
            for line in f:
                line = line.strip()
                if not line:
                    continue
                try:
                    entry = json.loads(line)
                    if entry.get("status") == "pending":
                        todos.append(entry)
                except json.JSONDecodeError:
                    continue
    except Exception as e:
        log(f"读取 TODO 文件失败: {e}")
    return todos


def mark_todo(entry, status, result=""):
    """在 JSONL 中将条目标记为 completed 或 failed。"""
    if not os.path.exists(TODO_FILE):
        return
    entry["status"] = status
    entry["resolved_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    if result:
        entry["result"] = result

    # 重写整个文件，标记匹配条目
    lines = []
    try:
        with open(TODO_FILE, "r", encoding="utf-8") as f:
            for line in f:
                line = line.strip()
                if not line:
                    continue
                try:
                    existing = json.loads(line)
                    # 按 created + service 匹配
                    if (existing.get("created") == entry.get("created")
                            and existing.get("service") == entry.get("service")):
                        lines.append(json.dumps(entry, ensure_ascii=False))
                    else:
                        lines.append(line)
                except json.JSONDecodeError:
                    lines.append(line)
    except Exception as e:
        log(f"重写 TODO 文件失败: {e}")
        return

    try:
        with open(TODO_FILE, "w", encoding="utf-8") as f:
            f.write("\n".join(lines) + "\n")
    except Exception as e:
        log(f"写入 TODO 文件失败: {e}")


def execute_fix(entry):
    """执行修复操作。返回 (success, detail)。"""
    script = entry.get("fix_script", "")
    args = entry.get("fix_args", [])
    cwd = entry.get("fix_cwd", None)
    service = entry.get("service", "unknown")

    if not script or not os.path.exists(script):
        return (False, f"修复脚本不存在: {script}")

    cmd = [PYTHON, script] + list(args)
    log(f"执行修复: {service} → {' '.join(cmd[-3:])}")

    try:
        r = subprocess.run(
            cmd,
            cwd=cwd,
            capture_output=True,
            text=True,
            timeout=30,
            creationflags=subprocess.CREATE_NO_WINDOW,
        )
        if r.returncode == 0:
            return (True, f"exit=0")
        else:
            detail = r.stderr[:200] if r.stderr else r.stdout[:200]
            return (False, f"exit={r.returncode}: {detail}")
    except subprocess.TimeoutExpired:
        return (False, "timeout (30s)")
    except Exception as e:
        return (False, str(e)[:100])


def escalate_to_xmpp(entry, result):
    """通过 xmpp_bot 的 HTTP 桥发送告警到群聊（失败 escalation）。"""
    service = entry.get("service", "?")
    issue = entry.get("issue", "?")
    detail = result[:200]
    payload = json.dumps({
        "message": f"[executor] 修复失败: {service}\n问题: {issue}\n结果: {detail}\n需要人工介入"
    }).encode("utf-8")
    try:
        req = urllib.request.Request(
            "http://127.0.0.1:5802/send",
            data=payload,
            headers={
                "Content-Type": "application/json",
                "X-Api-Key": "xxm_bridge_8f3a2c",
            },
        )
        urllib.request.urlopen(req, timeout=5)
        log(f"已通过 XMPP 发送 escalation: {service}")
    except Exception as e:
        log(f"XMPP escalation 失败: {e}")


def main():
    log("=== TODO Executor 启动 ===")
    todos = read_pending_todos()
    log(f"待处理 TODO: {len(todos)} 条")

    for entry in todos:
        service = entry.get("service", "?")
        issue = entry.get("issue", "?")
        log(f"处理: {service} — {issue}")

        success, detail = execute_fix(entry)
        # "another instance is already running" = 服务已在正常运行，不算失败
        if not success and "another instance is already running" in detail:
            mark_todo(entry, "completed", "already_running")
            log(f"  ✅ {service}: 已在运行，无需修复 ({detail[:60]})")
        elif success:
            mark_todo(entry, "completed", detail)
            log(f"  ✅ {service}: 修复成功 ({detail})")
        else:
            mark_todo(entry, "failed", detail)
            log(f"  ❌ {service}: 修复失败 ({detail}) → escalation")
            try:
                escalate_to_xmpp(entry, detail)
            except Exception as e:
                log(f"  escalation 异常: {e}")

    log(f"=== TODO Executor 完成 ({len(todos)} 条处理) ===")


if __name__ == "__main__":
    main()
