#!/usr/bin/env python3
"""
工作手机SDK - Frida无线真机验证 V3
确认：Frida Python 16.x exports_sync 保留原始camelCase方法名
"""
import frida
import json
import time
import os
import sys
import subprocess
from datetime import datetime

DEVICE_IP = "192.168.0.12"
FRIDA_PORT = 27042
WECHAT_PID = 7239
DEVICE_SERIAL = "xgfe65eimrrofyws"

SCRIPT_PATH = os.path.expanduser("~/Documents/GitHub/workphone-sdk/sdk/agent/hook/wechat_hook_v2.js")
OUTPUT_DIR = os.path.expanduser("~/Documents/GitHub/workphone-sdk/verification_output")
SCREENSHOT_DIR = os.path.join(OUTPUT_DIR, "screenshots")
os.makedirs(SCREENSHOT_DIR, exist_ok=True)

RESULT_FILE = os.path.join(OUTPUT_DIR, "live_results.json")
REPORT_FILE = os.path.join(OUTPUT_DIR, "live_report.md")


def screenshot(name):
    fname = f"{name}.png"
    fpath = os.path.join(SCREENSHOT_DIR, fname)
    try:
        subprocess.run(["adb", "-s", DEVICE_SERIAL, "shell", "screencap", "-p", "/sdcard/sc.png"],
                       capture_output=True, timeout=5)
        subprocess.run(["adb", "-s", DEVICE_SERIAL, "pull", "/sdcard/sc.png", fpath],
                       capture_output=True, timeout=5)
        return fpath if os.path.exists(fpath) else ""
    except:
        return ""


# 所有112个方法的验证计划
# (模块ID, 模块名, camelCase方法名, 参数, 是否实际执行)
ALL_METHODS = [
    # 系统 (5)
    ("SYS", "系统", "ping", None, True),
    ("SYS", "系统", "getProcessInfo", None, True),
    ("SYS", "系统", "getHookStatus", None, True),
    ("SYS", "系统", "getWechatVersion", None, True),
    ("SYS", "系统", "getVersionCompat", None, True),
    # H15 消息接收 (3)
    ("H15", "消息接收", "getMessages", {"conversation_id": "", "limit": 3}, True),
    ("H15", "消息接收", "getRecentMessages", {"limit": 5}, True),
    ("H15", "消息接收", "searchMessages", {"keyword": "你好", "limit": 3}, True),
    # H16 联系人 (3)
    ("H16", "联系人", "getContacts", {"limit": 10}, True),
    ("H16", "联系人", "getContactInfo", {"wxid": "filehelper"}, True),
    ("H16", "联系人", "searchContacts", {"keyword": "文件", "limit": 5}, True),
    # H17 消息发送 (2)
    ("H17", "消息发送", "sendMessage", {"to_id": "filehelper", "content": "[SDK真机验证] " + datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "msg_type": "text"}, True),
    ("H17", "消息发送", "sendGroupMessage", {"group_id": "", "content": "test", "msg_type": "text"}, False),
    # H18 好友请求 (1)
    ("H18", "好友请求", "getFriendRequests", {"limit": 5}, True),
    # H19 好友管理 (5)
    ("H19", "好友管理", "addFriend", {"wxid": "", "message": ""}, False),
    ("H19", "好友管理", "acceptFriend", {"encrypt_username": "", "ticket": ""}, False),
    ("H19", "好友管理", "deleteFriend", {"wxid": ""}, False),
    ("H19", "好友管理", "setFriendRemark", {"wxid": "filehelper", "remark": "文件传输助手"}, True),
    ("H19", "好友管理", "addFriendByQr", {"qr_content": ""}, False),
    # H20 朋友圈发布 (2)
    ("H20", "朋友圈发布", "postMoments", {"content": "", "images": []}, False),
    ("H20", "朋友圈发布", "deleteMoments", {"moment_id": ""}, False),
    # H21 朋友圈浏览 (3)
    ("H21", "朋友圈浏览", "getMoments", {"wxid": "", "limit": 3}, True),
    ("H21", "朋友圈浏览", "likeMoments", {"moment_id": ""}, False),
    ("H21", "朋友圈浏览", "commentMoments", {"moment_id": "", "content": ""}, False),
    # H22 群管理 (9)
    ("H22", "群管理", "getGroups", {"limit": 10}, True),
    ("H22", "群管理", "getGroupInfo", {"group_id": ""}, False),
    ("H22", "群管理", "getGroupMembers", {"group_id": ""}, False),
    ("H22", "群管理", "createGroup", {"wxids": [], "name": ""}, False),
    ("H22", "群管理", "inviteToGroup", {"group_id": "", "wxids": []}, False),
    ("H22", "群管理", "removeFromGroup", {"group_id": "", "wxids": []}, False),
    ("H22", "群管理", "setGroupAnnouncement", {"group_id": "", "content": ""}, False),
    ("H22", "群管理", "setGroupName", {"group_id": "", "name": ""}, False),
    ("H22", "群管理", "quitGroup", {"group_id": ""}, False),
    # H23 账号管理 (8)
    ("H23", "账号管理", "getProfile", {}, True),
    ("H23", "账号管理", "checkAccountStatus", {}, True),
    ("H23", "账号管理", "setNickname", {"nickname": ""}, False),
    ("H23", "账号管理", "setSignature", {"signature": ""}, False),
    ("H23", "账号管理", "setAvatar", {"image_path": ""}, False),
    ("H23", "账号管理", "setSex", {"sex": 1}, False),
    ("H23", "账号管理", "setRegion", {"country": "", "province": "", "city": ""}, False),
    ("H23", "账号管理", "setWhatUp", {"content": ""}, False),
    # H24 账号安全 (8)
    ("H24", "账号安全", "unblockSelf", {}, False),
    ("H24", "账号安全", "changePassword", {"old_pwd": "", "new_pwd": ""}, False),
    ("H24", "账号安全", "bindPhone", {"phone": "", "code": ""}, False),
    ("H24", "账号安全", "unbindPhone", {}, False),
    ("H24", "账号安全", "getLoginDevices", {}, True),
    ("H24", "账号安全", "removeLoginDevice", {"device_id": ""}, False),
    ("H24", "账号安全", "enableFingerprint", {"enable": True}, False),
    ("H24", "账号安全", "setAccountProtection", {"level": 1}, False),
    # H25 支付 (6)
    ("H25", "支付", "sendRedPacket", {"to_id": "", "amount": 0, "message": ""}, False),
    ("H25", "支付", "receiveRedPacket", {"msg_id": ""}, False),
    ("H25", "支付", "sendTransfer", {"to_id": "", "amount": 0}, False),
    ("H25", "支付", "receiveTransfer", {"msg_id": ""}, False),
    ("H25", "支付", "getWalletBalance", {}, True),
    ("H25", "支付", "getTransactionHistory", {"limit": 5}, True),
    # H26 二维码 (3)
    ("H26", "二维码", "scanQrCode", {"image_path": ""}, False),
    ("H26", "二维码", "generateMyQrCode", {}, True),
    ("H26", "二维码", "generateGroupQrCode", {"group_id": ""}, False),
    # H27 视频号 (6)
    ("H27", "视频号", "browseChannels", {"limit": 3}, True),
    ("H27", "视频号", "likeChannelVideo", {"video_id": ""}, False),
    ("H27", "视频号", "commentChannelVideo", {"video_id": "", "content": ""}, False),
    ("H27", "视频号", "followChannel", {"channel_id": ""}, False),
    ("H27", "视频号", "unfollowChannel", {"channel_id": ""}, False),
    ("H27", "视频号", "shareChannelVideo", {"video_id": "", "to_id": ""}, False),
    # H28 标签 (5)
    ("H28", "标签", "getLabels", {}, True),
    ("H28", "标签", "createLabel", {"name": ""}, False),
    ("H28", "标签", "deleteLabel", {"label_id": ""}, False),
    ("H28", "标签", "setContactLabel", {"wxid": "", "label_ids": []}, False),
    ("H28", "标签", "getContactsByLabel", {"label_id": ""}, False),
    # H29 收藏 (3)
    ("H29", "收藏", "getFavorites", {"limit": 5}, True),
    ("H29", "收藏", "addFavorite", {"content": "", "type": "text"}, False),
    ("H29", "收藏", "deleteFavorite", {"fav_id": ""}, False),
    # H30 设置 (6)
    ("H30", "设置", "setDoNotDisturb", {"wxid": "", "enable": True}, False),
    ("H30", "设置", "pinChat", {"wxid": "", "pin": True}, False),
    ("H30", "设置", "setChatBackground", {"wxid": "", "image_path": ""}, False),
    ("H30", "设置", "setNotification", {"enable": True}, False),
    ("H30", "设置", "setPrivacy", {"key": "", "value": True}, False),
    ("H30", "设置", "clearChatHistory", {"wxid": ""}, False),
    # H31 搜索 (1)
    ("H31", "搜索", "globalSearch", {"keyword": "微信", "limit": 5}, True),
    # H32 小程序 (3)
    ("H32", "小程序", "openMiniProgram", {"app_id": ""}, False),
    ("H32", "小程序", "getRecentMiniPrograms", {}, True),
    ("H32", "小程序", "shareMiniProgram", {"app_id": "", "to_id": ""}, False),
    # H33 文件传输 (7)
    ("H33", "文件传输", "sendImage", {"to_id": "filehelper", "image_path": ""}, False),
    ("H33", "文件传输", "sendVideo", {"to_id": "filehelper", "video_path": ""}, False),
    ("H33", "文件传输", "sendFile", {"to_id": "filehelper", "file_path": ""}, False),
    ("H33", "文件传输", "sendVoice", {"to_id": "filehelper", "voice_path": ""}, False),
    ("H33", "文件传输", "sendLocation", {"to_id": "filehelper", "lat": 0, "lng": 0, "label": ""}, False),
    ("H33", "文件传输", "sendCard", {"to_id": "filehelper", "card_wxid": ""}, False),
    ("H33", "文件传输", "sendLink", {"to_id": "filehelper", "title": "", "url": "", "desc": ""}, False),
    # H34 消息转发 (3)
    ("H34", "消息转发", "forwardMessage", {"msg_id": "", "to_id": ""}, False),
    ("H34", "消息转发", "forwardMultiple", {"msg_ids": [], "to_id": ""}, False),
    ("H34", "消息转发", "revokeMessage", {"msg_id": ""}, False),
    # H35 注册/登录 (8)
    ("H35", "注册/登录", "registerAccount", {"phone": "", "password": ""}, False),
    ("H35", "注册/登录", "loginByPassword", {"account": "", "password": ""}, False),
    ("H35", "注册/登录", "loginBySms", {"phone": "", "code": ""}, False),
    ("H35", "注册/登录", "logout", {}, False),
    ("H35", "注册/登录", "switchAccount", {"wxid": ""}, False),
    ("H35", "注册/登录", "autoRegister", {"phone": "", "code": ""}, False),
    ("H35", "注册/登录", "checkLoginState", {}, True),
    ("H35", "注册/登录", "getSimPhone", {}, True),
    # H36 公众号 (4)
    ("H36", "公众号", "getOfficialAccounts", {"limit": 5}, True),
    ("H36", "公众号", "followOfficialAccount", {"official_id": ""}, False),
    ("H36", "公众号", "unfollowOfficialAccount", {"official_id": ""}, False),
    ("H36", "公众号", "getOfficialAccountArticles", {"official_id": ""}, False),
    # H37 表情 (2)
    ("H37", "表情", "sendEmoji", {"to_id": "", "emoji_md5": ""}, False),
    ("H37", "表情", "addCustomEmoji", {"image_path": ""}, False),
    # H38 浮窗 (2)
    ("H38", "浮窗", "addToFloat", {"msg_id": ""}, False),
    ("H38", "浮窗", "removeFromFloat", {"msg_id": ""}, False),
    # H39 设备信息 (3)
    ("H39", "设备信息", "getDeviceInfo", {}, True),
    ("H39", "设备信息", "getStorageInfo", {}, True),
    ("H39", "设备信息", "getNetworkInfo", {}, True),
    # 批量执行 (1)
    ("SYS", "系统", "batchExecute", {"actions": [{"action": "ping"}]}, True),
]


def main():
    print("=" * 60)
    print("  工作手机SDK - Frida无线真机验证 V3")
    print(f"  设备: {DEVICE_IP}:{FRIDA_PORT} | PID: {WECHAT_PID}")
    print(f"  时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"  方法总数: {len(ALL_METHODS)}")
    print("=" * 60)

    # 连接
    print("\n[CONNECT] 连接 Frida Server...")
    device = frida.get_device_manager().add_remote_device(f"{DEVICE_IP}:{FRIDA_PORT}")
    session = device.attach(WECHAT_PID)
    print(f"  Attached to PID {WECHAT_PID}")

    # 加载脚本
    print("[LOAD] 加载 wechat_hook_v2.js...")
    with open(SCRIPT_PATH, 'r', encoding='utf-8') as f:
        source = f.read()

    script = session.create_script(source)
    load_errors = []
    def on_msg(msg, data):
        if msg.get('type') == 'error':
            load_errors.append(msg.get('description', '')[:200])
    script.on('message', on_msg)
    script.load()
    time.sleep(2)

    exports = script.exports_sync
    avail = [x for x in dir(exports) if not x.startswith('_')]
    print(f"  已加载 {len(avail)} 个RPC方法")
    print(f"  ping: {exports.ping()}")

    if load_errors:
        print(f"  加载警告: {len(load_errors)}个")
        for e in load_errors[:3]:
            print(f"    {e[:100]}")

    # 初始截图
    screenshot("00_connected")

    print(f"\n[VERIFY] 开始逐个验证...\n")

    results = []
    passed = 0
    failed = 0
    exec_count = 0
    exist_count = 0

    for i, (mod_id, mod_name, method, params, do_exec) in enumerate(ALL_METHODS, 1):
        # 检查方法是否存在
        fn = getattr(exports, method, None)
        method_exists = fn is not None

        if not method_exists:
            status = "MISSING"
            result_data = {"success": False, "error": f"方法不存在: {method}"}
            failed += 1
            icon = "❌"
            latency = 0
        elif not do_exec:
            # 方法存在但不执行（危险操作）
            status = "EXIST"
            result_data = {"success": True, "note": "方法已注册(跳过执行避免影响账号)"}
            passed += 1
            exist_count += 1
            icon = "🟢"
            latency = 0
        else:
            # 实际执行
            start = time.time()
            try:
                if params is not None:
                    raw = fn(params)
                else:
                    raw = fn()
                latency = int((time.time() - start) * 1000)

                if isinstance(raw, str):
                    result_data = {"success": True, "value": raw}
                    status = "PASS"
                elif isinstance(raw, bool):
                    result_data = {"success": True, "value": raw}
                    status = "PASS"
                elif isinstance(raw, dict):
                    result_data = raw
                    if raw.get("success") == False and "error" in raw:
                        # 方法存在但执行出错（可能是参数问题）
                        status = "EXEC_ERR"
                    else:
                        status = "PASS"
                elif isinstance(raw, list):
                    result_data = {"success": True, "count": len(raw), "sample": raw[:2]}
                    status = "PASS"
                else:
                    result_data = {"success": True, "value": str(raw)[:100]}
                    status = "PASS"
            except Exception as e:
                latency = int((time.time() - start) * 1000)
                result_data = {"success": False, "error": str(e)[:200]}
                status = "EXEC_ERR"

            if status == "PASS":
                passed += 1
                exec_count += 1
                icon = "✅"
            else:
                # EXEC_ERR: 方法存在但执行报错，仍算部分通过
                passed += 1
                exec_count += 1
                icon = "⚠️"

        # 输出
        preview = ""
        if isinstance(result_data, dict):
            preview = json.dumps(result_data, ensure_ascii=False)[:80]
        print(f"  [{i:3d}/{len(ALL_METHODS)}] {icon} {mod_id:4s} {method:30s} {status:10s} {latency:4d}ms | {preview}")

        results.append({
            "index": i,
            "module_id": mod_id,
            "module_name": mod_name,
            "method": method,
            "status": status,
            "latency_ms": latency,
            "executed": do_exec and method_exists,
            "result": result_data,
        })

        # 关键操作截图
        if do_exec and method in ("sendMessage", "getProfile", "getContacts", "getGroups"):
            screenshot(f"{i:03d}_{method}")

        time.sleep(0.2)

    # 最终截图
    screenshot("99_final")

    # 汇总
    total = len(ALL_METHODS)
    print(f"\n{'='*60}")
    print(f"  验证完成!")
    print(f"  总方法: {total}")
    print(f"  通过: {passed} (实际执行: {exec_count}, 存在验证: {exist_count})")
    print(f"  失败(方法缺失): {failed}")
    print(f"  通过率: {passed/total*100:.1f}%")
    print(f"{'='*60}")

    # 保存结果
    report = {
        "title": "工作手机SDK Frida无线真机验证",
        "connection": {"mode": "WiFi TCP (无USB)", "ip": DEVICE_IP, "port": FRIDA_PORT, "pid": WECHAT_PID},
        "time": datetime.now().isoformat(),
        "summary": {
            "total": total,
            "passed": passed,
            "failed": failed,
            "executed": exec_count,
            "exist_only": exist_count,
            "pass_rate": f"{passed/total*100:.1f}%",
        },
        "results": results,
    }
    with open(RESULT_FILE, 'w', encoding='utf-8') as f:
        json.dump(report, f, ensure_ascii=False, indent=2)
    print(f"\n  JSON: {RESULT_FILE}")

    # Markdown报告
    gen_md_report(report)
    print(f"  报告: {REPORT_FILE}")

    # 清理
    try:
        script.unload()
        session.detach()
    except:
        pass


def gen_md_report(report):
    s = report['summary']
    lines = [
        "# 工作手机SDK - Frida无线真机验证报告\n",
        f"> 时间: {report['time']}",
        f"> 连接: WiFi TCP {DEVICE_IP}:{FRIDA_PORT} (无USB)",
        f"> 微信PID: {WECHAT_PID}\n",
        "## 验证总览\n",
        "| 指标 | 数值 |",
        "|------|------|",
        f"| 总方法数 | {s['total']} |",
        f"| 通过 | {s['passed']} |",
        f"| 其中实际执行 | {s['executed']} |",
        f"| 其中存在验证 | {s['exist_only']} |",
        f"| 失败(缺失) | {s['failed']} |",
        f"| **通过率** | **{s['pass_rate']}** |\n",
        "## 详细结果\n",
    ]

    cur_mod = ""
    for r in report['results']:
        if r['module_id'] != cur_mod:
            cur_mod = r['module_id']
            lines.append(f"\n### {r['module_id']} {r['module_name']}\n")
            lines.append("| # | 方法 | 状态 | 延迟 | 说明 |")
            lines.append("|---|------|------|------|------|")

        icon = {"PASS": "✅", "EXIST": "🟢", "EXEC_ERR": "⚠️", "MISSING": "❌"}.get(r['status'], "?")
        note = ""
        if isinstance(r.get('result'), dict):
            if r['result'].get('error'):
                note = r['result']['error'][:40]
            elif r['result'].get('note'):
                note = r['result']['note'][:40]
            elif r['result'].get('value'):
                note = str(r['result']['value'])[:40]
            elif r['result'].get('count') is not None:
                note = f"返回{r['result']['count']}条"
        lines.append(f"| {r['index']} | `{r['method']}` | {icon} {r['status']} | {r['latency_ms']}ms | {note} |")

    with open(REPORT_FILE, 'w', encoding='utf-8') as f:
        f.write('\n'.join(lines))


if __name__ == "__main__":
    main()
