"""
Agent端技能基类（含防封拟人化行为层 + 深层防护集成）
"""

import time
import random
import logging
import shlex
from typing import Dict, Any, List, Optional, Tuple

logger = logging.getLogger(__name__)


class BaseSkill:
    """技能基类"""
    
    PACKAGE: str = ""
    NAME: str = ""
    SPEED_MULTIPLIER: float = 0.3
    
    def __init__(self, device, bus=None, anti_ban_ctx=None):
        self.d = device
        self.bus = bus
        self._ab = anti_ban_ctx or {}
        self._risk = self._ab.get("risk_sentinel")
        self._touch = self._ab.get("touch_hardener")
        self._sensor = self._ab.get("sensor_sim")
        self._ui_cache = None
        self._ui_cache_time = 0

    # ==========================================================
    #  防封拟人化行为层（所有 Skill 操作必须使用这些方法）
    # ==========================================================

    @staticmethod
    def human_delay(min_sec: float = 0.5, max_sec: float = 3.0):
        """拟人延迟：高斯分布，均值在 min-max 中间，避免机械化固定间隔"""
        mean = (min_sec + max_sec) / 2
        std = (max_sec - min_sec) / 6
        delay = max(min_sec, min(max_sec, random.gauss(mean, std)))
        time.sleep(delay)

    def human_type(self, text: str, clear: bool = True):
        """拟人输入：逐字输入 + 随机间隔 + 偶尔打错重输"""
        if clear:
            self.d.clear_text()
        self.human_delay(0.2, 0.5)
        for i, char in enumerate(text):
            self.d.send_keys(char)
            time.sleep(random.uniform(0.04, 0.18))
            if random.random() < 0.03 and i < len(text) - 1:
                wrong = random.choice("abcdefg1234")
                self.d.send_keys(wrong)
                time.sleep(random.uniform(0.1, 0.25))
                self.d.press("del")
                time.sleep(random.uniform(0.08, 0.15))

    def human_click(self, x: int, y: int, offset: int = 8):
        """拟人点击：加微小随机偏移（root tap 优先绕 BUG-8）"""
        dx = random.randint(-offset, offset)
        dy = random.randint(-offset, offset)
        tx, ty = x + dx, y + dy
        if self._root_input_available() and self._root_tap(tx, ty):
            self.human_delay(0.05, 0.2)
            return
        if self._touch:
            try:
                self._touch.tap(tx, ty, randomize=False)
            except Exception:
                self.d.click(tx, ty)
        else:
            self.d.click(tx, ty)
        self.human_delay(0.05, 0.2)

    def human_swipe(
        self,
        start: Tuple[int, int],
        end: Tuple[int, int],
        duration: Optional[float] = None,
    ):
        """拟人滑动：贝塞尔曲线轨迹 + 触摸加固通道 + 传感器模拟"""
        if self._risk and not self._risk.can_operate():
            return
        if duration is None:
            duration = random.uniform(0.3, 0.8)
        # 滑动前注入手持抖动
        if self._sensor:
            self._sensor.simulate_hand_shake(duration=random.uniform(0.2, 0.5))
        ctrl_x = (start[0] + end[0]) // 2 + random.randint(-30, 30)
        ctrl_y = (start[1] + end[1]) // 2 + random.randint(-30, 30)
        steps = max(8, int(duration * 40))
        points = []
        for i in range(steps + 1):
            t = i / steps
            bx = (1 - t) ** 2 * start[0] + 2 * (1 - t) * t * ctrl_x + t ** 2 * end[0]
            by = (1 - t) ** 2 * start[1] + 2 * (1 - t) * t * ctrl_y + t ** 2 * end[1]
            points.append((int(bx), int(by)))
        # root swipe 优先绕 BUG-8；否则 shell 加固通道 / u2
        if self._root_input_available() and self._root_swipe(start[0], start[1], end[0], end[1], int(duration * 1000)):
            return
        if self._touch:
            self._touch.swipe(start, end, int(duration * 1000))
        else:
            try:
                self.d.swipe_points(points, duration)
            except (AttributeError, TypeError):
                self.d.swipe(start[0], start[1], end[0], end[1], duration=duration)

    def random_browse(self, duration_sec: float = 30):
        """随机浏览：模拟真人无目的翻看，用于养号/填充自然行为"""
        try:
            info = self.d.info
            w = info.get("displayWidth", 1080)
            h = info.get("displayHeight", 2400)
        except Exception:
            w, h = 1080, 2400
        end_time = time.time() + duration_sec
        while time.time() < end_time:
            action = random.choice(["scroll", "pause", "scroll", "pause", "tap_safe"])
            if action == "scroll":
                self.human_swipe((w // 2, int(h * 0.75)), (w // 2, int(h * 0.25)))
                self.human_delay(1.5, 4.0)
            elif action == "pause":
                self.human_delay(2.0, 6.0)
            elif action == "tap_safe":
                safe_x = random.randint(int(w * 0.1), int(w * 0.9))
                safe_y = random.randint(int(h * 0.2), int(h * 0.7))
                self.human_click(safe_x, safe_y, offset=5)
                self.human_delay(1.0, 3.0)
                self.d.press("back")
                self.human_delay(0.5, 1.5)

    def natural_behavior_before(self, action: str = "send_message"):
        """自然行为链前置：快速模式下仅做最小延迟"""
        multiplier = self.SPEED_MULTIPLIER
        high_risk = {"send_message", "add_friend", "post_moments", "batch_send", "mass_send"}
        trigger_rate = min(0.9, (0.5 if action in high_risk else 0.3) * multiplier)
        if random.random() > trigger_rate:
            return
        try:
            info = self.d.info
            w = info.get("displayWidth", 1080)
            h = info.get("displayHeight", 2400)
        except Exception:
            w, h = 1080, 2400
        self.human_delay(0.8 * multiplier, 2.0 * multiplier)
        if random.random() < 0.6:
            self.human_swipe((w // 2, int(h * 0.3)), (w // 2, int(h * 0.7)))
            self.human_delay(1.0 * multiplier, 3.0 * multiplier)
        self.human_delay(0.5 * multiplier, 1.5 * multiplier)

    def natural_behavior_after(self, action: str = "send_message"):
        """自然行为链后置：快速模式下跳过"""
        multiplier = self.SPEED_MULTIPLIER
        trigger = min(0.8, 0.3 * multiplier)
        if random.random() > trigger:
            return
        self.human_delay(1.0 * multiplier, 3.0 * multiplier)
        self.random_browse(duration_sec=random.uniform(5, 15) * multiplier)
    
    def say(self, message: str, to_skill: Optional[str] = None, data: Optional[Dict[str, Any]] = None):
        """向总线发一条消息，其它 Skill 可通过 read_chat 看到"""
        if self.bus:
            self.bus.append(self.NAME, message, to_skill=to_skill, data=data or {})
    
    def read_chat(self, since_index: int = 0, from_skill: Optional[str] = None) -> List[Dict[str, Any]]:
        """读取总线上其它 Skill 的消息"""
        if not self.bus:
            return []
        return self.bus.get_messages(since_index=since_index, from_skill=from_skill)
    
    def last_from(self, skill_name: str) -> Optional[Dict[str, Any]]:
        """取指定 Skill 最后一条消息"""
        if not self.bus:
            return None
        return self.bus.get_last(from_skill=skill_name)
    
    # ========== APP控制 ==========
    
    def launch(self) -> bool:
        """启动APP（短间隔，界面确认由 wait_for_app_ready 负责）"""
        try:
            self.d.app_start(self.PACKAGE)
            time.sleep(0.5)
            return True
        except Exception as e:
            logger.error(f"启动{self.NAME}失败: {e}")
            return False
    
    def close(self):
        """关闭APP"""
        self.d.app_stop(self.PACKAGE)
    
    def is_running(self) -> bool:
        """检查是否运行"""
        try:
            return self.d.app_current()['package'] == self.PACKAGE
        except:
            return False
    
    # ========== UI操作 ==========
    # BUG-8 修复：MIUI/红米下「USB调试(安全设置)」未开时，u2 的 injectInputEvent 被
    # INJECT_EVENTS 权限拦截（连 adb shell input 也报 SecurityException）。设备已 Root，
    # 故经 `su -c 'input ...'`（root 上下文）绕过该限制（真机验证 exit=0 无异常）。
    # 元素点击：先用 u2 dump 取 bounds（只读，不需注入），再 root tap 中心。

    def _root_input_available(self) -> bool:
        """检测 root input 是否可用（缓存）。"""
        cached = getattr(self, "_root_input_ok", None)
        if cached is not None:
            return cached
        ok = False
        try:
            out = (self.d.shell("su -c id").output or "")
            ok = "uid=0" in out
        except Exception:
            ok = False
        self._root_input_ok = ok
        return ok

    def _su_input(self, args: str) -> bool:
        try:
            self.d.shell("su -c 'input %s'" % args)
            return True
        except Exception as e:
            logger.debug(f"root input 失败: {e}")
            return False

    def _root_text(self, text: str) -> bool:
        """通过 root input text 输入 ASCII 文本，绕过 ADBKeyboard 安装/启用失败。"""
        if not self._root_input_available():
            return False
        try:
            # Android input text 对空格使用 %s；复杂 Unicode 仍优先走系统/u2 输入法。
            escaped = str(text).replace("%", "\\%").replace(" ", "%s")
            cmd = "input text " + shlex.quote(escaped)
            self.d.shell("su -c %s" % shlex.quote(cmd))
            return True
        except Exception as e:
            logger.debug(f"root text 输入失败: {e}")
            return False

    def _root_tap(self, x: int, y: int) -> bool:
        return self._su_input("tap %d %d" % (int(x), int(y)))

    def _root_swipe(self, x1: int, y1: int, x2: int, y2: int, ms: int = 300) -> bool:
        return self._su_input("swipe %d %d %d %d %d" % (int(x1), int(y1), int(x2), int(y2), int(ms)))

    def _root_key(self, keycode: int) -> bool:
        return self._su_input("keyevent %d" % int(keycode))

    def _element_center(self, sel):
        """取 u2 元素中心坐标（bounds 只读，不需 INJECT_EVENTS）。"""
        try:
            if sel.exists:
                b = sel.info.get("bounds") or {}
                if b:
                    return (b["left"] + b["right"]) // 2, (b["top"] + b["bottom"]) // 2
        except Exception:
            pass
        return None

    def _click_selector(self, sel) -> bool:
        """优先 root tap 元素中心（绕 BUG-8），失败回退 u2 click。"""
        if self._root_input_available():
            c = self._element_center(sel)
            if c and self._root_tap(c[0], c[1]):
                return True
        try:
            sel.click()
            return True
        except Exception:
            return False

    def click(self, x: int, y: int):
        """点击坐标（root 优先绕 BUG-8）"""
        if self._root_input_available() and self._root_tap(x, y):
            return
        self.d.click(x, y)

    # 常用按键名 → keyevent 码（绕 BUG-8 的 root keyevent）
    _KEYMAP = {"back": 4, "home": 3, "enter": 66, "menu": 82, "del": 67, "search": 84}

    def press(self, key) -> None:
        """按键（root keyevent 优先绕 BUG-8），兼容 self.d.press 的键名/码。"""
        code = self._KEYMAP.get(key, key) if isinstance(key, str) else key
        if isinstance(code, int) and self._root_input_available() and self._root_key(code):
            return
        try:
            self.d.press(key)
        except Exception:
            pass

    def long_click(self, x: int, y: int, duration: float = 1.0) -> None:
        """长按（root：用 input swipe 原地停留 duration 模拟长按，绕 BUG-8）。"""
        if self._root_input_available() and self._root_swipe(x, y, x, y, int(duration * 1000)):
            return
        try:
            self.d.long_click(x, y, duration)
        except Exception:
            pass
    
    def click_text(self, text: str, timeout: float = 10) -> bool:
        """点击文字"""
        try:
            sel = self.d(text=text)
            if sel.wait(timeout=timeout):
                return self._click_selector(sel)
            return False
        except:
            return False
    
    def click_contains(self, text: str, timeout: float = 10) -> bool:
        """点击包含文字的元素"""
        try:
            sel = self.d(textContains=text)
            if sel.wait(timeout=timeout):
                return self._click_selector(sel)
            return False
        except:
            return False
    
    def click_id(self, resource_id: str) -> bool:
        """点击资源ID"""
        try:
            return self._click_selector(self.d(resourceId=resource_id))
        except:
            return False
    
    def click_desc(self, desc: str) -> bool:
        """点击content-desc"""
        try:
            return self._click_selector(self.d(description=desc))
        except:
            return False
    
    def input_text(self, text: str, clear: bool = True):
        """输入文字"""
        if clear:
            try:
                self.d.clear_text()
            except Exception:
                pass
        try:
            self.d.send_keys(text)
            return
        except Exception as e:
            logger.warning(f"u2 输入失败，尝试 root input text: {e}")
        if not self._root_text(text):
            raise RuntimeError("文本输入失败：u2/ADBKeyboard 不可用，root input text 兜底失败")
    
    def input_to(self, resource_id: str, text: str, clear: bool = True):
        """向指定元素输入"""
        elem = self.d(resourceId=resource_id)
        if clear:
            elem.clear_text()
        elem.send_keys(text)
    
    def swipe(self, direction: str, scale: float = 0.8):
        """滑动"""
        self.d.swipe_ext(direction, scale=scale)
    
    def swipe_up(self, scale: float = 0.5):
        """向上滑动"""
        self.d.swipe_ext("up", scale=scale)
    
    def swipe_down(self, scale: float = 0.5):
        """向下滑动"""
        self.d.swipe_ext("down", scale=scale)
    
    # ========== 查找元素 ==========
    
    def exists(self, text: str = None, resource_id: str = None, timeout: float = 3) -> bool:
        """检查元素是否存在"""
        if text:
            return self.d(text=text).exists(timeout=timeout)
        if resource_id:
            return self.d(resourceId=resource_id).exists(timeout=timeout)
        return False
    
    def wait_for(self, text: str = None, resource_id: str = None, timeout: float = 10) -> bool:
        """等待元素出现"""
        if text:
            return self.d(text=text).wait(timeout=timeout)
        if resource_id:
            return self.d(resourceId=resource_id).wait(timeout=timeout)
        return False
    
    def wait_gone(self, text: str = None, resource_id: str = None, timeout: float = 10) -> bool:
        """等待元素消失"""
        if text:
            return self.d(text=text).wait_gone(timeout=timeout)
        if resource_id:
            return self.d(resourceId=resource_id).wait_gone(timeout=timeout)
        return False
    
    # ========== 工具方法 ==========
    
    def screenshot(self) -> bytes:
        """截图"""
        return self.d.screenshot(format='raw')
    
    def get_ui_tree(self, max_age: float = 2.0) -> str:
        """获取UI树（带缓存，max_age 秒内复用上次结果）"""
        now = time.time()
        if self._ui_cache and (now - self._ui_cache_time) < max_age:
            return self._ui_cache
        self._ui_cache = self.d.dump_hierarchy()
        self._ui_cache_time = now
        return self._ui_cache
    
    def invalidate_ui_cache(self):
        """手动失效UI缓存（点击/输入后调用）"""
        self._ui_cache = None
        self._ui_cache_time = 0
    
    def sleep(self, seconds: float):
        """等待（应用速度乘数）"""
        time.sleep(seconds * self.SPEED_MULTIPLIER)
    
    def back(self):
        """返回键"""
        self.d.press("back")
    
    def home(self):
        """Home键"""
        self.d.press("home")
    
    # ========== 新增功能：搜索 ==========
    
    def search(self, keyword: str, wait_time: float = 2.0) -> Dict[str, Any]:
        """
        在APP内搜索
        
        Args:
            keyword: 搜索关键词
            wait_time: 等待应用加载时间
            
        Returns:
            搜索结果
        """
        try:
            self.sleep(wait_time)
            
            # 方法1: 查找搜索框（常见位置）
            search_found = False
            
            # 尝试点击搜索图标/文字
            search_selectors = [
                ("text", "搜索"),
                ("description", "搜索"),
                ("textContains", "搜索"),
                ("resourceId", "search"),
            ]
            
            for selector_type, selector_value in search_selectors:
                try:
                    if selector_type == "text":
                        if self.d(text=selector_value).exists(timeout=1):
                            self.d(text=selector_value).click()
                            search_found = True
                            break
                    elif selector_type == "description":
                        if self.d(description=selector_value).exists(timeout=1):
                            self.d(description=selector_value).click()
                            search_found = True
                            break
                    elif selector_type == "textContains":
                        if self.d(textContains=selector_value).exists(timeout=1):
                            self.d(textContains=selector_value).click()
                            search_found = True
                            break
                    elif selector_type == "resourceId":
                        if self.d(resourceId=selector_value).exists(timeout=1):
                            self.d(resourceId=selector_value).click()
                            search_found = True
                            break
                except:
                    continue
            
            # 方法2: 如果没找到，尝试点击屏幕上方（常见搜索框位置）
            if not search_found:
                info = self.d.info
                # 点击屏幕上方中间位置
                self.d.click(info['displayWidth'] // 2, info['displayHeight'] // 8)
                self.sleep(0.5)
            
            # 输入搜索关键词
            self.sleep(0.5)
            self.input_text(keyword, clear=True)
            self.sleep(1)
            
            # 执行搜索（回车或点击搜索按钮）
            try:
                self.d.press("enter")
            except:
                # 尝试点击搜索按钮
                if self.click_text("搜索") or self.click_text("确定"):
                    pass
            
            self.sleep(1)
            
            return {
                "success": True,
                "keyword": keyword,
                "message": f"已搜索: {keyword}"
            }
            
        except Exception as e:
            logger.error(f"搜索失败: {e}")
            return {"success": False, "error": str(e), "keyword": keyword}
    
    # ========== 新增功能：复合命令 ==========
    
    def execute_compound_command(self, commands: list) -> Dict[str, Any]:
        """
        执行复合命令
        
        Args:
            commands: 命令列表，每个命令是 {"action": "...", "params": {...}}
            
        Returns:
            执行结果
        """
        results = []
        
        for i, cmd in enumerate(commands):
            action = cmd.get("action", "")
            params = cmd.get("params", {})
            
            try:
                if action == "open_app":
                    result = self.launch()
                    results.append({"action": action, "success": result})
                    self.sleep(2)  # 等待应用启动
                    
                elif action == "search":
                    keyword = params.get("keyword", "")
                    result = self.search(keyword)
                    results.append(result)
                    
                elif action == "click":
                    x = params.get("x", 0)
                    y = params.get("y", 0)
                    self.click(x, y)
                    results.append({"action": action, "success": True})
                    self.sleep(0.5)
                    
                elif action == "swipe":
                    direction = params.get("direction", "up")
                    scale = params.get("scale", 0.5)
                    self.swipe(direction, scale)
                    results.append({"action": action, "success": True})
                    self.sleep(0.5)
                    
                elif action == "input_text":
                    text = params.get("text", "")
                    self.input_text(text)
                    results.append({"action": action, "success": True})
                    self.sleep(0.5)
                    
                elif action == "back":
                    self.back()
                    results.append({"action": action, "success": True})
                    self.sleep(0.5)
                    
                elif action == "home":
                    self.home()
                    results.append({"action": action, "success": True})
                    self.sleep(0.5)
                    
                elif action == "wait":
                    seconds = params.get("seconds", 1)
                    self.sleep(seconds)
                    results.append({"action": action, "success": True})
                    
                else:
                    results.append({
                        "action": action,
                        "success": False,
                        "error": f"未知操作: {action}"
                    })
                    
            except Exception as e:
                results.append({
                    "action": action,
                    "success": False,
                    "error": str(e)
                })
        
        success_count = sum(1 for r in results if r.get("success", False))
        
        return {
            "success": success_count == len(commands),
            "total": len(commands),
            "success_count": success_count,
            "results": results
        }
    
    # ========== 新增功能：智能等待 ==========
    
    def wait_for_app_ready(self, timeout: float = 5) -> bool:
        """等待APP完全加载（截图确认界面，间隔 0.2s 轮询）"""
        try:
            start_time = time.time()
            while time.time() - start_time < timeout:
                if self.is_running():
                    if not self.d(className="android.widget.ProgressBar").exists(timeout=0.2):
                        return True
                time.sleep(0.2)
            return False
        except:
            return True

    def wait_for_ui(self, hints: list, timeout: float = 3) -> bool:
        """等待任一 UI 文案出现（界面确认后直接往下）"""
        start = time.time()
        while time.time() - start < timeout:
            for h in hints:
                if self.exists(h, timeout=0.3):
                    return True
            time.sleep(0.15)
        return False
    
    # ========== 新增功能：截图和OCR ==========
    
    def screenshot_to_file(self, filepath: str = None) -> str:
        """
        截图并保存到文件
        
        Args:
            filepath: 保存路径，默认 /sdcard/screenshot_{timestamp}.png
            
        Returns:
            文件路径
        """
        if not filepath:
            filepath = f"/sdcard/screenshot_{int(time.time() * 1000)}.png"
        
        self.d.screenshot(filepath)
        return filepath
    
    def get_text_from_screen(self, region: Dict[str, int] = None) -> List[str]:
        """
        从屏幕提取文字（需要OCR）
        
        Args:
            region: {"x": 0, "y": 0, "width": 1080, "height": 2400}
            
        Returns:
            文字列表
        """
        # TODO: 集成OCR库（如paddleocr）
        # 暂时返回空列表
        return []
    
    # ========== 新增功能：元素查找增强 ==========
    
    # ========== 新增功能：错误处理和重试 ==========
    
    def retry_operation(self, operation, max_retries: int = 3, 
                        retry_delay: float = 1.0, **kwargs) -> Any:
        """
        重试操作
        
        Args:
            operation: 操作函数
            max_retries: 最大重试次数
            retry_delay: 重试延迟（秒）
            **kwargs: 传递给操作函数的参数
            
        Returns:
            操作结果
        """
        last_error = None
        
        for attempt in range(max_retries):
            try:
                result = operation(**kwargs)
                if result and (not isinstance(result, dict) or result.get("success", True)):
                    return result
                # 如果返回失败，继续重试
            except Exception as e:
                last_error = e
                logger.warning(f"操作失败 (尝试 {attempt + 1}/{max_retries}): {e}")
            
            if attempt < max_retries - 1:
                self.sleep(retry_delay)
        
        # 所有重试都失败
        error_msg = str(last_error) if last_error else "操作失败"
        logger.error(f"操作最终失败: {error_msg}")
        return {"success": False, "error": error_msg}
    
    def safe_click(self, selector_type: str, selector_value: str, 
                   timeout: float = 10, max_retries: int = 2) -> bool:
        """
        安全点击（带重试）
        
        Args:
            selector_type: 选择器类型（text, description, resourceId等）
            selector_value: 选择器值
            timeout: 超时时间
            max_retries: 最大重试次数
            
        Returns:
            是否成功
        """
        def _click():
            if selector_type == "text":
                return self.click_text(selector_value, timeout)
            elif selector_type == "description":
                return self.click_desc(selector_value)
            elif selector_type == "resourceId":
                return self.click_id(selector_value)
            elif selector_type == "textContains":
                return self.click_contains(selector_value, timeout)
            return False
        
        result = self.retry_operation(_click, max_retries=max_retries)
        return result if isinstance(result, bool) else result.get("success", False)
    
    def safe_input(self, text: str, clear: bool = True, max_retries: int = 2) -> bool:
        """
        安全输入（带重试）
        
        Args:
            text: 输入文本
            clear: 是否清空
            max_retries: 最大重试次数
            
        Returns:
            是否成功
        """
        def _input():
            self.input_text(text, clear=clear)
            return True
        
        result = self.retry_operation(_input, max_retries=max_retries)
        return result if isinstance(result, bool) else result.get("success", False)
    
    def find_element_by_multiple(self, **kwargs) -> Any:
        """
        通过多种方式查找元素
        
        Args:
            text: 文字
            textContains: 包含文字
            resourceId: 资源ID
            description: content-desc
            className: 类名
            
        Returns:
            元素对象或None
        """
        for key, value in kwargs.items():
            if not value:
                continue
            try:
                if key == "text":
                    if self.d(text=value).exists(timeout=1):
                        return self.d(text=value)
                elif key == "textContains":
                    if self.d(textContains=value).exists(timeout=1):
                        return self.d(textContains=value)
                elif key == "resourceId":
                    if self.d(resourceId=value).exists(timeout=1):
                        return self.d(resourceId=value)
                elif key == "description":
                    if self.d(description=value).exists(timeout=1):
                        return self.d(description=value)
                elif key == "className":
                    if self.d(className=value).exists(timeout=1):
                        return self.d(className=value)
            except:
                continue
        return None
