"""
微信自动注册引擎 v1.0
全自动流程：检测登录状态 → 获取 SIM 手机号 → 注册 → 短信验证 → 设置资料 → 发送消息

双通道支持：
- ADB UI 自动化（主通道，无需 Root / Frida）
- Frida Hook（辅助通道，需注入 Gadget）

适配：微信 8.0.56+ / Redmi Note 13 5G (1080x2400)
"""

import time
import re
import logging
import asyncio
from typing import Dict, Any, Optional, Tuple

logger = logging.getLogger(__name__)

WX_PKG = "com.tencent.mm"
WX_LAUNCHER = f"{WX_PKG}/.ui.LauncherUI"
WX_REGISTER = f"{WX_PKG}/.plugin.account.ui.MobileRegSmsCodeUI"
WX_LOGIN_PAGE = f"{WX_PKG}/.ui.account.LoginPasswordUI"


class WeChatAutoRegister:
    """微信全自动注册引擎"""

    def __init__(self, device, screen_w: int = 1080, screen_h: int = 2400):
        self.d = device
        self._w = screen_w
        self._h = screen_h
        self._detect_screen()

    def _detect_screen(self):
        try:
            size = self._sh("wm size")
            if "Physical size:" in size:
                dims = size.split("Physical size:")[-1].strip()
                if "x" in dims:
                    self._w, self._h = map(int, dims.split("x"))
        except Exception:
            pass

    # ═══════════════════ 底层方法 ═══════════════════

    def _sh(self, cmd: str, timeout: int = 10) -> str:
        return self.d._shell(cmd, timeout=timeout)

    def _tap(self, x: int, y: int):
        self._sh(f"input tap {x} {y}")
        time.sleep(0.4)

    def _swipe(self, x1, y1, x2, y2, ms=400):
        self._sh(f"input swipe {x1} {y1} {x2} {y2} {ms}")
        time.sleep(0.5)

    def _back(self):
        self._sh("input keyevent KEYCODE_BACK")
        time.sleep(0.3)

    def _get_xml(self) -> str:
        try:
            self._sh("uiautomator dump /sdcard/ui.xml", timeout=5)
            return self._sh("cat /sdcard/ui.xml", timeout=3)
        except Exception:
            return ""

    def _find_text(self, text: str, xml: str = None) -> Optional[Tuple[int, int]]:
        if xml is None:
            xml = self._get_xml()
        if not xml:
            return None
        for pat in [
            f'text="{re.escape(text)}"[^>]*bounds="\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]"',
            f'content-desc="{re.escape(text)}"[^>]*bounds="\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]"',
            f'text="[^"]*{re.escape(text)}[^"]*"[^>]*bounds="\\[(\\d+),(\\d+)\\]\\[(\\d+),(\\d+)\\]"',
        ]:
            m = re.search(pat, xml)
            if m:
                x1, y1, x2, y2 = map(int, m.groups())
                return ((x1 + x2) // 2, (y1 + y2) // 2)
        return None

    def _click_text(self, text: str, timeout: float = 5) -> bool:
        end = time.time() + timeout
        while time.time() < end:
            pos = self._find_text(text)
            if pos:
                self._tap(*pos)
                return True
            time.sleep(0.5)
        return False

    def _wait_for_text(self, text: str, timeout: float = 15) -> bool:
        end = time.time() + timeout
        while time.time() < end:
            if self._find_text(text):
                return True
            time.sleep(0.8)
        return False

    def _input_text(self, text: str, clear: bool = False):
        if clear:
            try:
                self._sh("input keyevent KEYCODE_CTRL_A", timeout=2)
                time.sleep(0.1)
                self._sh("input keyevent KEYCODE_DEL", timeout=2)
                time.sleep(0.1)
            except Exception:
                pass
        has_cjk = any(ord(c) > 127 for c in text)
        if has_cjk:
            escaped = text.replace("'", "'\\''").replace('"', '\\"')
            try:
                result = self._sh(
                    f"am broadcast -a ADB_INPUT_TEXT --es msg '{escaped}' 2>&1",
                    timeout=3,
                )
                if "Broadcast completed" in result:
                    return
            except Exception:
                pass
            self._sh(f"input text '{escaped}'", timeout=5)
        else:
            escaped = text.replace(" ", "%s").replace("'", "\\'")
            self._sh(f"input text '{escaped}'")
        time.sleep(0.3)

    def _get_current_activity(self) -> str:
        try:
            out = self._sh("dumpsys window | grep -E 'mCurrentFocus'")
            return out.strip()
        except Exception:
            return ""

    def _screenshot_b64(self) -> str:
        try:
            self._sh("screencap -p /sdcard/auto_reg_screen.png", timeout=5)
            raw = self._sh("base64 /sdcard/auto_reg_screen.png", timeout=10)
            return raw.strip()[:200] + "..."
        except Exception:
            return ""

    # ═══════════════════ 1. 检测登录状态 ═══════════════════

    def check_wechat_login_state(self) -> Dict[str, Any]:
        """
        检测微信当前登录状态
        返回: {logged_in: bool, state: str, activity: str}

        state 值:
        - logged_in: 已登录，在主界面
        - login_page: 在登录页面（未登录）
        - register_page: 在注册页面
        - not_installed: 微信未安装
        - unknown: 无法判断
        """
        try:
            pkg_check = self._sh(f"pm list packages | grep {WX_PKG}")
            if WX_PKG not in pkg_check:
                return {"logged_in": False, "state": "not_installed", "activity": ""}
        except Exception:
            pass

        self._sh(f"am start -n {WX_LAUNCHER}")
        time.sleep(3)

        activity = self._get_current_activity()
        xml = self._get_xml()

        if "LauncherUI" in activity:
            if self._find_text("微信", xml) and (
                self._find_text("通讯录", xml) or self._find_text("发现", xml)
            ):
                return {"logged_in": True, "state": "logged_in", "activity": activity}

        if "LoginPasswordUI" in activity or "WelcomeLoginUI" in activity:
            return {"logged_in": False, "state": "login_page", "activity": activity}

        if "MobileRegSmsCodeUI" in activity or "RegByMobile" in activity:
            return {"logged_in": False, "state": "register_page", "activity": activity}

        login_texts = ["登录", "注册", "用手机号登录", "用微信号/QQ号/邮箱登录"]
        for t in login_texts:
            if self._find_text(t, xml):
                return {"logged_in": False, "state": "login_page", "activity": activity}

        main_texts = ["通讯录", "发现", "我"]
        found = sum(1 for t in main_texts if self._find_text(t, xml))
        if found >= 2:
            return {"logged_in": True, "state": "logged_in", "activity": activity}

        return {"logged_in": False, "state": "unknown", "activity": activity}

    # ═══════════════════ 2. 获取 SIM 卡手机号 ═══════════════════

    def get_sim_phone_number(self) -> Dict[str, Any]:
        """
        从设备 SIM 卡获取手机号。
        多种途径：
        1. getprop 系统属性
        2. service call iphonesubinfo（部分设备可用）
        3. 设备设置 > 关于手机
        4. SIM 卡管理器
        """
        phone = ""

        methods_tried = []

        try:
            line1 = self._sh("service call iphonesubinfo 15 2>&1", timeout=5)
            nums = re.findall(r"'([^']+)'", line1)
            raw = "".join(nums).replace(".", "").strip()
            digits = re.sub(r'\D', '', raw)
            if len(digits) >= 11:
                phone = digits[-11:]
                methods_tried.append(("iphonesubinfo_15", phone))
        except Exception:
            pass

        if not phone:
            try:
                line1 = self._sh("service call iphonesubinfo 17 2>&1", timeout=5)
                nums = re.findall(r"'([^']+)'", line1)
                raw = "".join(nums).replace(".", "").strip()
                digits = re.sub(r'\D', '', raw)
                if len(digits) >= 11:
                    phone = digits[-11:]
                    methods_tried.append(("iphonesubinfo_17", phone))
            except Exception:
                pass

        if not phone:
            props = [
                "gsm.sim.operator.numeric",
                "persist.sys.phone_number",
                "ro.telephony.default_phone",
            ]
            for prop in props:
                try:
                    val = self._sh(f"getprop {prop}").strip()
                    digits = re.sub(r'\D', '', val)
                    if len(digits) >= 11:
                        phone = digits[-11:]
                        methods_tried.append((f"getprop_{prop}", phone))
                        break
                except Exception:
                    continue

        if not phone:
            try:
                dumpsys = self._sh("dumpsys telephony.registry 2>&1 | head -50", timeout=5)
                m = re.search(r'mLine1Number\s*=\s*\+?(\d{11,})', dumpsys)
                if m:
                    phone = m.group(1)[-11:]
                    methods_tried.append(("telephony_registry", phone))
            except Exception:
                pass

        if not phone:
            try:
                siminfo = self._sh("content query --uri content://telephony/siminfo 2>&1", timeout=5)
                m = re.search(r'number=(\d{11,})', siminfo)
                if m:
                    phone = m.group(1)[-11:]
                    methods_tried.append(("siminfo_provider", phone))
            except Exception:
                pass

        sim_state = "unknown"
        try:
            sim_state = self._sh("getprop gsm.sim.state").strip()
        except Exception:
            pass

        operator = ""
        try:
            operator = self._sh("getprop gsm.operator.alpha").strip()
        except Exception:
            pass

        return {
            "phone": phone,
            "found": bool(phone),
            "sim_state": sim_state,
            "operator": operator,
            "methods_tried": methods_tried,
        }

    # ═══════════════════ 3. 读取短信验证码 ═══════════════════

    def read_sms_verification_code(self, timeout: int = 60) -> Dict[str, Any]:
        """
        自动读取最新的短信验证码（从微信发送的短信中提取 4-6 位数字）。
        优先从 content://sms/inbox 读取，降级到通知栏解析。
        """
        start = time.time()
        logger.info(f"[AutoReg] 等待短信验证码 (最长 {timeout}s)...")

        while time.time() - start < timeout:
            code = self._read_sms_code()
            if code:
                return {"code": code, "source": "sms_inbox", "elapsed": time.time() - start}

            code = self._read_notification_code()
            if not code:
                code = self._read_ocr_code()
            if code:
                return {"code": code, "source": "notification", "elapsed": time.time() - start}

            time.sleep(3)

        return {"code": "", "source": "timeout", "elapsed": timeout}

    def _read_sms_code(self) -> str:
        try:
            sms = self._sh(
                "content query --uri content://sms/inbox "
                "--projection body --sort 'date DESC' --limit 5 2>&1",
                timeout=5,
            )
            for line in sms.split("\n"):
                if "body=" in line:
                    body = line.split("body=", 1)[1].strip()
                    if "微信" in body or "wechat" in body.lower() or "验证码" in body:
                        m = re.search(r'(\d{4,6})', body)
                        if m:
                            return m.group(1)
        except Exception:
            pass
        return ""

    def _read_notification_code(self) -> str:
        try:
            notif = self._sh(
                "dumpsys notification --noredact 2>&1 | grep -A5 '10086\\|10010\\|10001\\|1069\\|验证码' | head -20",
                timeout=5,
            )
            m = re.search(r'(\d{4,6})', notif)
            if m:
                return m.group(1)
        except Exception:
            pass
        return ""

    # ═══════════════════ 4. 自动注册流程 ═══════════════════

    def auto_register(
        self,
        phone: str = "",
        nickname: str = "卡若AI",
        password: str = "",
    ) -> Dict[str, Any]:
        """
        微信全自动注册主流程。

        参数:
            phone: 手机号（空则自动从 SIM 获取）
            nickname: 昵称
            password: 密码（空则生成随机安全密码）

        返回:
            {success, phone, nickname, wxid, steps, errors}
        """
        steps = []
        errors = []

        def step(name: str, detail: str = ""):
            logger.info(f"[AutoReg] Step: {name} {detail}")
            steps.append({"step": name, "detail": detail, "time": time.time()})

        # ── 4.1 获取手机号 ──
        if not phone:
            step("get_sim_phone")
            sim_info = self.get_sim_phone_number()
            phone = sim_info.get("phone", "")
            if not phone:
                errors.append("无法从 SIM 卡获取手机号，需手动输入")
                return {
                    "success": False, "phone": "", "nickname": nickname,
                    "wxid": "", "steps": steps, "errors": errors,
                    "sim_info": sim_info,
                }
            step("phone_found", phone)

        # ── 4.2 生成密码 ──
        if not password:
            import random
            import string
            chars = string.ascii_letters + string.digits + "!@#$%"
            password = "Wx" + "".join(random.choices(chars, k=10))
            step("password_generated")

        # ── 4.3 检查微信状态 ──
        step("check_login_state")
        state = self.check_wechat_login_state()
        step("state_detected", state.get("state", "unknown"))

        if state.get("logged_in"):
            step("already_logged_in")
            return {
                "success": True, "phone": phone, "nickname": nickname,
                "wxid": "already_logged_in", "steps": steps, "errors": errors,
                "note": "微信已登录，无需注册",
            }

        # ── 4.4 导航到注册页 ──
        step("navigate_to_register")
        nav_ok = self._navigate_to_register()
        if not nav_ok:
            errors.append("无法导航到注册页面")
            return {
                "success": False, "phone": phone, "nickname": nickname,
                "wxid": "", "steps": steps, "errors": errors,
            }
        step("on_register_page")

        # ── 4.5 填写手机号 ──
        step("input_phone", phone)
        phone_ok = self._fill_phone_number(phone)
        if not phone_ok:
            errors.append("填写手机号失败")
            return {
                "success": False, "phone": phone, "nickname": nickname,
                "wxid": "", "steps": steps, "errors": errors,
            }
        step("phone_submitted")

        # ── 4.6 等待并输入验证码 ──
        step("waiting_sms_code")
        sms_result = self.read_sms_verification_code(timeout=90)
        code = sms_result.get("code", "")
        if not code:
            errors.append("未收到短信验证码")
            return {
                "success": False, "phone": phone, "nickname": nickname,
                "wxid": "", "steps": steps, "errors": errors,
                "sms_result": sms_result,
            }
        step("sms_code_received", code)

        code_ok = self._fill_sms_code(code)
        if not code_ok:
            errors.append("输入验证码失败")
        step("sms_code_submitted")

        # ── 4.7 设置昵称和密码 ──
        time.sleep(2)
        step("set_profile")
        self._set_registration_profile(nickname, password)
        step("profile_set")

        # ── 4.8 完成注册后检查 ──
        time.sleep(5)
        step("verify_registration")
        final_state = self.check_wechat_login_state()

        if final_state.get("logged_in"):
            step("registration_success")
            return {
                "success": True, "phone": phone, "nickname": nickname,
                "password": password, "wxid": "pending_detection",
                "steps": steps, "errors": errors,
            }

        errors.append(f"注册后状态异常: {final_state.get('state', 'unknown')}")
        return {
            "success": False, "phone": phone, "nickname": nickname,
            "wxid": "", "steps": steps, "errors": errors,
            "final_state": final_state,
        }

    # ═══════════════════ 4.x 注册子步骤 ═══════════════════

    def _navigate_to_register(self) -> bool:
        """从微信首页/登录页导航到注册页"""
        self._sh(f"am start -n {WX_LAUNCHER}")
        time.sleep(3)

        xml = self._get_xml()

        if self._find_text("注册", xml):
            self._click_text("注册")
            time.sleep(2)
            return True

        if self._find_text("更多", xml):
            self._click_text("更多")
            time.sleep(1)
            if self._click_text("注册", timeout=3):
                time.sleep(2)
                return True

        try:
            self._sh(
                f"am start -n {WX_PKG}/.plugin.account.ui.MobileRegUI"
            )
            time.sleep(2)
            act = self._get_current_activity()
            if "MobileReg" in act:
                return True
        except Exception:
            pass

        for attempt in range(3):
            pos = self._find_text("登录")
            if pos:
                y_more = min(pos[1] + 200, self._h - 50)
                self._tap(self._w // 2, y_more)
                time.sleep(1)
                if self._click_text("注册", timeout=3):
                    time.sleep(2)
                    return True

            self._tap(self._w - 80, self._h - 80)
            time.sleep(1)
            if self._click_text("注册", timeout=2):
                time.sleep(2)
                return True

        return False

    def _fill_phone_number(self, phone: str) -> bool:
        """在注册页填写手机号并提交"""
        xml = self._get_xml()

        phone_field = self._find_edittext(xml)
        if phone_field:
            self._tap(*phone_field)
            time.sleep(0.3)

        self._input_text(phone, clear=True)
        time.sleep(0.5)

        if self._click_text("下一步", timeout=3):
            time.sleep(2)
            return True

        if self._click_text("同意并继续", timeout=2):
            time.sleep(1)
            if self._click_text("下一步", timeout=3):
                time.sleep(2)
                return True

        return self._click_text("确定", timeout=2) or self._click_text("发送", timeout=2)

    def _find_edittext(self, xml: str) -> Optional[Tuple[int, int]]:
        m = re.search(
            r'class="android\.widget\.EditText"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"',
            xml,
        )
        if m:
            x1, y1, x2, y2 = map(int, m.groups())
            return ((x1 + x2) // 2, (y1 + y2) // 2)
        return None

    def _fill_sms_code(self, code: str) -> bool:
        """输入短信验证码"""
        xml = self._get_xml()

        et = self._find_edittext(xml)
        if et:
            self._tap(*et)
            time.sleep(0.2)

        self._input_text(code, clear=True)
        time.sleep(1)

        for btn_text in ["下一步", "确定", "提交", "验证"]:
            if self._click_text(btn_text, timeout=2):
                time.sleep(2)
                return True

        return True

    def _set_registration_profile(self, nickname: str, password: str):
        """注册流程中设置昵称和密码"""
        xml = self._get_xml()

        if self._find_text("昵称", xml) or self._find_text("填写昵称", xml):
            et = self._find_edittext(xml)
            if et:
                self._tap(*et)
                time.sleep(0.2)
                self._input_text(nickname, clear=True)
                time.sleep(0.5)

        if self._find_text("密码", xml) or self._find_text("设置密码", xml):
            edits = re.findall(
                r'class="android\.widget\.EditText"[^>]*bounds="\[(\d+),(\d+)\]\[(\d+),(\d+)\]"',
                xml,
            )
            if len(edits) >= 2:
                x1, y1, x2, y2 = map(int, edits[1])
                self._tap((x1 + x2) // 2, (y1 + y2) // 2)
                time.sleep(0.2)
                self._input_text(password)
                time.sleep(0.5)

        for btn in ["下一步", "完成", "确定", "注册"]:
            if self._click_text(btn, timeout=2):
                time.sleep(2)
                break

        if self._find_text("同意", self._get_xml()):
            self._click_text("同意")
            time.sleep(1)

    # ═══════════════════ 5. 发送消息 ═══════════════════

    def send_message_after_register(self, to_id: str, content: str) -> Dict[str, Any]:
        """注册成功后发送测试消息"""
        state = self.check_wechat_login_state()
        if not state.get("logged_in"):
            return {"success": False, "error": "微信未登录"}

        from services.wechat_adb_engine import WeChatADBEngine
        engine = WeChatADBEngine(self.d)
        return engine.execute("send_message", {"to_id": to_id, "content": content})

    # ═══════════════════ 6. 全自动入口 ═══════════════════

    def full_auto_setup(
        self,
        nickname: str = "卡若AI",
        password: str = "",
        test_msg_to: str = "",
        test_msg_content: str = "你好，我是卡若AI工作手机 🤖",
    ) -> Dict[str, Any]:
        """
        全自动设备初始化入口（一键完成）

        1. 检测微信登录状态
        2. 未登录 → 获取 SIM 手机号 → 自动注册
        3. 已登录 → 获取账号信息
        4. 可选：发送测试消息
        """
        result = {"success": False, "steps": []}

        login_state = self.check_wechat_login_state()
        result["login_state"] = login_state

        if login_state.get("logged_in"):
            result["success"] = True
            result["note"] = "微信已登录"

            if test_msg_to:
                msg_result = self.send_message_after_register(
                    test_msg_to, test_msg_content
                )
                result["test_message"] = msg_result

            return result

        reg_result = self.auto_register(
            phone="",
            nickname=nickname,
            password=password,
        )
        result["registration"] = reg_result
        result["success"] = reg_result.get("success", False)

        if result["success"] and test_msg_to:
            time.sleep(3)
            msg_result = self.send_message_after_register(
                test_msg_to, test_msg_content
            )
            result["test_message"] = msg_result

        return result


async def run_auto_register(
    device_id: str,
    nickname: str = "卡若AI",
    password: str = "",
    test_msg_to: str = "",
    test_msg_content: str = "你好，我是卡若AI工作手机",
) -> Dict[str, Any]:
    """异步入口 — 被 API 路由调用"""
    from services.adb_device import adb_manager

    adb_device = adb_manager.get_device(device_id)
    if not adb_device:
        return {"success": False, "error": f"ADB 设备不可用: {device_id}"}

    engine = WeChatAutoRegister(adb_device)
    loop = asyncio.get_running_loop()
    result = await loop.run_in_executor(
        None,
        lambda: engine.full_auto_setup(
            nickname=nickname,
            password=password,
            test_msg_to=test_msg_to,
            test_msg_content=test_msg_content,
        ),
    )
    result["device_id"] = device_id
    return result

    def _read_ocr_code(self) -> str:
        """降级方案：通过 OCR 识别屏幕上的验证码"""
        try:
            self._sh("screencap -p /sdcard/ocr_sms.png", timeout=5)
            xml = self._get_xml()
            if "验证码" in xml or "code" in xml.lower():
                m = re.search(r"(\d{4,6})", xml)
                if m:
                    return m.group(1)
        except Exception:
            pass
        return ""
