#!/usr/bin/env python3
"""
手机操作后台 Worker
监听 ~/.hermes/phone_tasks/ 目录，执行任务，结果写到 ~/.hermes/phone_results/

🔴 核心原则：所有操作必须在虚拟屏执行，绝不触碰主屏 (Display 0)
"""

import os
import json
import time
import re
import subprocess
from pathlib import Path
import uiautomator2 as u2

TASKS_DIR = Path.home() / ".hermes" / "phone_tasks"
RESULTS_DIR = Path.home() / ".hermes" / "phone_results"
SERIAL = "localhost:15556"
SCRCPY_PATH = "/usr/local/bin/scrcpy"
ADB_PATH = "/usr/bin/adb"

# 全局虚拟屏 ID，启动时检测/创建
DISPLAY_ID = None
scrcpy_process = None


def adb_shell(cmd: str) -> str:
    """执行 adb shell 命令"""
    result = subprocess.run(
        ["adb", "-s", SERIAL, "shell", cmd],
        capture_output=True, text=True, timeout=30
    )
    return result.stdout.strip()


def get_virtual_display_id() -> int | None:
    """获取当前虚拟屏 ID（非 0 的 Display）"""
    output = adb_shell("dumpsys display | grep 'Display Id'")
    for line in output.split('\n'):
        match = re.search(r'Display Id=(\d+)', line)
        if match:
            did = int(match.group(1))
            if did != 0:  # 排除主屏
                return did
    return None


def create_virtual_display() -> int | None:
    """创建隐藏虚拟屏，返回 Display ID（使用 scrcpy --new-display，主屏无任何显示）"""
    global scrcpy_process
    
    print(f"[{time.strftime('%H:%M:%S')}] 创建隐藏虚拟屏...")
    
    # 先确保 ADB 连接
    subprocess.run(["adb", "connect", SERIAL], capture_output=True, timeout=10)
    time.sleep(1)
    
    # 先关闭 overlay 虚拟屏（如果有）
    adb_shell('settings put global overlay_display_devices ""')
    time.sleep(0.5)
    
    # 用 scrcpy 创建隐藏虚拟屏（--render-driver=software 让无头服务器也能运行）
    env = os.environ.copy()
    env["ADB"] = ADB_PATH
    scrcpy_process = subprocess.Popen(
        [SCRCPY_PATH, "--serial", SERIAL, "--new-display=1080x2400", "--render-driver=software"],
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        env=env
    )
    
    # 等待虚拟屏创建
    for _ in range(5):
        time.sleep(1)
        did = get_virtual_display_id()
        if did:
            print(f"[{time.strftime('%H:%M:%S')}] ✅ 虚拟屏创建成功: Display {did}")
            return did
    
    print(f"[{time.strftime('%H:%M:%S')}] ❌ 虚拟屏创建失败")
    return None


def ensure_virtual_display() -> int:
    """确保虚拟屏存在，不存在则创建"""
    global DISPLAY_ID
    
    # 检查现有虚拟屏
    did = get_virtual_display_id()
    if did:
        DISPLAY_ID = did
        return did
    
    # 创建新的
    did = create_virtual_display()
    if did:
        DISPLAY_ID = did
        return did
    
    raise RuntimeError("无法创建虚拟屏，拒绝在主屏操作")


def shell_on_display(cmd: str) -> str:
    """
    🔴 核心函数：在虚拟屏执行 shell 命令
    自动替换 input/am 命令，强制指定虚拟屏
    """
    ensure_virtual_display()
    
    # 替换 input 命令，加上 -d DISPLAY_ID
    # input tap -> input touchscreen -d X tap
    # input swipe -> input touchscreen -d X swipe
    cmd = re.sub(
        r'\binput\s+(tap|swipe|keyevent)',
        f'input touchscreen -d {DISPLAY_ID} \\1',
        cmd
    )
    
    # 替换 am start，加上 --display
    # am start -n -> am start --display X -n
    if 'am start' in cmd and '--display' not in cmd:
        cmd = cmd.replace('am start', f'am start --display {DISPLAY_ID}')
    
    return adb_shell(cmd)


def tap(x: int, y: int):
    """在虚拟屏点击"""
    ensure_virtual_display()
    adb_shell(f"input touchscreen -d {DISPLAY_ID} tap {x} {y}")


def start_app(package: str, activity: str = None, uri: str = None):
    """在虚拟屏启动 App"""
    ensure_virtual_display()
    
    if uri:
        cmd = f"am start --display {DISPLAY_ID} -a android.intent.action.VIEW -d '{uri}'"
    elif activity:
        cmd = f"am start --display {DISPLAY_ID} -n {package}/{activity}"
    else:
        cmd = f"am start --display {DISPLAY_ID} -n {package}/.MainActivity"
    
    adb_shell(cmd)


def input_text(text: str):
    """
    通过 yadb 输入文字（不需要切换输入法！）
    yadb 是 Midscene 自带的工具，已经 push 到 /data/local/tmp/yadb
    """
    # 转义单引号
    escaped = text.replace("'", "'\\''")
    adb_shell(f"app_process -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -keyboard '{escaped}'")


def play_music(song: str, artist: str = "") -> dict:
    """播放音乐 - 在虚拟屏操作（使用 uiautomator2 dump）"""
    query = f"{artist} {song}".strip() if artist else song
    
    # 在虚拟屏打开 Spotify 搜索
    start_app("com.spotify.music", uri=f"spotify:search:{query}")
    time.sleep(3)
    
    # 用 uiautomator2 dump UI（能拿到虚拟屏节点）
    d = u2.connect(SERIAL)
    xml = d.dump_hierarchy()
    
    # 找歌曲节点
    matches = re.findall(
        rf'text="([^"]*{re.escape(song)}[^"]*)"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"',
        xml, re.IGNORECASE
    )
    
    for text, x1, y1, x2, y2 in matches:
        cy = (int(y1) + int(y2)) // 2
        if cy > 200:  # 跳过顶部搜索框
            cx = (int(x1) + int(x2)) // 2
            tap(cx, cy)
            time.sleep(2)
            result = adb_shell("dumpsys media_session | grep 'description='")
            return {"success": True, "playing": result[:200]}
    
    return {"success": False, "error": "未找到歌曲"}


def send_wechat(contact: str, message: str) -> dict:
    """发微信消息 - 在虚拟屏操作（使用 yadb 输入，不切换输入法）"""
    # 在虚拟屏启动微信
    start_app("com.tencent.mm", ".ui.LauncherUI")
    time.sleep(2)
    
    # 点击搜索
    tap(920, 150)
    time.sleep(0.5)
    
    # 输入联系人
    input_text(contact)
    time.sleep(1.5)
    
    # 点击搜索结果
    tap(540, 438)
    time.sleep(1)
    
    # 点击输入框
    tap(540, 2280)
    time.sleep(0.3)
    
    # 输入消息
    input_text(message)
    time.sleep(0.3)
    
    # 点击发送
    tap(980, 2096)
    time.sleep(0.5)
    
    return {"success": True, "sent_to": contact, "message": message}


def send_qq(contact: str, message: str) -> dict:
    """发QQ消息 - 在虚拟屏操作（使用 yadb 输入，不切换输入法）"""
    # 在虚拟屏启动QQ
    start_app("com.tencent.mobileqq", ".activity.SplashActivity")
    time.sleep(2)
    
    # 点击搜索（QQ顶部搜索按钮）
    tap(920, 150)
    time.sleep(0.5)
    
    # 输入联系人
    input_text(contact)
    time.sleep(1.5)
    
    # 点击搜索结果（第一个联系人）
    tap(540, 400)
    time.sleep(1)
    
    # 点击输入框（底部）
    tap(540, 2280)
    time.sleep(0.3)
    
    # 输入消息
    input_text(message)
    time.sleep(0.3)
    
    # 点击发送按钮
    tap(980, 2096)
    time.sleep(0.5)
    
    return {"success": True, "sent_to": contact, "message": message}


def process_task(task_file: Path):
    """处理单个任务"""
    try:
        with open(task_file) as f:
            task = json.load(f)
        
        task_id = task_file.stem
        action = task.get("action")
        
        print(f"[{time.strftime('%H:%M:%S')}] 执行任务: {task_id} ({action})")
        
        if action == "play_music":
            result = play_music(task.get("song", ""), task.get("artist", ""))
        elif action == "send_wechat":
            result = send_wechat(task.get("contact", ""), task.get("message", ""))
        elif action == "send_qq":
            result = send_qq(task.get("contact", ""), task.get("message", ""))
        else:
            result = {"success": False, "error": f"未知操作: {action}"}
        
        # 写结果
        result["task_id"] = task_id
        result["display_id"] = DISPLAY_ID
        result["completed_at"] = time.strftime("%Y-%m-%d %H:%M:%S")
        
        with open(RESULTS_DIR / f"{task_id}.json", "w") as f:
            json.dump(result, f, ensure_ascii=False, indent=2)
        
        # 删除任务文件
        task_file.unlink()
        print(f"[{time.strftime('%H:%M:%S')}] ✅ 完成: {task_id}")
        
    except Exception as e:
        print(f"[{time.strftime('%H:%M:%S')}] ❌ 任务失败: {e}")
        # 写错误结果
        try:
            result = {
                "success": False,
                "error": str(e),
                "task_id": task_file.stem,
                "completed_at": time.strftime("%Y-%m-%d %H:%M:%S")
            }
            with open(RESULTS_DIR / f"{task_file.stem}.json", "w") as f:
                json.dump(result, f, ensure_ascii=False, indent=2)
            task_file.unlink()
        except:
            pass


def main():
    # 确保目录存在
    TASKS_DIR.mkdir(parents=True, exist_ok=True)
    RESULTS_DIR.mkdir(parents=True, exist_ok=True)
    
    print(f"[{time.strftime('%H:%M:%S')}] Phone Worker 启动")
    print(f"[{time.strftime('%H:%M:%S')}] 任务目录: {TASKS_DIR}")
    print(f"[{time.strftime('%H:%M:%S')}] 结果目录: {RESULTS_DIR}")
    
    # 启动时确保虚拟屏存在
    try:
        ensure_virtual_display()
        print(f"[{time.strftime('%H:%M:%S')}] 🔒 安全模式：所有操作将在 Display {DISPLAY_ID} 执行")
    except Exception as e:
        print(f"[{time.strftime('%H:%M:%S')}] ⚠️ 虚拟屏初始化失败: {e}")
        print(f"[{time.strftime('%H:%M:%S')}] 将在首个任务时重试...")
    
    # 主循环
    while True:
        task_files = sorted(TASKS_DIR.glob("*.json"))
        for task_file in task_files:
            process_task(task_file)
        time.sleep(1)


if __name__ == "__main__":
    main()
