# -*- coding: utf-8 -*-
import sys
sys.stdout.reconfigure(encoding='utf-8')
import socket, base64, json, time, urllib.request

# ── 超时设置（秒）────────────────────────────────────────────────────────────
LOGIN_WAIT_TIMEOUT = 120   # 最多等待扫码登录的时间
LOGIN_CHECK_INTERVAL = 3   # 每隔多少秒检测一次

def get_pages():
    data = json.loads(urllib.request.urlopen('http://localhost:9222/json', timeout=3).read())
    return [t for t in data if t.get('type') == 'page']

def ws_handshake(s, host, path):
    key = base64.b64encode(b'workbuddy12345678').decode()
    req = (
        f"GET {path} HTTP/1.1\r\n"
        f"Host: {host}\r\n"
        "Upgrade: websocket\r\n"
        "Connection: Upgrade\r\n"
        f"Sec-WebSocket-Key: {key}\r\n"
        "Sec-WebSocket-Version: 13\r\n\r\n"
    )
    s.sendall(req.encode())
    resp = b''
    while b'\r\n\r\n' not in resp:
        resp += s.recv(4096)

def ws_send(s, obj):
    data = json.dumps(obj).encode('utf-8')
    length = len(data)
    mask_key = b'\x00\x00\x00\x00'
    if length < 126:
        header = bytes([0x81, 0x80 | length])
    elif length < 65536:
        header = bytes([0x81, 0xFE, length >> 8, length & 0xFF])
    else:
        header = bytes([0x81, 0xFF] + [(length >> (8*i)) & 0xFF for i in range(7, -1, -1)])
    s.sendall(header + mask_key + data)

def ws_recv(s):
    def recv_exact(n):
        buf = b''
        while len(buf) < n:
            chunk = s.recv(n - len(buf))
            if not chunk:
                raise ConnectionError('connection closed')
            buf += chunk
        return buf
    b0, b1 = recv_exact(2)
    masked = (b1 & 0x80) != 0
    length = b1 & 0x7F
    if length == 126:
        length = int.from_bytes(recv_exact(2), 'big')
    elif length == 127:
        length = int.from_bytes(recv_exact(8), 'big')
    mask = recv_exact(4) if masked else b'\x00\x00\x00\x00'
    payload = bytearray(recv_exact(length))
    if masked:
        for i in range(length):
            payload[i] ^= mask[i % 4]
    return json.loads(payload.decode('utf-8'))

def cdp_call(s, mid, method, params=None):
    msg = {"id": mid, "method": method}
    if params:
        msg["params"] = params
    ws_send(s, msg)
    while True:
        r = ws_recv(s)
        if r.get('id') == mid:
            return r

def check_login_status(page_id):
    """
    检测登录状态：
    - 已登录：URL 包含 token= 参数，说明已进入后台
    - 未登录：URL 是 mp.weixin.qq.com 但没有 token，停在登录页
    返回 True 表示已登录
    """
    try:
        pages = get_pages()
        for p in pages:
            url = p.get('url', '')
            if 'mp.weixin.qq.com' in url and 'token=' in url:
                return True, url
        # 还未登录，再用JS检测一下后台元素是否存在
        s2 = socket.socket()
        s2.settimeout(5)
        s2.connect(('127.0.0.1', 9222))
        ws_handshake(s2, '127.0.0.1', f'/devtools/page/{page_id}')
        # 检测后台主菜单元素（已登录才有）
        msg_id = 99
        ws_send(s2, {
            'id': msg_id,
            'method': 'Runtime.evaluate',
            'params': {
                'expression': '''(function(){
                    var url = window.location.href;
                    var hasToken = url.indexOf('token=') !== -1;
                    var hasMenu = !!document.querySelector('#menuBar, .nav_menu, #js_main_content, .weui-desktop-navbar');
                    return JSON.stringify({url: url.substring(0,120), hasToken: hasToken, hasMenu: hasMenu});
                })()''',
                'returnByValue': True
            }
        })
        s2.settimeout(8)
        while True:
            r = ws_recv(s2)
            if r.get('id') == msg_id:
                val = r.get('result', {}).get('result', {}).get('value', '{}')
                info = json.loads(val)
                s2.close()
                if info.get('hasToken') or info.get('hasMenu'):
                    return True, info.get('url', '')
                return False, info.get('url', '')
    except Exception as e:
        return False, str(e)

# ── 主流程 ───────────────────────────────────────────────────────────────────
pages = get_pages()
print(f'[INFO] Found {len(pages)} page(s)')
for i, p in enumerate(pages):
    print(f'  {i} {p["id"]} {p["url"][:80]}')

page_id = pages[0]['id']
print(f'[INFO] Navigating page {page_id} to mp.weixin.qq.com...')

s = socket.socket()
s.settimeout(10)
s.connect(('127.0.0.1', 9222))
ws_handshake(s, '127.0.0.1', f'/devtools/page/{page_id}')
print('[OK] WebSocket connected')

# 导航到公众号后台
r = cdp_call(s, 1, 'Page.navigate', {'url': 'https://mp.weixin.qq.com'})
print(f'[OK] Navigate sent')
s.close()

# 等待页面初步加载
time.sleep(3)

# ── 自动检测登录状态，最多等待 LOGIN_WAIT_TIMEOUT 秒 ─────────────────────────
print(f'\n[等待登录] 自动检测中，最多等待 {LOGIN_WAIT_TIMEOUT} 秒...')
print('[提示] 如需扫码登录，请现在扫描。无需任何手动操作，登录后自动继续。\n')

start_time = time.time()
logged_in = False
elapsed = 0

while elapsed < LOGIN_WAIT_TIMEOUT:
    ok, url = check_login_status(page_id)
    elapsed = int(time.time() - start_time)
    if ok:
        print(f'[✅ 已登录] 检测到登录态！耗时 {elapsed} 秒')
        print(f'[URL] {url}')
        logged_in = True
        break
    else:
        remaining = LOGIN_WAIT_TIMEOUT - elapsed
        print(f'[等待中] {elapsed}s 已过，还剩 {remaining}s | 当前URL: {str(url)[:80]}')
        time.sleep(LOGIN_CHECK_INTERVAL)

if not logged_in:
    print(f'\n[❌ 超时] {LOGIN_WAIT_TIMEOUT} 秒内未检测到登录，请检查是否已扫码登录。')
    print('[提示] 请手动登录后重新运行本脚本，或增大 LOGIN_WAIT_TIMEOUT 的值。')
    sys.exit(1)

print('\n[DONE] 登录确认，可以继续下一步。')
