#!/usr/bin/env python3
"""
Heartbeat Check - 系统健康检查脚本
由本地 Qwen3.5 模型调用，分析系统状态
输出人类可读的检查报告，供模型判断是否需要告警
"""
import json, subprocess, pathlib, time

ROOT = pathlib.Path(__file__).parent.parent.absolute()
MT5_COMMON = pathlib.Path.home() / "Library/Application Support/net.metaquotes.wine.metatrader5/drive_c/users/user/AppData/Roaming/MetaQuotes/Terminal/Common/Files"

def check_process(name, pid_command, regex):
    """检查进程是否运行"""
    try:
        result = subprocess.run(pid_command, shell=True, capture_output=True, text=True, timeout=5)
        pids = [l.strip() for l in result.stdout.strip().split('\n') if l.strip() and regex in l]
        return "RUNNING" if pids else "STOPPED", pids
    except:
        return "ERROR", []

def check_oml_server():
    """检查 oMLX 服务器"""
    try:
        import urllib.request
        req = urllib.request.Request(
            "http://127.0.0.1:8000/v1/models",
            headers={"Authorization": "Bearer 786540"}
        )
        with urllib.request.urlopen(req, timeout=3) as r:
            data = json.loads(r.read())
        models = [m["id"] for m in data.get("data", [])]
        return "RUNNING", models
    except Exception as e:
        return f"ERROR: {e}", []

def check_mt5():
    """检查 MT5"""
    try:
        result = subprocess.run(
            "ps aux | grep -i 'metatrader\\|metaquotes' | grep -v grep",
            shell=True, capture_output=True, text=True, timeout=5
        )
        lines = [l for l in result.stdout.strip().split('\n') if l.strip()]
        return "RUNNING", len(lines)
    except:
        return "ERROR", 0

def check_daemon():
    """检查 gold_trader daemon"""
    pid_file = ROOT / "logs/gold_trader.pid"
    if not pid_file.exists():
        return "DEAD", "No PID file"
    try:
        pid = int(pid_file.read_text().strip())
        subprocess.run(["kill", "-0", str(pid)], capture_output=True, timeout=3)
        # 检查进程是否在运行
        result = subprocess.run(
            f"ps -p {pid} -o etime,state",
            shell=True, capture_output=True, text=True, timeout=5
        )
        lines = result.stdout.strip().split('\n')
        if len(lines) >= 2:
            return "RUNNING", lines[1].strip()
        return "RUNNING", f"PID {pid}"
    except FileNotFoundError:
        return "DEAD", "PID file exists but process dead"
    except:
        return "DEAD", "Error checking process"

def check_bridge():
    """检查 bridge 目录"""
    bridge_dir = MT5_COMMON / "gold_autotrade/bridge"
    to_ea = bridge_dir / "to_ea"
    from_ea = bridge_dir / "from_ea"
    status_file = bridge_dir / "terminal_status.json"

    to_files = list(to_ea.glob("*.json")) if to_ea.exists() else []
    from_files = list(from_ea.glob("*.json")) if from_ea.exists() else []

    status_ok = status_file.exists()
    last_update = None
    file_age = None  # seconds since last modification
    if status_ok:
        try:
            file_age = time.time() - status_file.stat().st_mtime
            data = json.loads(status_file.read_text())
            last_update = data.get("updated", "?")
        except:
            last_update = "PARSE_ERROR"
            file_age = None

    return {
        "terminal_status_exists": status_ok,
        "last_update": last_update,
        "file_age_seconds": file_age,
        "pending_requests": len(to_files),
        "pending_responses": len(from_files),
    }

def check_gateway():
    """检查 OpenClaw Gateway"""
    try:
        result = subprocess.run(
            "launchctl list | grep 'ai.openclaw.gateway'",
            shell=True, capture_output=True, text=True, timeout=5
        )
        return "RUNNING" if result.stdout.strip() else "STOPPED"
    except:
        return "ERROR"

def get_system_summary():
    """生成系统健康报告"""
    omlx_status, omlx_models = check_oml_server()
    mt5_status, mt5_info = check_mt5()
    daemon_status, daemon_info = check_daemon()
    bridge_data = check_bridge()
    gateway_status = check_gateway()

    report = {
        "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ"),
        "services": {
            "oMLX_server": {"status": omlx_status, "models": omlx_models},
            "MT5_Wine": {"status": mt5_status, "info": mt5_info},
            "Gold_Trader_Daemon": {"status": daemon_status, "info": daemon_info},
            "OpenClaw_Gateway": {"status": gateway_status},
        },
        "bridge": bridge_data,
        "issues": [],
        "health_score": 100,
    }

    # 检测问题
    if omlx_status != "RUNNING":
        report["issues"].append(f"oMLX服务器离线")
        report["health_score"] -= 30
    if mt5_status != "RUNNING":
        report["issues"].append(f"MT5未运行")
        report["health_score"] -= 30
    if daemon_status != "RUNNING":
        report["issues"].append(f"gold_trader守护进程已停止")
        report["health_score"] -= 25
    if gateway_status != "RUNNING":
        report["issues"].append(f"OpenClaw Gateway未运行")
        report["health_score"] -= 10
    if not bridge_data.get("terminal_status_exists"):
        report["issues"].append(f"MT5持仓状态文件不存在（EA可能未加载）")
        report["health_score"] -= 5
    ea_age = bridge_data.get("file_age_seconds")
    if ea_age is not None and ea_age > 60:
        report["issues"].append(f"EA响应延迟: {ea_age:.0f}秒未更新（K线数据不足 → Bridge通信中断）")
        report["health_score"] -= 15
    if bridge_data.get("pending_requests", 0) > 5:
        report["issues"].append(f"待处理请求过多: {bridge_data['pending_requests']}")
        report["health_score"] -= 5

    report["health_score"] = max(0, report["health_score"])

    return report

if __name__ == "__main__":
    import sys, json as _json
    report = get_system_summary()

    if "--json" in sys.argv:
        print(_json.dumps(report, indent=2, ensure_ascii=False))
    else:
        print("=== 系统健康检查报告 ===")
        print(f"时间: {report['timestamp']}")
        print(f"健康评分: {report['health_score']}/100")
        print()
        for svc, data in report["services"].items():
            status = data["status"]
            icon = "✅" if status == "RUNNING" else "❌"
            info = data.get("info") or data.get("models") or ""
            print(f"  {icon} {svc}: {status} {info}")
        print(f"  Bridge: terminal_status={'✅' if report['bridge'].get('terminal_status_exists') else '❌'} | "
              f"last_update={report['bridge'].get('last_update','?')} | "
              f"pending_req={report['bridge'].get('pending_requests',0)}")
        if report["issues"]:
            print()
            print("⚠️  问题:")
            for issue in report["issues"]:
                print(f"  - {issue}")
        else:
            print()
            print("✅ 所有服务运行正常")

        # 输出 JSON 格式供模型分析
        print()
        print("---JSON---")
        print(_json.dumps(report, ensure_ascii=False))

    # ── 自动修复触发 ──────────────────────────────────────
    # 当检测到 EA 无响应（K线数据不足），自动 ping + 必要时重启
    ea_age = report.get("bridge", {}).get("file_age_seconds")
    if ea_age is not None and ea_age > 60:
        print()
        print("[AUTO-HEAL] 检测到 EA 无响应，触发自动修复...")
        # 调用 health_watchdog 的 heal_ping_ea（同一目录）
        wd_script = pathlib.Path(__file__).parent / "health_watchdog.py"
        if wd_script.exists():
            result = subprocess.run(
                [sys.executable, str(wd_script)],
                capture_output=True, text=True, timeout=30,
                cwd=str(pathlib.Path(__file__).parent.parent),
            )
            # 打印 health_watchdog 的输出（用于日志）
            if result.stdout:
                for line in result.stdout.strip().split("\n")[-5:]:
                    print(f"  {line}")
        else:
            print("[AUTO-HEAL] health_watchdog.py 未找到，请手动检查")
