import socket, json, time, struct, urllib.request, base64

HOST, PORT = '127.0.0.1', 9222

def get_pages():
    with urllib.request.urlopen(f'http://{HOST}:{PORT}/json') as r:
        return json.loads(r.read())

def ws_connect(page_id):
    path = f'/devtools/page/{page_id}'
    sock = socket.create_connection((HOST, PORT), timeout=15)
    key = 'dGhlIHNhbXBsZSBub25jZQ=='
    handshake = (f'GET {path} HTTP/1.1\r\nHost: {HOST}:{PORT}\r\n'
                 'Upgrade: websocket\r\nConnection: Upgrade\r\n'
                 f'Sec-WebSocket-Key: {key}\r\nSec-WebSocket-Version: 13\r\n\r\n')
    sock.sendall(handshake.encode())
    resp = b''
    while b'\r\n\r\n' not in resp:
        resp += sock.recv(4096)
    return sock

def send_raw(sock, msg_str):
    payload = msg_str.encode('utf-8')
    n = len(payload)
    mask_key = b'\x00\x00\x00\x00'
    if n < 126:
        header = bytes([0x81, 0x80 | n]) + mask_key
    elif n < 65536:
        header = bytes([0x81, 0xfe]) + struct.pack('>H', n) + mask_key
    else:
        header = bytes([0x81, 0xff]) + struct.pack('>Q', n) + mask_key
    sock.sendall(header + payload)

def call(sock, expr, timeout=10):
    cmd = json.dumps({'id': 99, 'method': 'Runtime.evaluate',
                      'params': {'expression': expr, 'returnByValue': True, 'awaitPromise': True}})
    send_raw(sock, cmd)
    time.sleep(0.3)
    sock.settimeout(timeout)
    data = b''
    try:
        while True:
            chunk = sock.recv(65536)
            if not chunk: break
            data += chunk
            try:
                idx = 0; idx += 1
                masked_len = data[idx]; idx += 1
                length = masked_len & 0x7f
                if length == 126: length = struct.unpack('>H', data[idx:idx+2])[0]; idx += 2
                elif length == 127: length = struct.unpack('>Q', data[idx:idx+8])[0]; idx += 8
                if len(data) >= idx + length:
                    r = json.loads(data[idx:idx+length].decode('utf-8'))
                    v = r.get('result', {}).get('result', {})
                    return v.get('value', v.get('description', ''))
            except: continue
    except: pass
    return None

def wait_for_btn(sock, btn_text, max_wait=10):
    """轮询等待指定文字的可见按钮，返回点击结果"""
    for i in range(max_wait):
        r = call(sock, f'''
        (function(){{
            var btns = Array.from(document.querySelectorAll('button'));
            var target = btns.find(function(b){{
                var rect = b.getBoundingClientRect();
                return b.innerText && b.innerText.trim() === '{btn_text}' && rect.width > 0 && rect.height > 0;
            }});
            if(!target) return 'NOT_FOUND';
            target.click();
            return 'clicked: ' + target.className;
        }})()
        ''')
        if r and r != 'NOT_FOUND':
            return r
        print(f'  等待"{btn_text}"按钮... {i+1}s')
        time.sleep(1)
    return 'TIMEOUT'

# ========== 开始 ==========
pages = get_pages()
edit_page = next((p for p in pages if 'mp.weixin.qq.com' in p.get('url', '') and p.get('type') == 'page'), None)
if not edit_page:
    print('[ERROR] 未找到编辑页面')
    exit(1)

sock = ws_connect(edit_page['id'])

# Step 1: 点击封面区域
print('[Step 1] 点击封面区域...')
r = call(sock, '''
(function(){
    var cover = document.querySelector('#js_cover_area');
    if(!cover) return 'NO_COVER_AREA';
    cover.click();
    return 'clicked';
})()
''')
print('结果:', r)
time.sleep(2)

# Step 2: 点击"从正文选择"
print('[Step 2] 点击"从正文选择"...')
r = call(sock, '''
(function(){
    var btn = document.querySelector('a.js_selectCoverFromContent');
    if(!btn) return 'NO_BTN';
    btn.click();
    return 'clicked';
})()
''')
print('结果:', r)
time.sleep(3)

# Step 3: 点击弹窗中的图片
print('[Step 3] 点击图片...')
r = call(sock, '''
(function(){
    var dialog = document.querySelector('.weui-desktop-dialog_img-picker');
    if(!dialog) return 'NO_DIALOG';
    var li = dialog.querySelector('li.appmsg_content_img_item');
    if(!li) {
        var allLi = Array.from(dialog.querySelectorAll('li'));
        li = allLi.find(function(l){ var r=l.getBoundingClientRect(); return r.width>0&&r.height>0; });
    }
    if(!li) return 'NO_IMG_LI';
    li.click();
    return 'clicked li, cls:' + li.className;
})()
''')
print('结果:', r)
time.sleep(1.5)

# Step 4: 点击"下一步"
print('[Step 4] 点击"下一步"...')
r = wait_for_btn(sock, '下一步', max_wait=8)
print('结果:', r)
time.sleep(2)  # 等裁剪界面加载

# Step 5: 点击"确认"
print('[Step 5] 点击"确认"...')
r = wait_for_btn(sock, '确认', max_wait=10)
print('结果:', r)
time.sleep(3)  # 等封面渲染

# Step 6: 验证封面
print('[Step 6] 验证封面...')
cover = call(sock, '''
(function(){
    var area = document.querySelector('#js_cover_area');
    if(!area) return 'NO_COVER_AREA';
    var imgs = area.querySelectorAll('img');
    return JSON.stringify({hasImg: imgs.length>0, imgCount:imgs.length, text:(area.innerText||'').substring(0,40)});
})()
''')
print('封面状态:', cover)

try:
    cs = json.loads(cover)
    if cs.get('hasImg'):
        print('[OK] 封面设置成功！')
    else:
        print('[WARNING] 封面图片未检测到，继续保存')
except:
    pass

sock.close()
print('[DONE] 封面设置完成')
