"""探测二:微信 ClawBot 的【对话】与【主动推送】能力(命门验证)。

流程(都在一次运行里,不落库):
  1. 扫码绑定拿 bot_token(同探测一)
  2. getupdates 长轮询,等你给「微信 ClawBot」联系人发一条消息
  3. 收到后,依次测三种发送,逐一报 ret:
     A. 带 context_token 回复            -> 验「被动回复」是否通
     B. 等 25s 后,用【同一个】context_token 再发 -> 验「开口一次后能否延迟主动推」
     C. context_token 置空再发           -> 验「冷推(无 token)」是否被拒
  判读:
     A 通 = 双向对话成立
     B 通 = 用户开口一次后可后续推送(简报可走"先开口、后定时推"的弱化版)
     C 通 = 可冷推(几乎不可能,但要验)
     B/C 都不通 = ClawBot 纯被动回复,定时主动推送这条路不成立

ASCII-only 输出。bot_token 不打印。
"""
from __future__ import annotations

import base64
import os
import random
import sys
import time

import httpx
import segno

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


def _uin() -> str:
    return base64.b64encode(str(random.randint(0, 2**32 - 1)).encode()).decode()


def _headers(token: str | None = None) -> dict:
    h = {
        "Content-Type": "application/json",
        "AuthorizationType": "ilink_bot_token",
        "X-WECHAT-UIN": _uin(),
    }
    if token:
        h["Authorization"] = f"Bearer {token}"
    return h


def bind() -> tuple[str, str] | None:
    print("[bind] GET get_bot_qrcode ...")
    with httpx.Client(timeout=20) as c:
        r = c.get(f"{BASE}/ilink/bot/get_bot_qrcode",
                  params={"bot_type": "3"}, headers=_headers())
    if r.status_code != 200:
        print(f"[FAIL] get_bot_qrcode http {r.status_code}: {r.text[:300]}")
        return None
    d = r.json()
    qid = d.get("qrcode", "")
    link = d.get("qrcode_img_content", "")
    segno.make(link, error="m").save(QR_PATH, scale=8, border=3)
    try:
        import webbrowser
        webbrowser.open("file://" + QR_PATH.replace("\\", "/"))
    except Exception:
        pass
    print(f"[bind] QR opened -> {QR_PATH}  SCAN IT NOW with phone WeChat.")
    deadline = time.time() + 180
    with httpx.Client(timeout=40) as c:
        last = ""
        while time.time() < deadline:
            try:
                r = c.get(f"{BASE}/ilink/bot/get_qrcode_status",
                          params={"qrcode": qid}, headers=_headers())
                j = r.json()
                st = j.get("status", "")
                if st != last:
                    print(f"[bind] status={st!r}")
                    last = st
                if st == "confirmed":
                    print("[bind] confirmed.")
                    return j.get("bot_token", ""), (j.get("baseurl") or BASE)
                if st == "expired":
                    print("[bind] QR expired before scan.")
                    return None
            except Exception as e:
                print(f"[bind] poll err: {type(e).__name__}: {e}")
            time.sleep(2)
    print("[bind] timeout waiting for scan.")
    return None


def _send(client: httpx.Client, token: str, to_user: str, text: str,
          context_token: str) -> dict:
    body = {
        "msg": {
            "to_user_id": to_user,
            "message_type": 2,
            "message_state": 2,
            "context_token": context_token,
            "item_list": [{"type": 1, "text_item": {"text": text}}],
        }
    }
    r = client.post(f"{BASE}/ilink/bot/sendmessage",
                    json=body, headers=_headers(token))
    try:
        return {"http": r.status_code, "json": r.json()}
    except Exception:
        return {"http": r.status_code, "text": r.text[:300]}


def main() -> int:
    b = bind()
    if not b:
        return 2
    token, base_url = b
    global BASE
    BASE = base_url or BASE

    print("[chat] now SEND a message (e.g. 'hi') to the WeChat ClawBot contact on your phone.")
    print("[chat] waiting via getupdates (up to ~150s)...")
    buf = ""
    deadline = time.time() + 150
    got = None
    with httpx.Client(timeout=40) as c:
        while time.time() < deadline and got is None:
            try:
                r = c.post(f"{BASE}/ilink/bot/getupdates",
                           json={"get_updates_buf": buf,
                                 "base_info": {"channel_version": CHANNEL_VER}},
                           headers=_headers(token))
                j = r.json()
                buf = j.get("get_updates_buf", buf)
                for m in j.get("msgs", []) or []:
                    txt = ""
                    for it in m.get("item_list", []) or []:
                        txt += (it.get("text_item", {}) or {}).get("text", "")
                    print(f"[chat] <- from={m.get('from_user_id')} text={txt!r}")
                    got = m
                    break
            except Exception as e:
                print(f"[chat] getupdates err: {type(e).__name__}: {e}")
                time.sleep(2)
    if got is None:
        print("[chat] no message received in window. Re-run and send promptly after scan.")
        return 1

    to_user = got.get("from_user_id", "")
    ctx = got.get("context_token", "")
    print(f"[chat] captured to_user={to_user}  context_token_len={len(ctx)}")

    with httpx.Client(timeout=30) as c:
        print("\n[testA] reply WITH context_token ...")
        ra = _send(c, token, to_user, "[zcbot 测试A] 收到你的消息,这是带 token 的回复。", ctx)
        print(f"[testA] result={ra}")

        print("\n[testB] wait 25s, then push again with the SAME context_token (delayed proactive)...")
        time.sleep(25)
        rb = _send(c, token, to_user, "[zcbot 测试B] 这是25秒后用同一token的延迟主动推送。", ctx)
        print(f"[testB] result={rb}")

        print("\n[testC] push with EMPTY context_token (cold push) ...")
        rc = _send(c, token, to_user, "[zcbot 测试C] 这是空token的冷推送。", "")
        print(f"[testC] result={rc}")

    def ok(r):
        j = r.get("json") or {}
        return r.get("http") == 200 and j.get("ret", -1) == 0

    print("\n========== VERDICT ==========")
    print(f"A reply(with token)        : {'OK' if ok(ra) else 'FAIL'}")
    print(f"B delayed push(same token) : {'OK' if ok(rb) else 'FAIL'}")
    print(f"C cold push(empty token)   : {'OK' if ok(rc) else 'FAIL'}")
    print("Interpretation:")
    print(" - A only           -> reply-only; scheduled PROACTIVE push NOT possible.")
    print(" - A+B              -> after user opens chat once, delayed push works (weak push OK).")
    print(" - C                -> true cold push works (unlikely).")
    return 0


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