# -*- coding: utf-8 -*-
"""用 Page.setUploadFiles 上传封面图"""
import sys, socket, json, time, base64, os, glob
sys.stdout.reconfigure(encoding='utf-8')
HOST = "localhost"; PORT = 28800
PAGE_ID = "70039F329097C14448996F189E3A42A1"

IMG_LIST = glob.glob(r'D:\wxpic\*.jpg') + glob.glob(r'D:\wxpic\*.png')
IMG_PATH = max(IMG_LIST, key=os.path.getmtime) if IMG_LIST else None
print("[INFO] 图片:", IMG_PATH)

def ws():
    import urllib.parse
    u = "ws://%s:%s/devtools/page/%s" % (HOST, PORT, PAGE_ID)
    p = urllib.parse.urlparse(u)
    s = socket.create_connection((p.hostname, p.port or 80), timeout=15)
    req = ("GET %s HTTP/1.1\r\nHost: %s:%s\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n" % (p.path, p.hostname, p.port))
    s.send(req.encode())
    resp = b''
    while b'\r\n\r\n' not in resp: resp += s.recv(4096)
    return s

def send(s, d):
    d = d.encode('utf-8'); n = len(d); m = b'\x01\x02\x03\x04'
    if n <= 125: h = bytes([0x81, n|0x80]) + m
    elif n <= 65535: h = bytes([0x81, 0xFE]) + n.to_bytes(2,'big') + m
    else: h = bytes([0x81, 0xFF]) + n.to_bytes(8,'big') + m
    s.send(h + bytes(d[i]^m[i%4] for i in range(n)))

def recv_raw(sock, n):
    buf = b''
    while len(buf) < n: buf += sock.recv(n - len(buf))
    return buf

def recv_msg(sock):
    h = recv_raw(sock, 2)
    n = h[1] & 0x7f
    if n == 126: n = int.from_bytes(recv_raw(sock, 2), 'big')
    elif n == 127: n = int.from_bytes(recv_raw(sock, 8), 'big')
    return recv_raw(sock, n).decode('utf-8')

_mid = 0
def cdp(s, js, timeout=20):
    global _mid
    _mid += 1
    send(s, json.dumps({'id':_mid,'method':'Runtime.evaluate','params':{'expression':js,'returnByValue':True}}))
    s.settimeout(timeout)
    for _ in range(300):
        try:
            r = json.loads(recv_msg(s))
            if r.get('id') == _mid:
                return r.get('result',{}).get('result',{}).get('value','')
        except: pass
    return ''

def cdp_raw(s, method, params, timeout=20):
    global _mid
    _mid += 1
    msg = json.dumps({'id':_mid,'method':method,'params':params})
    send(s, msg)
    s.settimeout(timeout)
    for _ in range(300):
        try:
            r = json.loads(recv_msg(s))
            if r.get('id') == _mid:
                return r
        except: pass
    return None

s = ws()
print("[OK] Connected\n")

# Step 1: 用 Runtime.evaluate 找封面 input（index=1），获取其 objectId
print("[Step 1] 找封面 input 并获取 objectId...")
r = cdp(s, """
(function() {
  var inputs = document.querySelectorAll('input[type=file]');
  // index=1 是封面图
  var inp = inputs[1];
  if (!inp) return 'no_input_1';
  // 返回一些标识信息
  return 'found:index=1,accept=' + inp.accept;
})()
""")
print("  input:", r)

# Step 2: 用 document.deepQuerySelector 找 nodeId
# 先用 DOM.getDocument
print("\n[Step 2] DOM.getDocument...")
r = cdp_raw(s, 'DOM.getDocument', {'depth': 0})
print("  result:", r)
root_node_id = None
if r and 'result' in r:
    root_node_id = r['result'].get('root', {}).get('nodeId')
    print("  root nodeId:", root_node_id)

# Step 3: 用 JS 获取元素的 __reactFiber 等内部属性（包含 backendNodeId）
print("\n[Step 3] 获取元素的 nodeId...")
r = cdp(s, """
(function() {
  var inputs = document.querySelectorAll('input[type=file]');
  var inp = inputs[1];
  if (!inp) return null;
  // 找 __reactFiber
  var fiber = null;
  var keys = Object.keys(inp);
  for (var k of keys) {
    if (k.includes('reactFiber') || k.includes('Fiber')) {
      fiber = {key: k, type: typeof inp[k]};
      break;
    }
  }
  return JSON.stringify({
    tagName: inp.tagName,
    accept: inp.accept,
    fiber: fiber,
    // 尝试找 __proto__
    proto: Object.getPrototypeOf(inp) ? Object.getPrototypeOf(inp).constructor.name : 'unknown'
  });
}())
""")
print("  fiber info:", r)

# Step 4: 用 document.querySelector + evaluate 的 special reference
# Chrome CDP 支持通过 "$0" 引用当前选中的 DOM 节点
# 我们可以用 JS 把元素设为 $0，然后获取其 objectId
print("\n[Step 4] 通过 JS 获取 DOM 节点的 CDP objectId...")
r = cdp_raw(s, 'Runtime.evaluate', {
    'expression': """
(function() {
  var inputs = document.querySelectorAll('input[type=file]');
  var inp = inputs[1];
  if (!inp) return null;
  // 返回元素的唯一标识
  return {
    tagName: inp.tagName,
    accept: inp.accept,
    id: inp.id,
    // 返回 node 在 DOM tree 中的位置（用于构造 backendNodeId）
    // 通过 $0 方式
    '$0': 'will_be_set'
  };
}())
""",
    'returnByValue': True,
    'objectGroup': 'file-input-group'
})
print("  evaluate result:", r)
result_obj = None
if r and 'result' in r:
    result_obj = r['result'].get('result', {})
    print("  object:", result_obj.get('object', {}).get('objectId', 'N/A') if 'object' in result_obj else 'no objectId')
    print("  type:", result_obj.get('type', ''))
    print("  value:", str(result_obj.get('value', ''))[:100])

# 获取 objectId 后，用 Page.setUploadFiles
if result_obj and 'object' in result_obj:
    obj_id = result_obj['object'].get('objectId')
    if obj_id:
        print("\n[Step 5] Page.setUploadFiles...")
        # 先让用户（我们）选中元素
        # 通过 $0 引用
        r2 = cdp_raw(s, 'Runtime.evaluate', {
            'expression': 'var inp = document.querySelectorAll("input[type=file]")[1]; if (inp) { inp.scrollIntoView(); inp.focus(); } inp ? "ok" : "not_found";',
            'returnByValue': True
        })
        print("  focus input:", r2)
        time.sleep(0.5)
        
        # 直接设置文件
        r3 = cdp_raw(s, 'Page.setUploadFiles', {
            'files': [IMG_PATH],
            'nodeId': None  # 不知道 nodeId，先试试 None 或 0
        })
        print("  setUploadFiles (nodeId=nil):", r3)

# 另一种方法：通过 CDP 的 NodeId
# 找 input 的 nodeId
print("\n[Alternative] 用 document 找 nodeId...")
# 先让 CDP 返回 backendNodeId
r = cdp_raw(s, 'Runtime.evaluate', {
    'expression': 'window.__FILE_INPUT_1__ = document.querySelectorAll("input[type=file]")[1]; window.__FILE_INPUT_1__ ? "ok" : "null"',
    'returnByValue': True
})
print("  stored input:", r)

# 用 DOM.requestNode（如果支持）
r = cdp_raw(s, 'DOM.requestNode', {
    'objectId': obj_id if result_obj and 'object' in result_obj else None
})
print("  requestNode:", r)

s.close()
