"""一次性探测:微信 ClawBot 灰度是否覆盖某个微信号。

只做两件事(不碰 zcbot 主体、不落库):
  1. GET get_bot_qrcode 拿二维码 -> 存 qr.png 并自动打开
  2. 轮询 get_qrcode_status 等扫码确认 -> 报告 status

判读:
  - 接口连不通 / 非 200      -> 本机到 ilinkai 网络不通,换网或在有网机器跑
  - 出码成功、手机扫得动确认  -> 该微信号在灰度内,ClawBot 可用
  - 出码成功、扫了报"不支持"  -> 版本不够或未灰度到该号

ASCII-only 输出(Windows GBK 控制台)。
"""
from __future__ import annotations

import base64
import os
import random
import sys
import time
import webbrowser

import httpx

BASE = "https://ilinkai.weixin.qq.com"
QR_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "clawbot_qr.png")


def _uin_header() -> str:
    # X-WECHAT-UIN: base64(String(randomUint32()))
    return base64.b64encode(str(random.randint(0, 2**32 - 1)).encode()).decode()


def _headers() -> dict:
    return {
        "Content-Type": "application/json",
        "AuthorizationType": "ilink_bot_token",
        "X-WECHAT-UIN": _uin_header(),
    }


def _save_qr(img_content: str, qrcode_id: str) -> bool:
    """实测:qrcode_img_content 是微信深链(https://liteapp.weixin.qq.com/q/...),
    需把该 URL **编码成二维码** 让微信扫,而非当图片下载。
    兜底:若哪天返回的是真图片字节(data-uri / base64 PNG)则直接存。
    """
    try:
        if not img_content:
            print(f"[hint] no img content; encode this id manually: {qrcode_id}")
            return False
        # 情况 A:真图片字节
        if img_content.startswith("data:image"):
            data = base64.b64decode(img_content.split(",", 1)[1])
            with open(QR_PATH, "wb") as f:
                f.write(data)
            print(f"[ok] QR (image) saved -> {QR_PATH}")
            return True
        # 情况 B(实测):深链 / 任意字符串 -> 自己渲染成二维码
        import segno
        print(f"[info] encoding deep-link into QR: {img_content}")
        segno.make(img_content, error="m").save(QR_PATH, scale=8, border=3)
        print(f"[ok] QR (rendered from deep-link) saved -> {QR_PATH}")
        return True
    except Exception as e:
        print(f"[warn] could not build QR: {type(e).__name__}: {e}")
        print(f"[hint] deep-link to scan manually: {img_content}")
        return False


def main() -> int:
    print("[step1] GET get_bot_qrcode ...")
    try:
        with httpx.Client(timeout=20) as c:
            r = c.get(
                f"{BASE}/ilink/bot/get_bot_qrcode",
                params={"bot_type": "3"},
                headers=_headers(),
            )
    except Exception as e:
        print(f"[FAIL] network error to {BASE}: {type(e).__name__}: {e}")
        print("[judge] host cannot reach ilinkai.weixin.qq.com -> try another network.")
        return 2

    print(f"[http] status={r.status_code}")
    body_preview = r.text[:600]
    print(f"[body] {body_preview}")
    if r.status_code != 200:
        print("[judge] non-200 from get_bot_qrcode -> endpoint/params may be wrong or blocked.")
        return 3

    try:
        data = r.json()
    except Exception:
        print("[FAIL] response not JSON; see body above.")
        return 3

    qrcode_id = data.get("qrcode") or data.get("qrcode_id") or ""
    img = data.get("qrcode_img_content") or data.get("qrcode_img") or ""
    if not qrcode_id:
        print("[FAIL] no 'qrcode' field in response; field names differ -> inspect body above.")
        return 3

    if _save_qr(img, qrcode_id):
        try:
            webbrowser.open("file://" + QR_PATH.replace("\\", "/"))
        except Exception:
            pass
        print("[action] QR opened. Scan it with your phone WeChat NOW.")
    else:
        print("[action] QR image unavailable; cannot open. See hint above.")

    poll_secs = int(sys.argv[1]) if len(sys.argv) > 1 else 100
    print(f"[step2] polling get_qrcode_status (up to ~{poll_secs}s; Ctrl-C to stop)...")
    deadline = time.time() + poll_secs
    last = ""
    with httpx.Client(timeout=40) as c:
        while time.time() < deadline:
            try:
                r = c.get(
                    f"{BASE}/ilink/bot/get_qrcode_status",
                    params={"qrcode": qrcode_id},
                    headers=_headers(),
                )
                st = ""
                try:
                    st = (r.json() or {}).get("status", "")
                except Exception:
                    st = f"(non-json http {r.status_code})"
                if st != last:
                    print(f"[poll] status={st!r}")
                    last = st
                if st == "confirmed":
                    j = r.json()
                    tok = j.get("bot_token", "")
                    base_url = j.get("baseurl") or j.get("base_url") or ""
                    masked = (tok[:6] + "..." + tok[-4:]) if len(tok) > 12 else "(short)"
                    print("[SUCCESS] scan confirmed -> this WeChat account IS in the ClawBot rollout.")
                    print(f"[SUCCESS] bot_token={masked}  baseurl={base_url}")
                    print("[note] token masked on purpose; it is a per-user credential.")
                    return 0
            except Exception as e:
                print(f"[poll] error: {type(e).__name__}: {e}")
            time.sleep(2)
    print("[timeout] no confirmation within window. Either not scanned in time, or")
    print("[timeout] your WeChat lacks the ClawBot entry (version <8.0.70 or not gray-rolled).")
    return 1


if __name__ == "__main__":
    sys.exit(main())
