"""用于处理AI模型输出的动作处理器。"""

import ast
import base64
import re
import time
from dataclasses import dataclass
from io import BytesIO
from typing import Any, Callable, Optional, TYPE_CHECKING

from PIL import Image, ImageDraw, ImageFont

from ubox_py_sdk.logger import get_logger
from ubox_py_sdk.models import DeviceButton, OSType

from ..config.timing import TIMING_CONFIG
from ..device_interface import IDeviceOperations

if TYPE_CHECKING:
    from ubox_py_sdk.device import Device
    from ubox_py_sdk.models import DriverType

logger = get_logger("ubox_py_sdk.phone_agent.actions")

# 类型定义
ActionDict = dict[str, Any]


@dataclass
class ActionResult:
    """动作执行的结果。"""

    success: bool
    """执行是否成功。"""
    
    should_finish: bool
    """是否应该结束任务。"""
    
    message: Optional[str] = None
    """执行结果的消息（可选）。"""
    
    screenshot_after: Optional[str] = None
    """操作后的截图（base64）。"""


def _annotate_screenshot(
    base64_str: str,
    action_type: str,
    coordinates: Optional[list] = None,
    start_coord: Optional[list] = None,
    end_coord: Optional[list] = None,
) -> str:
    """
    在截图上标注操作位置。
    
    Args:
        base64_str: 原始截图的base64字符串
        action_type: 操作类型（"tap", "swipe", "long_press", "double_tap"等）
        coordinates: 点击/长按/双击的坐标 [x, y]（相对坐标0-1.0）
        start_coord: 滑动的起始坐标 [x, y]（相对坐标0-1.0）
        end_coord: 滑动的结束坐标 [x, y]（相对坐标0-1.0）
    
    Returns:
        标注后的截图base64字符串
    """
    try:
        # 处理base64字符串，移除前缀
        if base64_str.startswith('data:image'):
            base64_str = base64_str.split(',', 1)[1]
        
        # 解码图片
        img_data = base64.b64decode(base64_str)
        img = Image.open(BytesIO(img_data))
        width, height = img.size
        
        # 创建绘图对象
        draw = ImageDraw.Draw(img)
        
        if action_type == "tap" or action_type == "double_tap" or action_type == "long_press":
            if coordinates:
                # 将相对坐标转换为绝对坐标
                x = int(coordinates[0] * width) if coordinates[0] <= 1.0 else int(coordinates[0])
                y = int(coordinates[1] * height) if coordinates[1] <= 1.0 else int(coordinates[1])
                
                # 绘制红色圆点
                radius = max(10, min(width, height) // 50)  # 根据屏幕大小调整圆点大小
                draw.ellipse(
                    [x - radius, y - radius, x + radius, y + radius],
                    fill="red",
                    outline="darkred",
                    width=2
                )
                # 绘制白色边框
                draw.ellipse(
                    [x - radius - 2, y - radius - 2, x + radius + 2, y + radius + 2],
                    outline="white",
                    width=2
                )
        
        elif action_type == "swipe":
            if start_coord and end_coord:
                # 将相对坐标转换为绝对坐标
                start_x = int(start_coord[0] * width) if start_coord[0] <= 1.0 else int(start_coord[0])
                start_y = int(start_coord[1] * height) if start_coord[1] <= 1.0 else int(start_coord[1])
                end_x = int(end_coord[0] * width) if end_coord[0] <= 1.0 else int(end_coord[0])
                end_y = int(end_coord[1] * height) if end_coord[1] <= 1.0 else int(end_coord[1])
                
                # 绘制起始点（绿色圆点）
                radius = max(10, min(width, height) // 50)
                draw.ellipse(
                    [start_x - radius, start_y - radius, start_x + radius, start_y + radius],
                    fill="green",
                    outline="darkgreen",
                    width=2
                )
                draw.ellipse(
                    [start_x - radius - 2, start_y - radius - 2, start_x + radius + 2, start_y + radius + 2],
                    outline="white",
                    width=2
                )
                
                # 绘制结束点（红色圆点）
                draw.ellipse(
                    [end_x - radius, end_y - radius, end_x + radius, end_y + radius],
                    fill="red",
                    outline="darkred",
                    width=2
                )
                draw.ellipse(
                    [end_x - radius - 2, end_y - radius - 2, end_x + radius + 2, end_y + radius + 2],
                    outline="white",
                    width=2
                )
                
                # 绘制箭头线
                arrow_width = max(3, min(width, height) // 200)
                draw.line(
                    [start_x, start_y, end_x, end_y],
                    fill="blue",
                    width=arrow_width
                )
                
                # 绘制箭头头部（简单的三角形）
                arrow_size = radius * 1.5
                dx = end_x - start_x
                dy = end_y - start_y
                length = (dx ** 2 + dy ** 2) ** 0.5
                if length > 0:
                    # 箭头方向向量
                    ux = dx / length
                    uy = dy / length
                    # 箭头头部三个点
                    arrow_x1 = end_x - arrow_size * ux + arrow_size * 0.5 * uy
                    arrow_y1 = end_y - arrow_size * uy - arrow_size * 0.5 * ux
                    arrow_x2 = end_x - arrow_size * ux - arrow_size * 0.5 * uy
                    arrow_y2 = end_y - arrow_size * uy + arrow_size * 0.5 * ux
                    draw.polygon(
                        [(end_x, end_y), (arrow_x1, arrow_y1), (arrow_x2, arrow_y2)],
                        fill="blue",
                        outline="darkblue"
                    )
        
        # 将图片转换回base64
        buffered = BytesIO()
        img.save(buffered, format="PNG")
        annotated_base64 = base64.b64encode(buffered.getvalue()).decode()
        
        return "data:image/png;base64," + annotated_base64
    
    except Exception as e:
        logger.warning(f"标注截图失败: {e}，返回原始截图")
        return base64_str if base64_str.startswith('data:image') else "data:image/png;base64," + base64_str


class ActionHandler:
    """
    处理AI模型输出的动作执行。

    Args:
        device: 实现IDeviceOperations接口的设备对象，用于执行设备操作。
        confirmation_callback: 敏感操作确认的回调函数（可选）。
            应返回True继续执行，False取消。
        takeover_callback: 接管请求的回调函数（可选），用于登录、验证码等场景。
        original_user_prompt_getter: 获取原始用户输入的函数，用于提取包名。
    """

    def __init__(
            self,
            device: IDeviceOperations,
            confirmation_callback: Optional[Callable[[str], bool]] = None,
            takeover_callback: Optional[Callable[[str], None]] = None,
            original_user_prompt_getter: Optional[Callable[[], Optional[str]]] = None,
            model_client: Optional[Any] = None,
    ):
        self.device = device
        self.confirmation_callback = confirmation_callback or self._default_confirmation
        self.takeover_callback = takeover_callback or self._default_takeover
        self._original_user_prompt_getter = original_user_prompt_getter
        self.model_client = model_client  # 用于提取操作描述的模型客户端（可选）
    
    def _get_screenshot_base64(self) -> Optional[str]:
        """获取当前屏幕截图的base64字符串。"""
        try:
            return self.device.screenshot_base64()
        except Exception as e:
            logger.warning(f"获取截图失败: {e}")
            return None
    
    def prepare_action_screenshot(
        self, action: ActionDict, screen_width: int, screen_height: int, thinking: Optional[str] = None
    ) -> tuple[Optional[str], Optional[str], Optional[dict]]:
        """
        准备操作前的截图并标注（不执行动作）。
        
        Args:
            action: 动作字典
            screen_width: 屏幕宽度
            screen_height: 屏幕高度
            thinking: 模型的思考内容，用于提取操作描述（可选）
        
        Returns:
            (screenshot_before, action_description, extract_token_info) 元组
            extract_token_info: 操作描述提取的token消耗信息，格式: {"prompt_tokens": int, "completion_tokens": int, "total_tokens": int}
        """
        action_name = action.get("action")
        action_type = action.get("_metadata")
        
        # 对于 finish 或其他非 do 操作，不需要截图
        if action_type != "do" or not action_name:
            return None, None, None
        
        # 获取操作前截图
        screenshot_before = self._get_screenshot_base64()
        if not screenshot_before:
            return None, None, None
        
        # 根据动作类型标注截图
        # _annotate_screenshot 需要绝对坐标（像素），_convert_relative_to_absolute 返回的就是绝对坐标
        if action_name == "Tap":
            element = action.get("element")
            if element:
                x, y = self._convert_relative_to_absolute(element, screen_width, screen_height)
                screenshot_before = _annotate_screenshot(
                    screenshot_before, "tap", coordinates=[x, y]
                )
        elif action_name == "Swipe":
            start = action.get("start")
            end = action.get("end")
            if start and end:
                start_x, start_y = self._convert_relative_to_absolute(start, screen_width, screen_height)
                end_x, end_y = self._convert_relative_to_absolute(end, screen_width, screen_height)
                screenshot_before = _annotate_screenshot(
                    screenshot_before, "swipe",
                    start_coord=[start_x, start_y],
                    end_coord=[end_x, end_y]
                )
        elif action_name == "Double Tap":
            element = action.get("element")
            if element:
                x, y = self._convert_relative_to_absolute(element, screen_width, screen_height)
                screenshot_before = _annotate_screenshot(
                    screenshot_before, "double_tap", coordinates=[x, y]
                )
        elif action_name == "Long Press":
            element = action.get("element")
            if element:
                x, y = self._convert_relative_to_absolute(element, screen_width, screen_height)
                screenshot_before = _annotate_screenshot(
                    screenshot_before, "long_press", coordinates=[x, y]
                )
        # 对于 Launch, Type, Back, Home 等操作，不需要标注，直接使用原截图
        
        action_description, extract_token_info = self._generate_action_description(action, action_name, thinking)
        return screenshot_before, action_description, extract_token_info
    
    def _generate_action_description(self, action: ActionDict, action_name: str, thinking: Optional[str] = None) -> tuple[str, Optional[dict]]:
        """
        生成操作描述。优先使用模型提供的desc字段，如果没有则从thinking中提取，最后才使用默认模板。
        
        Returns:
            (操作描述, token消耗信息) 元组
            token消耗信息格式: {"prompt_tokens": int, "completion_tokens": int, "total_tokens": int}
        """
        # 优先使用模型提供的desc字段
        if "desc" in action and action["desc"]:
            return action["desc"], None
        
        # 如果没有desc字段，尝试从thinking中提取操作描述
        if thinking:
            extracted_desc, extract_token_info = self._extract_description_from_thinking(thinking, action_name)
            if extracted_desc:
                return extracted_desc, extract_token_info
        
        # 如果从thinking中提取失败，则使用默认模板生成描述
        if action_name == "Launch":
            app = action.get("app", "未知应用")
            return f"启动应用: {app}", None
        elif action_name == "Tap":
            element = action.get("element", [])
            return f"点击坐标: ({element[0] if len(element) > 0 else '?'}, {element[1] if len(element) > 1 else '?'})", None
        elif action_name == "Swipe":
            start = action.get("start", [])
            end = action.get("end", [])
            return f"滑动: ({start[0] if len(start) > 0 else '?'}, {start[1] if len(start) > 1 else '?'}) -> ({end[0] if len(end) > 0 else '?'}, {end[1] if len(end) > 1 else '?'})", None
        elif action_name == "Type":
            text = action.get("text", "")
            text_preview = text[:20] + "..." if len(text) > 20 else text
            return f"输入文本: {text_preview}", None
        elif action_name == "Back":
            return "返回上一页", None
        elif action_name == "Home":
            return "返回桌面", None
        elif action_name == "Double Tap":
            element = action.get("element", [])
            return f"双击坐标: ({element[0] if len(element) > 0 else '?'}, {element[1] if len(element) > 1 else '?'})", None
        elif action_name == "Long Press":
            element = action.get("element", [])
            return f"长按坐标: ({element[0] if len(element) > 0 else '?'}, {element[1] if len(element) > 1 else '?'})", None
        else:
            return f"执行操作: {action_name}", None
    
    def _extract_description_from_thinking(self, thinking: str, action_name: str) -> tuple[Optional[str], Optional[dict]]:
        """
        从模型的思考内容中提取操作描述。
        使用小模型分析提取，如果失败则返回None，使用默认模板。
        
        Args:
            thinking: 模型的思考内容
            action_name: 操作类型名称
        
        Returns:
            (提取的操作描述, token消耗信息) 元组。如果提取失败，返回 (None, token消耗信息)
            token消耗信息格式: {"prompt_tokens": int, "completion_tokens": int, "total_tokens": int}
        """
        if not thinking or not thinking.strip():
            return None, None
        
        # 使用模型提取（如果可用）
        if self.model_client:
            try:
                desc, token_info = self._extract_with_model(thinking, action_name)
                if desc:
                    return desc, token_info
                # 即使提取失败，也返回token信息
                return None, token_info
            except Exception as e:
                logger.debug(f"使用模型提取操作描述失败: {e}")
        
        # 如果模型提取失败，返回None
        return None, None
    
    def _extract_with_model(self, thinking: str, action_name: str) -> tuple[Optional[str], Optional[dict]]:
        """
        从思考内容中提取操作描述。
        使用固定的提示词，不记录上下文，直接提取。
        
        Args:
            thinking: 模型的思考内容
            action_name: 操作类型名称
        
        Returns:
            (提取的操作描述, token消耗信息) 元组。如果提取失败，返回 (None, token消耗信息)
            token消耗信息格式: {"prompt_tokens": int, "completion_tokens": int, "total_tokens": int}
        """
        if not self.model_client:
            return None, None
        
        try:
            # 固定的提示词模板，更明确地要求只输出一行
            prompt = f"""从以下思考内容中提取一个简短的操作描述（10-30字），说明要执行什么操作。

思考内容：{thinking}

要求：
1. 只输出一条操作描述，不要换行
2. 不要重复输出
3. 只输出操作描述本身，不要输出"操作描述："等前缀
4. 如果无法提取，只输出"无法提取"
"""
            
            # 直接使用模型客户端进行简单调用（非流式，不记录上下文）
            response = self.model_client.client.chat.completions.create(
                messages=[
                    {"role": "user", "content": prompt}
                ],
                model=self.model_client.config.model_name,
                max_tokens=50,  # 只需要简短描述
                temperature=0.0,  # 确定性输出
                stream=False,
            )
            
            # 提取token消耗信息
            token_info = None
            if hasattr(response, 'usage') and response.usage:
                token_info = {
                    "prompt_tokens": getattr(response.usage, 'prompt_tokens', None),
                    "completion_tokens": getattr(response.usage, 'completion_tokens', None),
                    "total_tokens": getattr(response.usage, 'total_tokens', None),
                }
            
            if response.choices and len(response.choices) > 0:
                desc = response.choices[0].message.content.strip()
                
                # 清理输出：移除前缀、换行符等
                desc = desc.replace("操作描述：", "").replace("操作描述:", "").strip()
                
                # 处理多行内容：只取第一行，或合并重复行
                lines = [line.strip() for line in desc.split('\n') if line.strip()]
                if lines:
                    # 如果有多行，去重后取第一行
                    unique_lines = []
                    seen = set()
                    for line in lines:
                        # 去除句号等标点后比较，避免重复
                        line_normalized = line.rstrip('。.')
                        if line_normalized and line_normalized not in seen:
                            unique_lines.append(line)
                            seen.add(line_normalized)
                    
                    if unique_lines:
                        desc = unique_lines[0]  # 只取第一行
                    else:
                        desc = lines[0]  # 如果去重后为空，使用原始第一行
                
                # 去除末尾的句号（如果只有一个）
                desc = desc.rstrip('。').rstrip('.')
                
                # 检查是否是有效描述
                if desc and desc != "无法提取" and 3 <= len(desc) <= 50:
                    return desc, token_info
            
            return None, token_info
            
        except Exception as e:
            logger.debug(f"模型提取操作描述时出错: {e}")
        
        return None, None

    def execute(
            self, action: ActionDict, screen_width: int, screen_height: int
    ) -> ActionResult:
        """
        执行AI模型输出的动作。

        Args:
            action: 来自模型的动作字典。
            screen_width: 当前屏幕宽度（像素）。
            screen_height: 当前屏幕高度（像素）。

        Returns:
            表示成功与否以及是否完成的ActionResult。
        """
        action_type = action.get("_metadata")

        if action_type == "finish":
            return ActionResult(
                success=True, should_finish=True, message=action.get("message")
            )

        if action_type != "do":
            return ActionResult(
                success=False,
                should_finish=True,
                message=f"未知的动作类型: {action_type}",
            )

        action_name = action.get("action")
        handler_method = self._get_handler(action_name)

        if handler_method is None:
            return ActionResult(
                success=False,
                should_finish=False,
                message=f"未知的动作: {action_name}",
            )

        try:
            return handler_method(action, screen_width, screen_height)
        except Exception as e:
            return ActionResult(
                success=False, should_finish=False, message=f"动作执行失败: {e}"
            )

    def _get_handler(
            self, action_name: str
    ) -> Optional[Callable[[ActionDict, int, int], ActionResult]]:
        """
        获取动作的处理器方法。

        Args:
            action_name: 动作名称。

        Returns:
            动作处理器函数，如果不存在则返回 None。
        """
        handlers = {
            "Launch": self._handle_launch,
            "Tap": self._handle_tap,
            "Type": self._handle_type,
            "Type_Name": self._handle_type,
            "Swipe": self._handle_swipe,
            "Back": self._handle_back,
            "Home": self._handle_home,
            "Double Tap": self._handle_double_tap,
            "Long Press": self._handle_long_press,
            "Wait": self._handle_wait,
            "Take_over": self._handle_takeover,
            "Note": self._handle_note,
            "Call_API": self._handle_call_api,
            "Interact": self._handle_interact,
        }
        return handlers.get(action_name)

    def _convert_relative_to_absolute(
        self, element: list[int], screen_width: int, screen_height: int
    ) -> tuple[int, int]:
        """
        将相对坐标（0-1000）转换为屏幕上的绝对坐标（像素）。

        Args:
            element: 相对坐标 [x, y]，范围 0-1000。
            screen_width: 屏幕宽度（像素）。
            screen_height: 屏幕高度（像素）。

        Returns:
            转换后的绝对坐标 (x, y)，单位为像素。
        """
        x = int(element[0] / 1000 * screen_width)
        y = int(element[1] / 1000 * screen_height)
        return x, y

    def _extract_package_from_user_input(self, app_name_from_model: str) -> Optional[str]:
        """
        从原始用户输入中提取包名，避免模型识别错误。
        Args:
            app_name_from_model: 模型输出的app名称/包名
            
        Returns:
            如果找到匹配的包名则返回，否则返回None
        """
        if not self._original_user_prompt_getter:
            return None

        original_prompt = self._original_user_prompt_getter()
        if not original_prompt:
            return None

        # 检查模型输出是否是包名格式（xxx.xxx.xxx）
        if not re.match(r'^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)+$', app_name_from_model):
            return None

        # 从原始用户输入中查找包名模式
        # 匹配 "包名：xxx" 或 "Bundle ID：xxx" 或直接包含包名格式的文本
        patterns = [
            (r'包名[：:]\s*([a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)+)', 1),  # 第一个捕获组
            (r'Bundle\s+ID[：:]\s*([a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)+)', 1),  # 第一个捕获组
            (r'\b([a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*){2,})\b', 1),  # 至少两个点分隔的包名格式
        ]

        for pattern, group_idx in patterns:
            matches = re.finditer(pattern, original_prompt, re.IGNORECASE)
            for match in matches:
                package = match.group(group_idx)
                # 如果模型输出的包名和原始输入中的包名相似（允许少量字符差异），使用原始输入中的
                if self._is_similar_package(package, app_name_from_model):
                    logger.info(f"从原始用户输入中提取包名: {package} (模型输出: {app_name_from_model})")
                    return package

        return None

    @staticmethod
    def _is_similar_package(package1: str, package2: str) -> bool:
        """
        判断两个包名是否相似（允许少量字符差异）。
        
        Args:
            package1: 包名1
            package2: 包名2
            
        Returns:
            如果相似返回True，否则返回False
        """
        # 如果完全相同，直接返回True
        if package1 == package2:
            return True

        # 如果长度差异太大，不相似
        if abs(len(package1) - len(package2)) > 3:
            return False

        # 检查点分隔符的位置是否一致
        dots1 = [i for i, c in enumerate(package1) if c == '.']
        dots2 = [i for i, c in enumerate(package2) if c == '.']

        if len(dots1) != len(dots2):
            return False

        # 如果点分隔符位置基本一致，认为是相似的
        if len(dots1) > 0:
            # 检查前几个点是否在相似位置
            for i in range(min(3, len(dots1))):
                if abs(dots1[i] - dots2[i]) > 2:
                    return False

        return True

    def _handle_launch(
            self, action: ActionDict, width: int, height: int
    ) -> ActionResult:
        """处理应用启动动作。"""
        app_name = action.get("app")
        if not app_name:
            return ActionResult(False, False, "未指定应用名称")

        try:
            from ..config.apps import get_package_name
            from ..config.apps_ios import get_bundle_id
            from ..config.apps_harmonyos import get_package_name as get_harmonyos_package_name

            # 优先从原始用户输入中提取包名（避免模型识别错误）
            package_from_input = self._extract_package_from_user_input(app_name)
            if package_from_input:
                package_name = package_from_input
            else:
                # 根据设备类型去不同的包名列表查找
                # 获取设备类型（如果设备对象有 os_type 属性）
                os_type = None
                if hasattr(self.device, 'os_type'):
                    os_type = self.device.os_type
                
                # 根据设备类型选择对应的包名查找函数
                if os_type == OSType.ANDROID:
                    # Android 设备：只查找 Android 包名列表
                    package_name = get_package_name(app_name) or app_name
                elif os_type == OSType.IOS:
                    # iOS 设备：只查找 iOS Bundle ID 列表
                    package_name = get_bundle_id(app_name) or app_name
                elif os_type == OSType.HM:
                    # 鸿蒙设备：只查找鸿蒙包名列表
                    package_name = get_harmonyos_package_name(app_name) or app_name
                else:
                    # 无法确定设备类型时，直接使用模型输出的名称
                    package_name = app_name

            success = self.device.start_app(package_name)
            if success:
                time.sleep(TIMING_CONFIG.device.default_launch_delay)
                screenshot_after = self._get_screenshot_base64()
                return ActionResult(
                    True,
                    False,
                    screenshot_after=screenshot_after,
                )
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                False,
                False,
                f"应用启动失败: {app_name}",
                screenshot_after=screenshot_after,
            )
        except Exception as e:
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                False,
                False,
                f"应用启动失败: {e}",
                screenshot_after=screenshot_after,
            )

    def _handle_tap(
            self, action: ActionDict, width: int, height: int
    ) -> ActionResult:
        """处理点击动作。"""
        element = action.get("element")
        if not element:
            return ActionResult(False, False, "未提供元素坐标")

        x, y = self._convert_relative_to_absolute(element, width, height)

        if "message" in action:
            if not self.confirmation_callback(action["message"]):
                return ActionResult(
                    success=False,
                    should_finish=True,
                    message="用户取消了敏感操作",
                )

        try:
            res = self.device.click_pos([x, y])
            time.sleep(TIMING_CONFIG.device.default_tap_delay)
            
            # 获取操作后截图
            screenshot_after = self._get_screenshot_base64()
            
            return ActionResult(
                res,
                False,
                screenshot_after=screenshot_after,
            )
        except Exception as e:
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                False,
                False,
                f"点击失败: {e}",
                screenshot_after=screenshot_after,
            )

    def _handle_type(
            self, action: ActionDict, width: int, height: int
    ) -> ActionResult:
        """处理文本输入动作。"""
        text = action.get("text", "")

        try:
            res = self.device.input_text(text, timeout=30, depth=10)
            time.sleep(TIMING_CONFIG.action.text_input_delay)
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                res,
                False,
                screenshot_after=screenshot_after,
            )
        except Exception as e:
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                False,
                False,
                f"文本输入失败: {e}",
                screenshot_after=screenshot_after,
            )

    def _handle_swipe(
            self, action: ActionDict, width: int, height: int
    ) -> ActionResult:
        """处理滑动动作。"""
        start = action.get("start")
        end = action.get("end")

        if not start or not end:
            return ActionResult(False, False, "缺少滑动坐标")

        start_x, start_y = self._convert_relative_to_absolute(start, width, height)
        end_x, end_y = self._convert_relative_to_absolute(end, width, height)

        try:
            res = self.device.slide_pos([start_x, start_y], [end_x, end_y])
            time.sleep(TIMING_CONFIG.device.default_swipe_delay)
            
            # 获取操作后截图
            screenshot_after = self._get_screenshot_base64()
            
            return ActionResult(
                res,
                False,
                screenshot_after=screenshot_after,
            )
        except Exception as e:
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                False,
                False,
                f"滑动失败: {e}",
                screenshot_after=screenshot_after,
            )

    def _handle_back(
            self, action: ActionDict, width: int, height: int
    ) -> ActionResult:
        """处理返回键动作。"""
        try:
            res = self.device.press(DeviceButton.BACK)
            time.sleep(TIMING_CONFIG.device.default_back_delay)
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                res,
                False,
                screenshot_after=screenshot_after,
            )
        except Exception as e:
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                False,
                False,
                f"返回键操作失败: {e}",
                screenshot_after=screenshot_after,
            )

    def _handle_home(
            self, action: ActionDict, width: int, height: int
    ) -> ActionResult:
        """处理Home键动作。"""
        try:
            res = self.device.press(DeviceButton.HOME)
            time.sleep(TIMING_CONFIG.device.default_home_delay)
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                res,
                False,
                screenshot_after=screenshot_after,
            )
        except Exception as e:
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                False,
                False,
                f"Home键操作失败: {e}",
                screenshot_after=screenshot_after,
            )

    def _handle_double_tap(
            self, action: ActionDict, width: int, height: int
    ) -> ActionResult:
        """处理双击动作。"""
        element = action.get("element")
        if not element:
            return ActionResult(False, False, "未提供元素坐标")

        x, y = self._convert_relative_to_absolute(element, width, height)
        
        try:
            res = self.device.click_pos([x, y], times=2)
            time.sleep(TIMING_CONFIG.device.default_double_tap_delay)
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                res,
                False,
                screenshot_after=screenshot_after,
            )
        except Exception as e:
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                False,
                False,
                f"双击失败: {e}",
                screenshot_after=screenshot_after,
            )

    def _handle_long_press(
            self, action: ActionDict, width: int, height: int
    ) -> ActionResult:
        """处理长按动作。"""
        element = action.get("element")
        if not element:
            return ActionResult(False, False, "未提供元素坐标")

        x, y = self._convert_relative_to_absolute(element, width, height)
        
        try:
            res = self.device.click_pos([x, y], duration=3.0)
            time.sleep(TIMING_CONFIG.device.default_long_press_delay)
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                res,
                False,
                screenshot_after=screenshot_after,
            )
        except Exception as e:
            screenshot_after = self._get_screenshot_base64()
            return ActionResult(
                False,
                False,
                f"长按失败: {e}",
                screenshot_after=screenshot_after,
            )

    def _handle_wait(
            self, action: ActionDict, width: int, height: int
    ) -> ActionResult:
        """处理等待动作。"""
        duration_str = action.get("duration", "1 seconds")
        try:
            duration = float(duration_str.replace("seconds", "").strip())
        except ValueError:
            duration = 1.0

        time.sleep(duration)
        return ActionResult(True, False)

    def _handle_takeover(
            self, action: ActionDict, width: int, height: int
    ) -> ActionResult:
        """处理接管请求（登录、验证码等）。"""
        # message = action.get("message", "需要用户干预")
        # self.takeover_callback(message)
        # 暂时不让用户干预 默认执行饭回操作
        return ActionResult(self.device.press(DeviceButton.BACK), False)

    def _handle_note(
            self, action: ActionDict, width: int, height: int
    ) -> ActionResult:
        """处理笔记动作（占位符）。"""
        return ActionResult(True, False)

    def _handle_call_api(
            self, action: ActionDict, width: int, height: int
    ) -> ActionResult:
        """处理API调用动作（占位符）。"""
        return ActionResult(True, False)

    def _handle_interact(
            self, action: ActionDict, width: int, height: int
    ) -> ActionResult:
        """处理交互请求（需要用户选择）。"""
        return ActionResult(True, False, message="需要用户交互")

    @staticmethod
    def _default_confirmation(message: str) -> bool:
        """使用控制台输入的默认确认回调。"""
        # response = input(f"敏感操作: {message}\n确认? (Y/N): ")
        # return response.upper() == "Y"
        # 默认确认
        return True

    @staticmethod
    def _default_takeover(message: str) -> None:
        """使用控制台输入的默认接管回调。"""
        # input(f"{message}\n完成手动操作后按回车继续...")
        # 不让用户介入


def parse_action(response: str) -> ActionDict:
    """
    从模型响应中解析动作。

    Args:
        response: 来自模型的原始响应字符串。

    Returns:
        解析后的动作字典。

    Raises:
        ValueError: 如果响应无法解析。
    """
    try:
        response = response.strip()
        if response.startswith("do"):
            try:
                response = response.replace('\n', '\\n')
                response = response.replace('\r', '\\r')
                response = response.replace('\t', '\\t')

                tree = ast.parse(response, mode="eval")
                if not isinstance(tree.body, ast.Call):
                    raise ValueError("期望一个函数调用")

                call = tree.body
                action = {"_metadata": "do"}
                for keyword in call.keywords:
                    key = keyword.arg
                    value = ast.literal_eval(keyword.value)
                    action[key] = value

                return action
            except (SyntaxError, ValueError) as e:
                raise ValueError(f"解析do()动作失败: {e}")

        elif response.startswith("finish"):
            action = {
                "_metadata": "finish",
                "message": response.replace("finish(message=", "")[1:-2],
            }
        else:
            raise ValueError(f"解析动作失败: 模型没有明确描述要执行的action")
        return action
    except Exception as e:
        raise ValueError(f"解析动作失败: {e}")


def do(**kwargs: Any) -> ActionDict:
    """
    用于创建'do'动作的辅助函数。

    Args:
        **kwargs: 动作参数。

    Returns:
        动作字典。
    """
    kwargs["_metadata"] = "do"
    return kwargs


def finish(**kwargs: Any) -> ActionDict:
    """
    用于创建'finish'动作的辅助函数。

    Args:
        **kwargs: 完成消息等参数。

    Returns:
        完成动作字典。
    """
    kwargs["_metadata"] = "finish"
    return kwargs
