"""
微信 UI 导航缓存系统 v1.0
—— 记录所有页面路径、元素坐标，避免重复探索，支持自动纠错

核心理念:
  1. 首次探索时录制页面结构到 JSON（持久化）
  2. 后续操作直接查缓存，跳过 uiautomator dump
  3. 缓存失效（点击后页面不符预期）自动触发重新探索
  4. 每个 action 的操作步骤作为 "路径模板" 存储
"""

import json
import time
import os
import re
import logging
from typing import Dict, Any, List, Optional, Tuple
from pathlib import Path

logger = logging.getLogger(__name__)

CACHE_DIR = Path(__file__).parent.parent.parent / "data" / "nav_cache"
LOG_DIR = Path(__file__).parent.parent.parent / "data" / "operation_logs"


class PageSignature:
    """页面特征签名——用于快速验证当前在哪个页面"""

    def __init__(self, name: str, must_have: List[str] = None,
                 must_not_have: List[str] = None, title: str = None):
        self.name = name
        self.must_have = must_have or []
        self.must_not_have = must_not_have or []
        self.title = title

    def match(self, texts: List[str]) -> bool:
        text_set = set(texts)
        for t in self.must_have:
            if t not in text_set:
                return False
        for t in self.must_not_have:
            if t in text_set:
                return False
        if self.title and self.title not in text_set:
            return False
        return True

    def to_dict(self):
        return {
            "name": self.name,
            "must_have": self.must_have,
            "must_not_have": self.must_not_have,
            "title": self.title,
        }

    @classmethod
    def from_dict(cls, d):
        return cls(d["name"], d.get("must_have", []),
                   d.get("must_not_have", []), d.get("title"))


class NavStep:
    """单步导航操作"""

    def __init__(self, action: str, target: str = None,
                 coords: List[int] = None, wait: float = 1.0,
                 verify_page: str = None, fallback: str = None):
        self.action = action  # tap_text / tap_coord / tap_tab / scroll_up / scroll_down / back / swipe
        self.target = target  # 文本目标 or tab名
        self.coords = coords  # [x, y] 精确坐标（缓存命中时用）
        self.wait = wait
        self.verify_page = verify_page  # 期望到达的页面签名名
        self.fallback = fallback  # 失败后备用动作

    def to_dict(self):
        return {k: v for k, v in {
            "action": self.action, "target": self.target,
            "coords": self.coords, "wait": self.wait,
            "verify_page": self.verify_page, "fallback": self.fallback,
        }.items() if v is not None}

    @classmethod
    def from_dict(cls, d):
        return cls(**d)


class NavPath:
    """一条完整的导航路径（从微信首页到目标页面）"""

    def __init__(self, name: str, steps: List[NavStep] = None,
                 target_page: str = None):
        self.name = name
        self.steps = steps or []
        self.target_page = target_page
        self.success_count = 0
        self.fail_count = 0
        self.last_success = None

    def to_dict(self):
        return {
            "name": self.name,
            "steps": [s.to_dict() for s in self.steps],
            "target_page": self.target_page,
            "success_count": self.success_count,
            "fail_count": self.fail_count,
            "last_success": self.last_success,
        }

    @classmethod
    def from_dict(cls, d):
        p = cls(d["name"], [NavStep.from_dict(s) for s in d.get("steps", [])],
                d.get("target_page"))
        p.success_count = d.get("success_count", 0)
        p.fail_count = d.get("fail_count", 0)
        p.last_success = d.get("last_success")
        return p


class WeChatNavCache:
    """导航缓存管理器"""

    def __init__(self, wechat_version: str = "8.0.56",
                 screen_w: int = 1080, screen_h: int = 2400):
        self.version = wechat_version
        self.screen_w = screen_w
        self.screen_h = screen_h

        CACHE_DIR.mkdir(parents=True, exist_ok=True)
        self.cache_file = CACHE_DIR / f"nav_v{wechat_version}_{screen_w}x{screen_h}.json"

        self.pages: Dict[str, Dict] = {}
        self.paths: Dict[str, NavPath] = {}
        self.signatures: Dict[str, PageSignature] = {}
        self.element_cache: Dict[str, Dict[str, Any]] = {}

        self._load()
        if not self.pages:
            self._init_builtin()

    def _load(self):
        if self.cache_file.exists():
            try:
                data = json.loads(self.cache_file.read_text(encoding="utf-8"))
                for name, page in data.get("pages", {}).items():
                    self.pages[name] = page
                for name, path in data.get("paths", {}).items():
                    self.paths[name] = NavPath.from_dict(path)
                for name, sig in data.get("signatures", {}).items():
                    self.signatures[name] = PageSignature.from_dict(sig)
                self.element_cache = data.get("element_cache", {})
                logger.info(f"导航缓存已加载: {len(self.pages)} 页面, "
                            f"{len(self.paths)} 路径, {len(self.element_cache)} 元素")
            except Exception as e:
                logger.warning(f"加载缓存失败: {e}")

    def save(self):
        data = {
            "version": self.version,
            "screen": f"{self.screen_w}x{self.screen_h}",
            "updated": time.strftime("%Y-%m-%d %H:%M:%S"),
            "pages": self.pages,
            "paths": {k: v.to_dict() for k, v in self.paths.items()},
            "signatures": {k: v.to_dict() for k, v in self.signatures.items()},
            "element_cache": self.element_cache,
        }
        self.cache_file.write_text(
            json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")

    def _init_builtin(self):
        """初始化内置的已知页面数据（基于真机录制 2026-03-14）"""

        # ─── 底部 Tab 坐标（精确录制） ───
        tab_y = 2148
        self.element_cache["tabs"] = {
            "微信": {"center": [135, tab_y], "bounds": [0, 2119, 270, 2176]},
            "通讯录": {"center": [405, tab_y], "bounds": [270, 2119, 540, 2176]},
            "发现": {"center": [675, tab_y], "bounds": [540, 2119, 810, 2176]},
            "我": {"center": [945, tab_y], "bounds": [810, 2119, 1080, 2176]},
        }

        # ─── 页面特征签名 ───
        sigs = {
            "home": PageSignature("home", must_have=[], title=None),
            "me": PageSignature("me", must_have=["服务", "收藏", "设置"]),
            "settings": PageSignature("settings", must_have=["账号与安全"],
                                      title="设置"),
            "account_security": PageSignature(
                "account_security", must_have=["微信号", "手机号", "微信密码"],
                title="账号与安全"),
            "safety_center": PageSignature(
                "safety_center", must_have=["微信安全中心"]),
            "contacts": PageSignature("contacts", must_have=["新的朋友"]),
            "discover": PageSignature("discover", must_have=["朋友圈", "视频号"]),
        }
        for name, sig in sigs.items():
            self.signatures[name] = sig

        # ─── "我"页面元素坐标（精确录制） ───
        self.pages["me"] = {
            "recorded_at": "2026-03-14 16:32",
            "elements": {
                "头像区": {"center": [608, 298]},
                "微信号": {"center": [643, 398], "text": "微信号：Lytiao1"},
                "状态": {"center": [379, 507]},
                "3个朋友": {"center": [676, 507]},
                "服务": {"center": [197, 706]},
                "收藏": {"center": [197, 883]},
                "朋友圈": {"center": [220, 1037]},
                "视频号": {"center": [220, 1191]},
                "订单与卡包": {"center": [266, 1345]},
                "表情": {"center": [197, 1499]},
                "设置": {"center": [197, 1676]},
            },
        }

        # ─── 设置页面元素坐标 ───
        self.pages["settings"] = {
            "recorded_at": "2026-03-14 16:32",
            "needs_scroll_to_top": True,
            "elements": {
                "账号与安全": {"center": [250, 300], "note": "顶部滑后第一项"},
                "青少年模式": {"center": [250, 479]},
                "关怀模式": {"center": [158, 631]},
                "新消息通知": {"center": [204, 800]},
                "聊天": {"center": [112, 958]},
                "通用": {"center": [158, 1112]},
                "隐私": {"center": [89, 1257], "section_header": True},
                "朋友权限": {"center": [112, 1423]},
                "个人信息与权限": {"center": [135, 1574]},
                "个人信息收集清单": {"center": [158, 1725]},
                "第三方信息共享清单": {"center": [181, 1876]},
                "插件": {"center": [89, 2018]},
                "关于微信": {"center": [89, 2168]},
            },
        }

        # ─── 账号与安全页面 ───
        self.pages["account_security"] = {
            "recorded_at": "2026-03-14 16:33",
            "elements": {
                "微信号": {"center": [112, 300], "value_center": [603, 300]},
                "手机号": {"center": [112, 451], "value_center": [603, 451]},
                "微信密码": {"center": [135, 625]},
                "声音锁": {"center": [112, 778]},
                "应急联系人": {"center": [158, 953]},
                "登录过的设备": {"center": [181, 1104]},
                "更多安全设置": {"center": [181, 1257]},
                "微信安全中心": {"center": [513, 1423]},
            },
        }

        # ─── 常用导航路径 ───
        self.paths["to_me"] = NavPath("to_me", [
            NavStep("tap_tab", "我", [945, tab_y], 1.5, "me"),
        ], "me")

        self.paths["to_settings"] = NavPath("to_settings", [
            NavStep("tap_tab", "我", [945, tab_y], 1.5, "me"),
            NavStep("scroll_up", None, None, 0.5),
            NavStep("tap_text", "设置", [197, 1676], 2.0, "settings"),
            NavStep("scroll_to_top", None, None, 0.5),
        ], "settings")

        self.paths["to_account_security"] = NavPath("to_account_security", [
            NavStep("tap_tab", "我", [945, tab_y], 1.5, "me"),
            NavStep("scroll_up", None, None, 0.5),
            NavStep("tap_text", "设置", [197, 1676], 2.0, "settings"),
            NavStep("scroll_to_top", None, None, 0.5),
            NavStep("tap_text", "账号与安全", [250, 300], 2.0, "account_security"),
        ], "account_security")

        self.paths["to_safety_center"] = NavPath("to_safety_center", [
            NavStep("tap_tab", "我", [945, tab_y], 1.5, "me"),
            NavStep("scroll_up", None, None, 0.5),
            NavStep("tap_text", "设置", [197, 1676], 2.0, "settings"),
            NavStep("scroll_to_top", None, None, 0.5),
            NavStep("tap_text", "账号与安全", [250, 300], 2.0, "account_security"),
            NavStep("tap_text", "微信安全中心", [513, 1423], 3.0, "safety_center"),
        ], "safety_center")

        self.paths["to_discover"] = NavPath("to_discover", [
            NavStep("tap_tab", "发现", [675, tab_y], 1.5, "discover"),
        ], "discover")

        self.paths["to_contacts"] = NavPath("to_contacts", [
            NavStep("tap_tab", "通讯录", [405, tab_y], 1.5, "contacts"),
        ], "contacts")

        self.save()
        logger.info("内置导航缓存已初始化并保存")

    def get_tab_coord(self, tab_name: str) -> Optional[List[int]]:
        tabs = self.element_cache.get("tabs", {})
        if tab_name in tabs:
            return tabs[tab_name]["center"]
        return None

    def get_element_coord(self, page: str, element: str) -> Optional[List[int]]:
        page_data = self.pages.get(page, {})
        elements = page_data.get("elements", {})
        if element in elements:
            return elements[element].get("center")
        return None

    def get_path(self, path_name: str) -> Optional[NavPath]:
        return self.paths.get(path_name)

    def record_page(self, page_name: str, elements: Dict[str, Dict]):
        """录制/更新一个页面的元素数据"""
        self.pages[page_name] = {
            "recorded_at": time.strftime("%Y-%m-%d %H:%M"),
            "elements": elements,
        }
        self.save()
        logger.info(f"页面 [{page_name}] 已录制: {len(elements)} 个元素")

    def record_path_result(self, path_name: str, success: bool):
        """记录路径执行结果"""
        path = self.paths.get(path_name)
        if path:
            if success:
                path.success_count += 1
                path.last_success = time.strftime("%Y-%m-%d %H:%M:%S")
            else:
                path.fail_count += 1
            self.save()

    def update_element_coord(self, page: str, element: str,
                             new_center: List[int]):
        """自动纠错：更新元素坐标"""
        if page not in self.pages:
            self.pages[page] = {"elements": {}}
        if "elements" not in self.pages[page]:
            self.pages[page]["elements"] = {}
        if element not in self.pages[page]["elements"]:
            self.pages[page]["elements"][element] = {}
        old = self.pages[page]["elements"][element].get("center")
        self.pages[page]["elements"][element]["center"] = new_center
        self.pages[page]["elements"][element]["updated_at"] = \
            time.strftime("%Y-%m-%d %H:%M")
        self.save()
        logger.info(f"坐标纠错: [{page}].{element} {old} -> {new_center}")


class OperationLogger:
    """操作日志记录器——每次操作自动记录步骤/耗时/结果"""

    def __init__(self, device_id: str = ""):
        LOG_DIR.mkdir(parents=True, exist_ok=True)
        self.device_id = device_id
        self.log_file = LOG_DIR / f"ops_{time.strftime('%Y%m%d')}.jsonl"
        self._current_op = None

    def start_operation(self, action: str, params: dict):
        self._current_op = {
            "action": action,
            "params": {k: v for k, v in params.items()
                       if k not in ("password", "pwd", "new_pwd", "old_pwd")},
            "device": self.device_id,
            "started_at": time.strftime("%Y-%m-%d %H:%M:%S"),
            "start_ts": time.time(),
            "steps": [],
        }

    def log_step(self, step_type: str, detail: str = "",
                 coords: List[int] = None, success: bool = True):
        if not self._current_op:
            return
        self._current_op["steps"].append({
            "type": step_type,
            "detail": detail,
            "coords": coords,
            "success": success,
            "ts": round(time.time() - self._current_op["start_ts"], 2),
        })

    def end_operation(self, success: bool, result: dict = None,
                      error: str = None):
        if not self._current_op:
            return
        self._current_op["success"] = success
        self._current_op["duration_ms"] = round(
            (time.time() - self._current_op["start_ts"]) * 1000)
        self._current_op["ended_at"] = time.strftime("%Y-%m-%d %H:%M:%S")
        if result:
            self._current_op["result_summary"] = str(result)[:200]
        if error:
            self._current_op["error"] = error

        del self._current_op["start_ts"]

        try:
            with open(self.log_file, "a", encoding="utf-8") as f:
                f.write(json.dumps(self._current_op, ensure_ascii=False) + "\n")
        except Exception as e:
            logger.warning(f"写入操作日志失败: {e}")

        op = self._current_op
        self._current_op = None
        return op

    def get_recent_ops(self, limit: int = 20) -> List[dict]:
        """读取最近的操作记录"""
        if not self.log_file.exists():
            return []
        ops = []
        try:
            with open(self.log_file, "r", encoding="utf-8") as f:
                for line in f:
                    line = line.strip()
                    if line:
                        ops.append(json.loads(line))
        except Exception:
            pass
        return ops[-limit:]

    def get_action_stats(self) -> Dict[str, Dict]:
        """统计每个 action 的成功率和平均耗时"""
        ops = self.get_recent_ops(1000)
        stats: Dict[str, Dict] = {}
        for op in ops:
            action = op.get("action", "unknown")
            if action not in stats:
                stats[action] = {"total": 0, "success": 0,
                                 "total_ms": 0, "errors": []}
            stats[action]["total"] += 1
            if op.get("success"):
                stats[action]["success"] += 1
            stats[action]["total_ms"] += op.get("duration_ms", 0)
            if op.get("error"):
                stats[action]["errors"].append(op["error"][:50])

        for action, s in stats.items():
            s["success_rate"] = (
                f"{s['success']/s['total']*100:.0f}%" if s["total"] else "N/A")
            s["avg_ms"] = (
                s["total_ms"] // s["total"] if s["total"] else 0)
        return stats


# 模块级单例，供 wechat_adb_engine 等导入
nav_cache = WeChatNavCache()
