"""
触摸加固层 — 将机器精确操作伪装为真人触摸

功能:
  1. 坐标随机偏移 (+-3~8px)
  2. 点击时长随机化 (50~150ms)
  3. 贝塞尔曲线轨迹滑动 (替代直线滑动)
  4. 按压-微移-抬起时序模拟
  5. 可配置的「手抖」程度
"""

import logging
import math
import random
import time
from typing import List, Optional, Tuple

logger = logging.getLogger(__name__)

Point = Tuple[float, float]


class TouchHardener:
    """触摸事件加固 — 让自动化操作看起来像人"""

    def __init__(self, device=None, tremor_level: float = 1.0):
        """
        Args:
            device: uiautomator2 设备对象
            tremor_level: 手抖程度倍数 (0.5=稳手, 1.0=普通, 2.0=抖得厉害)
        """
        self.d = device
        self.tremor = max(0.1, tremor_level)

    def humanized_click(self, x: int, y: int) -> Tuple[int, int]:
        """
        带随机偏移的点击。返回实际点击坐标。
        """
        offset_x = random.gauss(0, 3 * self.tremor)
        offset_y = random.gauss(0, 3 * self.tremor)
        actual_x = max(0, int(x + offset_x))
        actual_y = max(0, int(y + offset_y))

        duration_ms = random.randint(50, 150)

        if self.d:
            self.d.click(actual_x, actual_y)

        settle_ms = random.uniform(30, 80)
        time.sleep(settle_ms / 1000)

        logger.debug(f"click ({x},{y}) → ({actual_x},{actual_y}) dur={duration_ms}ms")
        return actual_x, actual_y

    def humanized_swipe(self, x1: int, y1: int, x2: int, y2: int,
                        duration: float = 0.5, steps: int = 0) -> List[Point]:
        """
        贝塞尔曲线滑动。返回轨迹点序列。
        """
        sx = x1 + random.gauss(0, 2 * self.tremor)
        sy = y1 + random.gauss(0, 2 * self.tremor)
        ex = x2 + random.gauss(0, 2 * self.tremor)
        ey = y2 + random.gauss(0, 2 * self.tremor)

        ctrl_points = self._random_bezier_controls(sx, sy, ex, ey)

        if steps <= 0:
            dist = math.hypot(ex - sx, ey - sy)
            steps = max(8, int(dist / 15))

        trajectory = self._bezier_curve(sx, sy, ex, ey, ctrl_points, steps)

        if self.d:
            self.d.swipe(int(sx), int(sy), int(ex), int(ey), duration=duration, steps=steps)

        logger.debug(f"swipe ({x1},{y1})→({x2},{y2}) pts={len(trajectory)}")
        return trajectory

    def humanized_long_press(self, x: int, y: int, duration_ms: int = 800):
        """长按 — 带起始微抖"""
        actual_x = int(x + random.gauss(0, 2 * self.tremor))
        actual_y = int(y + random.gauss(0, 2 * self.tremor))
        actual_dur = duration_ms + random.randint(-100, 150)
        actual_dur = max(300, actual_dur)

        if self.d:
            self.d.long_click(actual_x, actual_y, duration=actual_dur / 1000)

        logger.debug(f"long_press ({x},{y})→({actual_x},{actual_y}) dur={actual_dur}ms")

    def humanized_type(self, text: str, char_delay_range: Tuple[float, float] = (0.03, 0.12)):
        """
        逐字输入 — 每个字符间隔随机延迟，模拟打字节奏。
        """
        for i, char in enumerate(text):
            if self.d:
                self.d.send_keys(char)
            delay = random.uniform(*char_delay_range)
            if char in (' ', ',', '.', '。', '，'):
                delay *= random.uniform(1.5, 3.0)
            time.sleep(delay)
        logger.debug(f"typed {len(text)} chars")

    def pre_action_pause(self):
        """操作前的微停顿 — 模拟人的反应时间"""
        pause = random.uniform(0.2, 0.8) * self.tremor
        time.sleep(pause)

    def post_action_pause(self):
        """操作后的短暂停顿 — 模拟人看结果"""
        pause = random.uniform(0.3, 1.2) * self.tremor
        time.sleep(pause)

    # ---- 内部方法 ----

    @staticmethod
    def _random_bezier_controls(x1: float, y1: float,
                                 x2: float, y2: float) -> List[Point]:
        """生成 1~2 个随机控制点，使路径弯曲"""
        mx = (x1 + x2) / 2
        my = (y1 + y2) / 2
        dist = math.hypot(x2 - x1, y2 - y1)
        spread = dist * random.uniform(0.1, 0.35)

        c1 = (mx + random.gauss(0, spread), my + random.gauss(0, spread))

        if random.random() > 0.5:
            c2 = (mx + random.gauss(0, spread * 0.6), my + random.gauss(0, spread * 0.6))
            return [c1, c2]
        return [c1]

    @staticmethod
    def _bezier_curve(x1: float, y1: float, x2: float, y2: float,
                      controls: List[Point], steps: int) -> List[Point]:
        """计算贝塞尔曲线点"""
        points: List[Point] = []
        if len(controls) == 1:
            cx, cy = controls[0]
            for i in range(steps + 1):
                t = i / steps
                bx = (1 - t) ** 2 * x1 + 2 * (1 - t) * t * cx + t ** 2 * x2
                by = (1 - t) ** 2 * y1 + 2 * (1 - t) * t * cy + t ** 2 * y2
                points.append((bx, by))
        elif len(controls) >= 2:
            c1x, c1y = controls[0]
            c2x, c2y = controls[1]
            for i in range(steps + 1):
                t = i / steps
                bx = ((1 - t) ** 3 * x1 + 3 * (1 - t) ** 2 * t * c1x +
                       3 * (1 - t) * t ** 2 * c2x + t ** 3 * x2)
                by = ((1 - t) ** 3 * y1 + 3 * (1 - t) ** 2 * t * c1y +
                       3 * (1 - t) * t ** 2 * c2y + t ** 3 * y2)
                points.append((bx, by))
        else:
            for i in range(steps + 1):
                t = i / steps
                points.append((x1 + (x2 - x1) * t, y1 + (y2 - y1) * t))
        return points
