import uiautomator2 as u2
import os
import subprocess
import time
import xml.etree.ElementTree as ET
import numpy as np
import keyboard
import cv2
from PIL import Image
import imagehash
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import queue, threading, time
from datetime import datetime, timedelta
import hashlib
from concurrent.futures import ThreadPoolExecutor
import re


def run_command(command):
    """执行命令并返回输出"""
    result = subprocess.run(command, capture_output=True, text=True, shell=True, encoding="utf-8")
    return result.stdout.strip()


def enable_adb_keyboard(device_id):
    """启用并设置 ADB 键盘为默认"""
    # print("启用 ADB 键盘...")
    run_command(f"adb -s {device_id} shell ime enable com.android.adbkeyboard/.AdbIME")
    # print("设置 ADB 键盘为默认键盘...")
    run_command(f"adb -s {device_id} shell ime set com.android.adbkeyboard/.AdbIME")


def enable_sougou_keyboard(device_id):
    """启用并设置搜狗输入法为默认"""
    # print("启用搜狗输入法...")
    run_command(f"adb -s {device_id} shell ime enable com.sohu.inputmethod.sogou.xiaomi/.SogouIME")
    # print("设置搜狗输入法为默认键盘...")
    run_command(f"adb -s {device_id} shell ime set com.sohu.inputmethod.sogou.xiaomi/.SogouIME")


def check_ADBKeyboard(device_id):
    """
    检查是否安装并启用了 ADB 键盘，如果未安装则自动安装并启用。
    """
    def is_keyboard_active():
        """检查当前默认键盘是否为 ADB 键盘"""
        result = run_command(f"adb -s {device_id} shell settings get secure default_input_method")
        return "com.android.adbkeyboard/.AdbIME" in result

    def is_keyboard_installed():
        """检查是否已安装 ADB 键盘"""
        result = run_command(f"adb -s {device_id} shell pm list packages")
        # print(result)
        return "package:com.android.adbkeyboard" in result

    def install_adb_keyboard():
        """安装 ADB 键盘"""
        if not os.path.exists("ADBKeyBoard"):
            # print("正在下载 ADBKeyBoard...")
            run_command("git clone https://github.com/senzhk/ADBKeyBoard.git")
        apk_path = os.path.join("ADBKeyBoard", "AdbKeyboard.apk")
        print(apk_path)
        apk_path='ADBKeyBoard/ADBKeyboard.apk'
        print("当前工作目录:", os.getcwd())
        
        if os.path.exists(apk_path):
            print(apk_path)
            # print("正在安装 ADB 键盘...")
            run_command(f"adb -s {device_id} install -r {apk_path}")
        else:
            raise FileNotFoundError("未找到 AdbKeyboard.apk，请检查 Git 仓库内容。")

    # 检查当前键盘状态
    # print("检查默认键盘状态...")
    enable_sougou_keyboard(device_id)
    if is_keyboard_active():
        # print("ADB 键盘已设置为默认键盘，无需操作。")
        return

    # 检查是否安装了 ADB 键盘
    if not is_keyboard_installed():
        # print("ADB 键盘未安装，开始安装...")
        install_adb_keyboard()

    # 启用并设置为默认键盘
    # enable_adb_keyboard()
    # print("ADB 键盘已启用并设置为默认键盘。")
    # enable_sougou_keyboard()
    # print("搜狗输入法已启用并设置为默认键盘。")


def type_text(device_id, text):
    enable_adb_keyboard(device_id)
    
    """在手机上输入文字"""
    time.sleep(0.5)
    for char in text:
        if char == ' ':
            subprocess.run(f"adb -s {device_id} shell input text %s", capture_output=True, text=True, shell=True)
        elif char == '_':
            subprocess.run(f"adb -s {device_id} shell input keyevent 66", capture_output=True, text=True, shell=True)
        elif 'a' <= char <= 'z' or 'A' <= char <= 'Z' or char.isdigit():
            subprocess.run(f"adb -s {device_id} shell input text {char}", capture_output=True, text=True, shell=True)
        elif char in '-.,!?@\'°/:;()':
            subprocess.run(f"adb -s {device_id} shell input text \"{char}\"", capture_output=True, text=True, shell=True)
        else:
            subprocess.run(f"adb -s {device_id} shell am broadcast -a ADB_INPUT_TEXT --es msg \"{char}\"", capture_output=True, text=True, shell=True)
    enable_sougou_keyboard(device_id)
    time.sleep(1)


def get_screenshot(device_id=None, save_path=None):
    """截图并返回图像"""
    run_command(f"adb -s {device_id} shell screencap -p /sdcard/screenshot.jpg")
    run_command(f"adb -s {device_id} pull /sdcard/screenshot.jpg {save_path}")
    # print('save_path:', save_path)
    # d.screenshot(save_path)


def get_xml(device_id=None, save_path=None):
    """获取 XML 文件"""
    run_command(f"adb -s {device_id} shell uiautomator dump /sdcard/window_dump.xml")
    run_command(f"adb -s {device_id} pull /sdcard/window_dump.xml {save_path}")
    # print("文件拉取成功")


def press_enter(device_id):
    run_command(f"adb -s {device_id} shell input keyevent 66")
    time.sleep(1)

def open_app(device_id, package_name, stop=True):
    # run_command(f"adb -s {device_id} shell am start -n {package_name}")
    if stop:
        run_command(f'adb -s {device_id} shell am force-stop {package_name}')
    run_command(f'adb -s {device_id} shell monkey -p {package_name} -c android.intent.category.LAUNCHER 1')
    time.sleep(1)

def press_delete(device_id):
    run_command(f"adb -s {device_id} shell input keyevent 67")
    time.sleep(1)

def press_back(device_id):
    run_command(f"adb -s {device_id} shell input keyevent 4")
    time.sleep(1)

def press_home(device_id):
    run_command(f"adb -s {device_id} shell input keyevent 3")
    time.sleep(1)

def tap(device_id, x, y):
    run_command(f"adb -s {device_id} shell input tap {x} {y}")
    time.sleep(1)

def long_press(device_id, x, y, duration=600):
    run_command(f"adb -s {device_id} shell input swipe {x} {y} {x} {y} {duration}")
    time.sleep(1)

def swipe(device_id, x1, y1, x2, y2, duration=1000):
    run_command(f"adb -s {device_id} shell input swipe {x1} {y1} {x2} {y2} {duration}")
    time.sleep(1)


def get_resolution(device_id, retry_interval=30, max_retry_time=120):
    start_time = time.time()
    
    while True:
        output = run_command(f"adb -s {device_id} shell wm size")

        match = re.search(r"Override size:\s*(\d+)x(\d+)", output)
        if not match:
            match = re.search(r"Physical size:\s*(\d+)x(\d+)", output)
        
        if match:
            w = int(match.group(1))
            h = int(match.group(2))
            return w, h  # 成功则立即返回

        if time.time() - start_time > max_retry_time:
            print("超过最大重试时间，未找到物理分辨率信息")
            raise RuntimeError("Failed to get device resolution within time limit.")

        print("未找到物理分辨率信息，30 秒后重试...")
        time.sleep(retry_interval)


def home_clear(device_id):
    width, height = get_resolution(device_id)

    center_x = width // 2
    target_y = height - 200

    run_command(f"adb -s {device_id} shell input keyevent 3")  # 返回主屏幕
    time.sleep(0.4)
    run_command(f"adb -s {device_id} shell input keyevent 187")  # 显示任务管理器
    time.sleep(0.4)
    run_command(f"adb -s {device_id} shell input tap {center_x} {target_y}")  # 点击关闭所有任务
    time.sleep(1)
    run_command(f"adb -s {device_id} shell input keyevent 3")  # 返回主屏幕


def get_package_and_activity(device_id=None):
    device_flag = f"-s {device_id}" if device_id else ""
    cmd = f'adb {device_flag} shell dumpsys window | findstr mCurrentFocus'
    output = subprocess.getoutput(cmd)

    # 匹配包名和 Activity
    match = re.search(r'mCurrentFocus=Window\{.*?\s(\S+?)/(\S+?)\}', output)
    if match:
        package_name = match.group(1)
        activity_name = match.group(2)
        return package_name
    return None

def search_ui_elements_by_keywords_adb(device_id, keywords, xml_path='layout.xml'):
    """根据关键词从 adb dump 的 XML 中搜索元素，返回包含自身与父元素的组合"""
    def extract_node_info(node):
        return {
            "text": node.attrib.get("text", ""),
            "resource-id": node.attrib.get("resource-id", ""),
            "class": node.attrib.get("class", ""),
            "content-desc": node.attrib.get("content-desc", ""),
            "selected": node.attrib.get("selected", ""),
            "enabled": node.attrib.get("enabled", "")
        }

    get_xml(device_id, xml_path)
    tree = ET.parse(xml_path)
    root = tree.getroot()
    matched_pairs = []

    for parent in root.iter():
        for node in parent:
            text = node.attrib.get("text", "")
            content_desc = node.attrib.get("content-desc", "")
            if (text or content_desc) and (any(keyword == text for keyword in keywords) or any(keyword == content_desc for keyword in keywords)):
                matched_pairs.append({
                    "self": extract_node_info(node),
                    "parent": extract_node_info(parent)
                })
                break  # 每个 parent 下只匹配一个
    return matched_pairs


def verify_ui_elements_exist_adb(device_id, element_pairs, xml_path='layout.xml'):
    """逐组检查 self 与其真实父节点是否分别匹配"""
    def recurse(parent):
        for node in parent:
            all_node_pairs.append((parent, node))
            recurse(node)

    try:
        get_xml(device_id, xml_path)
        tree = ET.parse(xml_path)
        root = tree.getroot()

        # 递归获取所有 (parent, node) 对
        all_node_pairs = []
        
        recurse(root)

        # 验证每组 self/parent 是否都能成功匹配
        for pair in element_pairs:
            target_self = pair.get("self", {})
            target_parent = pair.get("parent", {})

            found = False
            for parent_node, child_node in all_node_pairs:
                node_dict = {k: child_node.attrib.get(k, "") for k in target_self}
                parent_dict = {k: parent_node.attrib.get(k, "") for k in target_parent}

                if all(target_self.get(k) == node_dict.get(k) for k in target_self) and \
                all(target_parent.get(k) == parent_dict.get(k) for k in target_parent):
                    found = True
                    break  # 当前 pair 匹配成功即可
            if not found:
                return False  # 有一组没匹配成功则返回 False
        return True
    except Exception as e:
        print(f"Error verifying UI elements: {e}")
        return False

import logging
import logging.handlers

# dddd
class FormatterNoInfo(logging.Formatter):
    def __init__(self, fmt='%(levelname)s: %(message)s'):
        logging.Formatter.__init__(self, fmt)

    def format(self, record):
        if record.levelno == logging.INFO:
            return str(record.getMessage())
        return logging.Formatter.format(self, record)


def setup_default_logging(log_path='./tmp_log.txt', default_level=logging.INFO):
    _logger = logging.getLogger()
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(FormatterNoInfo())
    logging.root.addHandler(console_handler)
    logging.root.setLevel(default_level)
    if log_path:
        file_handler = logging.handlers.RotatingFileHandler(log_path, maxBytes=(1024 ** 2 * 2), backupCount=3, encoding='utf8')
        file_formatter = logging.Formatter("%(asctime)s - %(name)s: [%(levelname)s] - %(message)s")
        file_handler.setFormatter(file_formatter)
        logging.root.addHandler(file_handler)

    return _logger
