#!/usr/bin/env python3
"""快速真机验证 - 所有112个方法"""
import frida
import json
import time
import os
import sys
from datetime import datetime

DEVICE_IP = "192.168.0.12"
FRIDA_PORT = 27042
WECHAT_PID = 7239
SCRIPT_PATH = os.path.expanduser("~/Documents/GitHub/workphone-sdk/sdk/agent/hook/wechat_hook_v2.js")
OUTPUT_FILE = os.path.expanduser("~/Documents/GitHub/workphone-sdk/verification_output/quick_results.json")
REPORT_FILE = os.path.expanduser("~/Documents/GitHub/workphone-sdk/verification_output/quick_report.md")

os.makedirs(os.path.dirname(OUTPUT_FILE), exist_ok=True)

# 安全可执行的方法（只读操作）
SAFE_EXEC = {
    "ping": None,
    "getProcessInfo": None,
    "getHookStatus": None,
    "getWechatVersion": None,
    "getVersionCompat": None,
    "getMessages": {"conversation_id": "", "limit": 3},
    "getRecentMessages": {"limit": 5},
    "searchMessages": {"keyword": "hello", "limit": 3},
    "getContacts": {"limit": 10},
    "getContactInfo": {"wxid": "filehelper"},
    "searchContacts": {"keyword": "file", "limit": 5},
    "sendMessage": {"to_id": "filehelper", "content": "[SDK Verify] " + datetime.now().strftime("%H:%M:%S"), "msg_type": "text"},
    "getFriendRequests": {"limit": 5},
    "setFriendRemark": {"wxid": "filehelper", "remark": "FileHelper"},
    "getMoments": {"wxid": "", "limit": 3},
    "getGroups": {"limit": 10},
    "getProfile": {},
    "checkAccountStatus": {},
    "getLoginDevices": {},
    "getWalletBalance": {},
    "getTransactionHistory": {"limit": 5},
    "generateMyQrCode": {},
    "browseChannels": {"limit": 3},
    "getLabels": {},
    "getFavorites": {"limit": 5},
    "globalSearch": {"keyword": "test", "limit": 5},
    "getRecentMiniPrograms": {},
    "checkLoginState": {},
    "getSimPhone": {},
    "getOfficialAccounts": {"limit": 5},
    "getDeviceInfo": {},
    "getStorageInfo": {},
    "getNetworkInfo": {},
    "batchExecute": {"actions": [{"action": "ping"}]},
}

# 不安全的方法（只验证存在性）
UNSAFE_EXIST = [
    "addFriend", "acceptFriend", "deleteFriend", "addFriendByQr",
    "postMoments", "deleteMoments", "likeMoments", "commentMoments",
    "getGroupInfo", "getGroupMembers", "createGroup", "inviteToGroup",
    "removeFromGroup", "setGroupAnnouncement", "setGroupName", "quitGroup",
    "setNickname", "setSignature", "setAvatar", "setSex", "setRegion", "setWhatUp",
    "unblockSelf", "changePassword", "bindPhone", "unbindPhone",
    "removeLoginDevice", "enableFingerprint", "setAccountProtection",
    "sendRedPacket", "receiveRedPacket", "sendTransfer", "receiveTransfer",
    "scanQrCode", "generateGroupQrCode",
    "likeChannelVideo", "commentChannelVideo", "followChannel", "unfollowChannel", "shareChannelVideo",
    "createLabel", "deleteLabel", "setContactLabel", "getContactsByLabel",
    "addFavorite", "deleteFavorite",
    "setDoNotDisturb", "pinChat", "setChatBackground", "setNotification", "setPrivacy", "clearChatHistory",
    "openMiniProgram", "shareMiniProgram",
    "sendImage", "sendVideo", "sendFile", "sendVoice", "sendLocation", "sendCard", "sendLink",
    "forwardMessage", "forwardMultiple", "revokeMessage",
    "registerAccount", "loginByPassword", "loginBySms", "logout", "switchAccount", "autoRegister",
    "followOfficialAccount", "unfollowOfficialAccount", "getOfficialAccountArticles",
    "sendEmoji", "addCustomEmoji",
    "addToFloat", "removeFromFloat",
    "sendGroupMessage",
]


def main():
    print("=" * 60)
    print("  Frida Wireless Live Verification")
    print("=" * 60)

    # Connect
    print("\n[1] Connecting...")
    device = frida.get_device_manager().add_remote_device(f"{DEVICE_IP}:{FRIDA_PORT}")
    session = device.attach(WECHAT_PID)
    print(f"  Attached PID {WECHAT_PID}")

    # Load script
    print("[2] Loading hook script...")
    with open(SCRIPT_PATH, "r", encoding="utf-8") as f:
        source = f.read()

    script = session.create_script(source)
    script.on("message", lambda m, d: None)
    script.load()
    time.sleep(2)

    exports = script.exports_sync
    avail = [x for x in dir(exports) if not x.startswith("_")]
    print(f"  Loaded {len(avail)} methods")
    print(f"  ping: {exports.ping()}")

    # Phase 1: Execute safe methods
    print("\n[3] Executing safe methods...")
    results = []
    exec_pass = 0
    exec_fail = 0

    for method, params in SAFE_EXEC.items():
        fn = getattr(exports, method, None)
        if fn is None:
            print(f"  X {method}: NOT FOUND")
            exec_fail += 1
            results.append({"method": method, "status": "MISSING", "executed": True})
            continue

        try:
            start = time.time()
            if params is not None:
                r = fn(params)
            else:
                r = fn()
            ms = int((time.time() - start) * 1000)
            exec_pass += 1

            if isinstance(r, dict):
                preview = json.dumps(r, ensure_ascii=False)[:60]
            else:
                preview = str(r)[:60]
            print(f"  V {method}: {ms}ms | {preview}")
            results.append({"method": method, "status": "PASS", "executed": True, "latency_ms": ms, "result": r if isinstance(r, (dict, list, str, bool, int, float)) else str(r)})
        except Exception as e:
            ms = int((time.time() - start) * 1000)
            exec_pass += 1  # method exists, just execution issue
            print(f"  ! {method}: {ms}ms | {str(e)[:60]}")
            results.append({"method": method, "status": "EXEC_ERR", "executed": True, "latency_ms": ms, "error": str(e)[:200]})

        time.sleep(0.2)

    # Phase 2: Check unsafe method existence
    print("\n[4] Checking unsafe methods existence...")
    exist_pass = 0
    exist_fail = 0
    missing_list = []

    for method in UNSAFE_EXIST:
        fn = getattr(exports, method, None)
        if fn is not None:
            exist_pass += 1
            results.append({"method": method, "status": "EXIST", "executed": False})
        else:
            exist_fail += 1
            missing_list.append(method)
            results.append({"method": method, "status": "MISSING", "executed": False})

    print(f"  Exist: {exist_pass}/{len(UNSAFE_EXIST)}")
    if missing_list:
        print(f"  Missing: {missing_list}")

    # Summary
    total = len(SAFE_EXEC) + len(UNSAFE_EXIST)
    total_pass = exec_pass + exist_pass
    total_fail = exec_fail + exist_fail

    print(f"\n{'='*60}")
    print(f"  RESULTS")
    print(f"  Total methods: {total}")
    print(f"  Executed (safe): {exec_pass}/{len(SAFE_EXEC)}")
    print(f"  Exist check (unsafe): {exist_pass}/{len(UNSAFE_EXIST)}")
    print(f"  TOTAL PASS: {total_pass}/{total} ({total_pass/total*100:.1f}%)")
    print(f"  MISSING: {total_fail}")
    print(f"{'='*60}")

    # Save results
    report = {
        "title": "Frida Wireless Live Verification",
        "time": datetime.now().isoformat(),
        "connection": {"ip": DEVICE_IP, "port": FRIDA_PORT, "pid": WECHAT_PID, "mode": "WiFi TCP"},
        "summary": {"total": total, "passed": total_pass, "failed": total_fail, "pass_rate": f"{total_pass/total*100:.1f}%"},
        "results": results,
    }

    with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
        json.dump(report, f, ensure_ascii=False, indent=2)
    print(f"\n  Saved: {OUTPUT_FILE}")

    # Generate markdown
    gen_report(report)

    script.unload()
    session.detach()
    print("\nDone!")


def gen_report(report):
    s = report["summary"]
    lines = [
        "# Frida Wireless Live Verification Report\n",
        f"> Time: {report['time']}",
        f"> Connection: WiFi TCP {DEVICE_IP}:{FRIDA_PORT} (No USB)",
        f"> WeChat PID: {WECHAT_PID}\n",
        "## Summary\n",
        "| Metric | Value |",
        "|--------|-------|",
        f"| Total Methods | {s['total']} |",
        f"| Passed | {s['passed']} |",
        f"| Failed | {s['failed']} |",
        f"| **Pass Rate** | **{s['pass_rate']}** |\n",
        "## Executed Methods (Safe)\n",
        "| Method | Status | Latency | Result |",
        "|--------|--------|---------|--------|",
    ]

    for r in report["results"]:
        if r.get("executed"):
            icon = "V" if r["status"] in ("PASS", "EXEC_ERR") else "X"
            lat = f"{r.get('latency_ms', 0)}ms"
            res = ""
            if "result" in r:
                res = json.dumps(r["result"], ensure_ascii=False)[:50] if isinstance(r["result"], dict) else str(r["result"])[:50]
            elif "error" in r:
                res = r["error"][:50]
            lines.append(f"| `{r['method']}` | {icon} {r['status']} | {lat} | {res} |")

    lines.append("\n## Existence Check (Unsafe)\n")
    lines.append("| Method | Exists |")
    lines.append("|--------|--------|")
    for r in report["results"]:
        if not r.get("executed"):
            icon = "V" if r["status"] == "EXIST" else "X"
            lines.append(f"| `{r['method']}` | {icon} |")

    with open(REPORT_FILE, "w", encoding="utf-8") as f:
        f.write("\n".join(lines))
    print(f"  Report: {REPORT_FILE}")


if __name__ == "__main__":
    main()
