#!/usr/bin/env python3
"""verify_reassess_pipeline.py — 重评推送管道审计 + 全局cron失败监控

检查：
1. price_monitor 每2分正常跑
2. zone breach检测正常
3. holding_strategies有数据
4. XMPP bridge在线
5. reassess模块可导入
6. 【新增】所有关键cron job状态（是否有failed）

输出：正常时 [SILENT]，有异常时推XMPP
"""
import json, os, sys, subprocess, sqlite3
from pathlib import Path
from datetime import datetime, timedelta
from urllib.request import Request, urlopen

BASE = Path(__file__).parent.parent
sys.path.insert(0, str(BASE))
sys.path.insert(0, "/home/hmo/MoFin")

XMPP_BRIDGE = "http://127.0.0.1:5805/"
XMPP_USER = "hmo@yoin.fun"

def xmpp_push(text):
    try:
        payload = json.dumps({"to": XMPP_USER, "body": text, "type": "chat"}).encode()
        req = Request(XMPP_BRIDGE, data=payload, headers={"Content-Type": "application/json"})
        urlopen(req, timeout=5)
    except Exception as e:
        print(f"[XMPP推送失败] {e}", file=sys.stderr)

def scan_cron_failures():
    """扫描两个cron jobs.json看是否有failed状态的关键job"""
    failures = []
    jobs_files = [
        "/home/hmo/.hermes/cron/jobs.json",
        "/home/hmo/.hermes/profiles/position-analyst/cron/jobs.json",
    ]
    for jf in jobs_files:
        try:
            data = json.load(open(jf))
            for job in data.get("jobs", []):
                jid = job.get("id", "?")
                name = job.get("name", "") or jid[:12]
                status = job.get("last_status", "")
                enabled = job.get("enabled", True)
                if not enabled:
                    continue
                # 关键job：价格监控、重评、盘前中监控
                key_job = any(kw in name.lower() for kw in [
                    "price_monitor", "monitor", "盘前中", "reassess",
                    "重评", "自选买入", "stale_push", "管道审计",
                    "宏观风险", "策略时效"
                ])
                if not key_job:
                    continue
                if status == "failed":
                    last_run = job.get("last_run_at", "?")
                    failures.append(f"  ❌ {name} ({jid[:8]}) last_run={last_run}")
        except Exception:
            pass
    return failures

def check_cron_jobs():
    """另法：直接查cron数据库"""
    issues = []
    for db_path in [
        BASE / "cron" / "cron.db",
        Path("/home/hmo/.hermes/cron/cron.db"),
    ]:
        if not db_path.exists():
            continue
        try:
            c = sqlite3.connect(str(db_path))
            for row in c.execute("""
                SELECT id, name, last_status, last_run_at, enabled
                FROM cron_jobs WHERE enabled=1
                ORDER BY last_run_at DESC
            """).fetchall():
                jid, name, status, last_run, enabled = row
                if status == "failed":
                    issues.append(f"  ❌ {name}({jid[:8]}) last_run={last_run}")
            c.close()
        except Exception:
            pass
    return issues

def run():
    ok = True
    alerts = []
    checks = []

    # 1. price_monitor 最近运行时间
    try:
        conn = sqlite3.connect("/home/hmo/MoFin/data/mofin.db")
        lp = conn.execute("SELECT MAX(updated_at) FROM live_prices").fetchone()[0]
        if lp:
            lp_dt = datetime.fromisoformat(lp) if isinstance(lp, str) else lp
            if hasattr(lp_dt, 'tzinfo') and lp_dt.tzinfo is None:
                if isinstance(lp, str) and '+' not in lp:
                    lp_dt = lp_dt.replace(tzinfo=None)
            mins_ago = (datetime.now() - lp_dt).total_seconds() / 60
            status = "ok" if mins_ago < 10 else "warn"
            if mins_ago > 15:
                status = "fail"
                ok = False
                alerts.append(f"price_monitor {mins_ago:.0f}分未更新")
            checks.append({"check":"price_monitor","status":status,"detail":f"最后更新{mins_ago:.0f}分前"})
        else:
            checks.append({"check":"price_monitor","status":"warn","detail":"live_prices无数据"})
    except Exception as e:
        checks.append({"check":"price_monitor","status":"fail","detail":str(e)})
        ok = False
        alerts.append(f"price_monitor异常: {e}")

    # 2. 策略评估活动（reassess_with_context写strategy_evaluations，不是holding_strategies）
    try:
        today_se = conn.execute("SELECT COUNT(*) FROM strategy_evaluations WHERE date(created_at)=date('now')").fetchone()[0]
        total_se = conn.execute("SELECT COUNT(*) FROM strategy_evaluations").fetchone()[0]
        # 也尝试查holding_strategies（如果存在并有数据）
        hs_exists = conn.execute("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='holding_strategies'").fetchone()[0]
        hs = 0
        if hs_exists:
            hs = conn.execute("SELECT COUNT(*) FROM holding_strategies").fetchone()[0]
        detail = f"今日{today_se}次评估, 累计{total_se}条"
        if hs > 0:
            detail += f", holding_strategies{hs}条"
        checks.append({"check":"strategy_activity","status":"ok","detail":detail})
    except Exception as e:
        checks.append({"check":"strategies","status":"fail","detail":str(e)})
        ok = False

    # 3. XMPP bridge 是否在线（TCP端口检测，不发消息到Dad）
    try:
        import socket
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(3)
        result = sock.connect_ex(("127.0.0.1", 5805))
        sock.close()
        bridge_ok = (result == 0)
        if not bridge_ok:
            ok = False
            alerts.append("XMPP bridge(5805)端口无响应")
        checks.append({"check":"xmpp_bridge","status":"ok" if bridge_ok else "fail","detail":"在线" if bridge_ok else "端口无响应"})
    except Exception as e:
        checks.append({"check":"xmpp_bridge","status":"fail","detail":str(e)})
        ok = False
        alerts.append(f"XMPP bridge不可达: {e}")

    # 4. reassess模块可导入
    try:
        from strategy_lifecycle import reassess_with_context
        checks.append({"check":"reassess_module","status":"ok","detail":"可导入"})
    except Exception as e:
        checks.append({"check":"reassess_module","status":"fail","detail":str(e)})
        ok = False
        alerts.append(f"reassess模块导入失败: {e}")

    # 5. cron job失败检测
    cron_issues = scan_cron_failures() + check_cron_jobs()
    if cron_issues:
        ok = False
        alerts.append(f"{len(cron_issues)}个cron job失败")
        for issue in cron_issues[:5]:
            alerts.append(issue)
        checks.append({"check":"cron_jobs","status":"fail","detail":"; ".join(cron_issues[:3])})
    else:
        checks.append({"check":"cron_jobs","status":"ok","detail":"所有关键job正常"})

    conn.close()

    # 输出
    result = {
        "pipeline": "ok" if ok else "degraded",
        "checked_at": datetime.now().isoformat(),
        "checks": checks,
        "alerts": alerts
    }

    if ok:
        print("[SILENT]")
    else:
        msg = "🔴 重评管道异常:\n" + "\n".join(alerts)
        print(json.dumps(result, ensure_ascii=False, indent=2))
        # 有异常时主动推XMPP（取代静默）
        xmpp_push(msg)
        print(f"\n已推送XMPP: {len(alerts)}条告警", file=sys.stderr)

if __name__ == "__main__":
    run()
