#!/usr/bin/env python3
"""
工作手机SDK - 逐步操作验证器
每个功能：导航到对应UI页面 -> 执行Frida RPC -> 等待UI响应 -> 截图 -> 返回数据
确保每张截图与功能一一对应
"""
import frida
import json
import time
import os
import subprocess
from datetime import datetime

DEVICE_IP = "192.168.0.12"
FRIDA_PORT = 27042
WECHAT_PID = 7239
DEVICE_SERIAL = "xgfe65eimrrofyws"
WECHAT_PKG = "com.tencent.mm"

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_steps")
os.makedirs(OUTPUT_DIR, exist_ok=True)

LOG_FILE = os.path.join(OUTPUT_DIR, "verification_log.md")


class PhoneController:
    """通过ADB控制手机UI导航"""

    def __init__(self, serial):
        self.serial = serial

    def adb(self, cmd):
        full = f"adb -s {self.serial} {cmd}"
        r = subprocess.run(full, shell=True, capture_output=True, timeout=10)
        return r.stdout.decode("utf-8", errors="ignore").strip()

    def shell(self, cmd):
        return self.adb(f'shell "{cmd}"')

    def screenshot(self, name):
        """截图并保存到本地，返回文件路径"""
        ts = int(time.time())
        fname = f"{name}_{ts}.png"
        fpath = os.path.join(OUTPUT_DIR, fname)
        self.shell("screencap -p /sdcard/verify_sc.png")
        time.sleep(0.5)
        self.adb(f"pull /sdcard/verify_sc.png {fpath}")
        if os.path.exists(fpath) and os.path.getsize(fpath) > 1000:
            return fpath
        return ""

    def tap(self, x, y):
        """点击屏幕坐标"""
        self.shell(f"input tap {x} {y}")
        time.sleep(0.8)

    def swipe(self, x1, y1, x2, y2, duration=300):
        """滑动"""
        self.shell(f"input swipe {x1} {y1} {x2} {y2} {duration}")
        time.sleep(0.5)

    def back(self):
        """返回键"""
        self.shell("input keyevent 4")
        time.sleep(0.5)

    def home(self):
        """Home键"""
        self.shell("input keyevent 3")
        time.sleep(0.5)

    def open_wechat(self):
        """打开微信主页"""
        self.shell(f"am start -n {WECHAT_PKG}/.ui.LauncherUI")
        time.sleep(2)

    def open_wechat_contacts(self):
        """打开微信通讯录Tab"""
        self.open_wechat()
        time.sleep(1)
        # 点击底部通讯录Tab (通常在底部第2个位置)
        # 小米手机分辨率通常是1080x2400
        self.tap(360, 2340)  # 通讯录tab
        time.sleep(1)

    def open_wechat_discover(self):
        """打开微信发现Tab"""
        self.open_wechat()
        time.sleep(1)
        self.tap(720, 2340)  # 发现tab
        time.sleep(1)

    def open_wechat_me(self):
        """打开微信我Tab"""
        self.open_wechat()
        time.sleep(1)
        self.tap(980, 2340)  # 我tab
        time.sleep(1)

    def open_chat(self, name="filehelper"):
        """打开指定聊天窗口"""
        self.open_wechat()
        time.sleep(1)
        # 点击搜索
        self.tap(950, 140)
        time.sleep(1)
        # 输入搜索内容
        self.shell(f"input text {name}")
        time.sleep(1)
        # 点击第一个结果
        self.tap(540, 400)
        time.sleep(1)

    def get_screen_size(self):
        """获取屏幕分辨率"""
        r = self.shell("wm size")
        # Physical size: 1080x2400
        if "x" in r:
            parts = r.split(":")[-1].strip().split("x")
            return int(parts[0]), int(parts[1])
        return 1080, 2400


class StepVerifier:
    """逐步验证器"""

    def __init__(self):
        self.phone = PhoneController(DEVICE_SERIAL)
        self.exports = None
        self.script = None
        self.session = None
        self.log_lines = []
        self.results = []

    def connect(self):
        """连接Frida"""
        print("[CONNECT] Connecting to Frida...")
        device = frida.get_device_manager().add_remote_device(f"{DEVICE_IP}:{FRIDA_PORT}")
        self.session = device.attach(WECHAT_PID)

        with open(SCRIPT_PATH, "r", encoding="utf-8") as f:
            source = f.read()

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

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

        # 获取屏幕尺寸
        w, h = self.phone.get_screen_size()
        print(f"  Screen: {w}x{h}")
        return True

    def log(self, text):
        print(text)
        self.log_lines.append(text)

    def verify_step(self, module, method, description, nav_action, params=None, wait_after=1):
        """
        执行一个验证步骤:
        1. 导航到对应页面
        2. 截图(操作前)
        3. 执行RPC
        4. 等待UI响应
        5. 截图(操作后)
        6. 记录结果
        """
        self.log(f"\n{'='*60}")
        self.log(f"  [{module}] {method}")
        self.log(f"  描述: {description}")
        self.log(f"{'='*60}")

        # Step 1: 导航
        self.log(f"  [NAV] {nav_action.__doc__ or 'navigating...'}")
        nav_action()
        time.sleep(1)

        # Step 2: 操作前截图
        before_img = self.phone.screenshot(f"{module}__{method}__before")
        self.log(f"  [BEFORE] 截图: {os.path.basename(before_img) if before_img else 'FAILED'}")

        # Step 3: 执行RPC
        fn = getattr(self.exports, method, None)
        if fn is None:
            self.log(f"  [ERROR] 方法不存在: {method}")
            self.results.append({
                "module": module, "method": method, "status": "MISSING",
                "description": description, "data": None,
                "before_img": before_img, "after_img": "",
            })
            return

        start = time.time()
        try:
            if params:
                result = fn(params)
            else:
                result = fn()
            latency = int((time.time() - start) * 1000)
        except Exception as e:
            latency = int((time.time() - start) * 1000)
            result = {"success": False, "error": str(e)[:200]}

        self.log(f"  [EXEC] {method}({json.dumps(params, ensure_ascii=False)[:50] if params else ''})")
        self.log(f"  [TIME] {latency}ms")

        # 格式化结果
        if isinstance(result, str):
            self.log(f"  [DATA] {result[:100]}")
            data_preview = result
        elif isinstance(result, dict):
            data_preview = json.dumps(result, ensure_ascii=False, indent=2)[:300]
            self.log(f"  [DATA] {data_preview[:150]}")
        elif isinstance(result, list):
            data_preview = f"[{len(result)} items] " + json.dumps(result[:2], ensure_ascii=False)[:200]
            self.log(f"  [DATA] {data_preview[:150]}")
        else:
            data_preview = str(result)[:100]
            self.log(f"  [DATA] {data_preview}")

        # Step 4: 等待UI响应
        time.sleep(wait_after)

        # Step 5: 操作后截图
        after_img = self.phone.screenshot(f"{module}__{method}__after")
        self.log(f"  [AFTER] 截图: {os.path.basename(after_img) if after_img else 'FAILED'}")

        # 判断状态
        if isinstance(result, dict) and result.get("success") == False:
            status = "EXEC_ERR"
        else:
            status = "PASS"

        self.log(f"  [STATUS] {status}")

        self.results.append({
            "module": module, "method": method, "status": status,
            "description": description, "latency_ms": latency,
            "data": result if isinstance(result, (dict, list, str, bool, int, float)) else str(result),
            "before_img": before_img, "after_img": after_img,
        })

    def run_all(self):
        """运行所有验证步骤"""
        self.log("# 工作手机SDK - 逐步真机验证")
        self.log(f"> 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        self.log(f"> 设备: {DEVICE_IP}:{FRIDA_PORT}")
        self.log("")

        # ===== 模块1: 系统状态 =====
        self.log("\n## 模块1: 系统状态\n")

        self.verify_step(
            "SYS", "getProcessInfo",
            "获取微信进程信息(PID/架构/模块数)",
            lambda: self.phone.open_wechat(),
        )

        self.verify_step(
            "SYS", "getWechatVersion",
            "获取微信版本号",
            lambda: None,  # 不需要导航，直接在当前页面
        )

        self.verify_step(
            "SYS", "getHookStatus",
            "检查Hook是否已激活",
            lambda: None,
        )

        # ===== 模块2: 联系人 =====
        self.log("\n## 模块2: 联系人\n")

        self.verify_step(
            "H16_联系人", "getContacts",
            "打开通讯录 -> 获取联系人列表",
            lambda: self.phone.open_wechat_contacts(),
            params={"limit": 10},
        )

        self.verify_step(
            "H16_联系人", "getContactInfo",
            "获取文件传输助手的详细信息",
            lambda: None,  # 保持在通讯录页面
            params={"wxid": "filehelper"},
        )

        self.verify_step(
            "H16_联系人", "searchContacts",
            "搜索包含'文件'的联系人",
            lambda: None,
            params={"keyword": "文件", "limit": 5},
        )

        # ===== 模块3: 消息发送 =====
        self.log("\n## 模块3: 消息发送\n")

        msg_content = f"[SDK验证] {datetime.now().strftime('%H:%M:%S')} 测试消息"
        self.verify_step(
            "H17_消息发送", "sendMessage",
            f"打开文件传输助手对话 -> 发送文本消息: '{msg_content}'",
            lambda: self.phone.open_chat("filehelper"),
            params={"to_id": "filehelper", "content": msg_content, "msg_type": "text"},
            wait_after=2,
        )

        # ===== 模块4: 消息接收 =====
        self.log("\n## 模块4: 消息接收\n")

        self.verify_step(
            "H15_消息接收", "getRecentMessages",
            "获取最近消息列表",
            lambda: self.phone.open_wechat(),
            params={"limit": 5},
        )

        self.verify_step(
            "H15_消息接收", "getMessages",
            "获取filehelper的消息记录",
            lambda: None,
            params={"conversation_id": "filehelper", "limit": 5},
        )

        # ===== 模块5: 好友管理 =====
        self.log("\n## 模块5: 好友管理\n")

        self.verify_step(
            "H19_好友管理", "setFriendRemark",
            "修改文件传输助手的备注名为'SDK文件助手'",
            lambda: self.phone.open_wechat_contacts(),
            params={"wxid": "filehelper", "remark": "SDK文件助手"},
            wait_after=2,
        )

        self.verify_step(
            "H18_好友请求", "getFriendRequests",
            "获取好友请求列表",
            lambda: None,
            params={"limit": 5},
        )

        # ===== 模块6: 群管理 =====
        self.log("\n## 模块6: 群管理\n")

        self.verify_step(
            "H22_群管理", "getGroups",
            "获取群聊列表",
            lambda: self.phone.open_wechat(),
            params={"limit": 10},
        )

        # ===== 模块7: 个人信息 =====
        self.log("\n## 模块7: 个人信息\n")

        self.verify_step(
            "H23_账号管理", "getProfile",
            "打开'我'页面 -> 获取个人资料",
            lambda: self.phone.open_wechat_me(),
            params={},
        )

        self.verify_step(
            "H23_账号管理", "checkAccountStatus",
            "检查账号状态",
            lambda: None,
            params={},
        )

        # ===== 模块8: 标签 =====
        self.log("\n## 模块8: 标签\n")

        self.verify_step(
            "H28_标签", "getLabels",
            "获取标签列表",
            lambda: None,
            params={},
        )

        # ===== 模块9: 朋友圈 =====
        self.log("\n## 模块9: 朋友圈\n")

        self.verify_step(
            "H21_朋友圈", "getMoments",
            "打开发现页 -> 获取朋友圈动态",
            lambda: self.phone.open_wechat_discover(),
            params={"wxid": "", "limit": 3},
        )

        # ===== 模块10: 搜索 =====
        self.log("\n## 模块10: 搜索\n")

        self.verify_step(
            "H31_搜索", "globalSearch",
            "全局搜索'微信'",
            lambda: self.phone.open_wechat(),
            params={"keyword": "微信", "limit": 5},
        )

        # ===== 模块11: 设备信息 =====
        self.log("\n## 模块11: 设备信息\n")

        self.verify_step(
            "H39_设备信息", "getDeviceInfo",
            "获取设备信息",
            lambda: None,
            params={},
        )

        self.verify_step(
            "H39_设备信息", "getNetworkInfo",
            "获取网络信息",
            lambda: None,
            params={},
        )

        self.verify_step(
            "H39_设备信息", "getStorageInfo",
            "获取存储信息",
            lambda: None,
            params={},
        )

        # ===== 模块12: 账号安全 =====
        self.log("\n## 模块12: 账号安全\n")

        self.verify_step(
            "H24_账号安全", "getLoginDevices",
            "获取登录设备列表",
            lambda: self.phone.open_wechat_me(),
            params={},
        )

        self.verify_step(
            "H35_注册登录", "checkLoginState",
            "检查登录状态",
            lambda: None,
            params={},
        )

        # ===== 汇总 =====
        self.summarize()

    def summarize(self):
        """汇总结果"""
        total = len(self.results)
        passed = sum(1 for r in self.results if r["status"] in ("PASS", "EXEC_ERR"))
        missing = sum(1 for r in self.results if r["status"] == "MISSING")

        self.log(f"\n{'='*60}")
        self.log(f"  验证完成!")
        self.log(f"  总计: {total}")
        self.log(f"  通过: {passed}")
        self.log(f"  缺失: {missing}")
        self.log(f"  通过率: {passed/total*100:.1f}%")
        self.log(f"{'='*60}")

        # 保存日志
        with open(LOG_FILE, "w", encoding="utf-8") as f:
            f.write("\n".join(self.log_lines))
        print(f"\n日志: {LOG_FILE}")

        # 保存JSON
        result_file = os.path.join(OUTPUT_DIR, "step_results.json")
        with open(result_file, "w", encoding="utf-8") as f:
            json.dump({
                "time": datetime.now().isoformat(),
                "total": total,
                "passed": passed,
                "missing": missing,
                "results": self.results,
            }, f, ensure_ascii=False, indent=2, default=str)
        print(f"结果: {result_file}")

    def cleanup(self):
        try:
            self.script.unload()
            self.session.detach()
        except:
            pass


def main():
    v = StepVerifier()
    try:
        if v.connect():
            v.run_all()
    finally:
        v.cleanup()


if __name__ == "__main__":
    main()
