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

HOST = "localhost"
PORT = 9222

TITLE = '放春假的孩子开心到「模糊」，而家长还在焦虑「弯道超车」？'

# 自动获取 D:\wxpic 目录下最新的图片文件
def get_latest_image():
    image_dir = r'D:\wxpic'
    image_extensions = ['*.jpg', '*.jpeg', '*.png', '*.gif', '*.bmp', '*.webp']
    all_images = []
    for ext in image_extensions:
        all_images.extend(glob.glob(os.path.join(image_dir, ext)))
    if not all_images:
        raise FileNotFoundError(f"在 {image_dir} 目录下没有找到图片文件")
    # 按修改时间排序，获取最新的
    latest = max(all_images, key=os.path.getmtime)
    return latest

IMAGE_PATH = get_latest_image()
print(f"[INFO] 使用最新图片: {IMAGE_PATH}")

PARAGRAPHS = [
    ('normal',    '近日，四川德阳一所学校的春假安排引发全网热议——六天假期，无作业。校长宣布消息时，孩子们欢呼雀跃、原地跳起的画面，让无数网友直呼「破防」。有人笑称「建议全国推广」，也有人调侃「请问还招二十多岁的学生吗？」这看似轻松的一幕，实则触动了教育焦虑的深层神经：当「卷」成为常态，我们是否还记得，学习的本质，是成长，而非消耗？'),
    ('divider',   ''),
    ('heading',   '一、被「作业」绑架的童年，何时才能真正「放假」？'),
    ('normal',    '在许多人的记忆里，假期=补习班+试卷+家长的催促。即便是春节、清明这样的传统节日，也常被「弯道超车」的焦虑填满。四川这所学校的「无作业春假」，之所以让人羡慕，是因为它把时间还给了孩子——去赏花、去奔跑、去发呆，去体验春天本该有的样子。'),
    ('normal',    '这并非「放羊」，而是一种教育理念的回归：真正的学习，发生在生活里。'),
    ('normal',    '孩子在田野间观察植物生长，比背诵生物课本更生动；在家庭旅行中规划路线、管理预算，比刷题更能锻炼综合能力。正如网友所言：「小时候欠我的假期，谁还我？」——我们缺的不是知识，而是被允许「浪费」的时间。'),
    ('divider',   ''),
    ('heading',   '二、学业规划的真相：不是「学得更多」，而是「学得更对」'),
    ('normal',    '面对中考、高考、考研、考公、国央企就业等人生关键节点，家长和学生常陷入「多学=多分=多出路」的迷思。但现实是：'),
    ('list',      '中考、高考：拼的是效率与心态，不是刷题时长。'),
    ('list',      '考研、考公：重在策略与信息差，不是盲目跟风。'),
    ('list',      '国央企就业：看重综合素养与实践能力，不是证书堆砌。'),
    ('normal',    '四川这所学校的「无作业春假」，恰恰提醒我们：真正的竞争力，来自松弛感。'),
    ('normal',    '一个被作业压得喘不过气的孩子，很难有创造力；一个被「必须成功」裹挟的青年，很难有抗挫力。而这些，恰恰是未来职场最稀缺的品质。'),
    ('divider',   ''),
    ('heading',   '三、给家长与学生的3条「反内卷」建议'),
    ('normal',    '1. 学会「战略性休息」'),
    ('normal',    '假期不是「补课黄金期」，而是调整节奏、积蓄能量的窗口。与其逼孩子刷题，不如带他去爬山、看电影、做志愿——这些「无用之用」，恰恰是人格塑造的关键。'),
    ('normal',    '2. 重新定义「成功路径」'),
    ('normal',    '中考、高考只是人生一站，考研、考公、国央企就业也非唯一出路。'),
    ('list',      '有人适合走学术路线，有人适合技能发展；'),
    ('list',      '有人擅长应试，有人长于实践。'),
    ('normal',    '尊重个体差异，比盲目追赶更重要。'),
    ('normal',    '3. 培养「终身学习力」'),
    ('normal',    '真正的学霸，不是靠假期刷题，而是拥有自主规划、自我驱动、持续迭代的能力。'),
    ('normal',    '这种能力，恰恰在「无作业」的自由中萌芽——当孩子学会自己安排时间，他才真正掌握了学习的主动权。'),
    ('divider',   ''),
    ('heading',   '四、结语：教育的终极目标，是让人「活得像个人」'),
    ('normal',    '四川这所学校的春假，不是「减负」的终点，而是教育觉醒的起点。'),
    ('normal',    '它告诉我们：'),
    ('list',      '孩子不是考试机器，而是有血有肉的生命；'),
    ('list',      '学习不是苦行僧式的修行，而是探索世界的过程；'),
    ('list',      '成功不是千军万马过独木桥，而是找到适合自己的节奏。'),
    ('normal',    '正如一位网友所说：「我们不是反对学习，而是反对把学习变成折磨。」'),
    ('normal',    '愿每一个孩子，都能在春天里奔跑，而不是在题海中沉没；'),
    ('normal',    '愿每一个青年，都能在人生路上从容前行，而不是在焦虑中迷失。'),
    ('divider',   ''),
    ('normal',    '今日互动'),
    ('normal',    '你支持「春假无作业」吗？'),
    ('normal',    '如果你是家长，你会让孩子在假期「彻底放松」还是「弯道超车」？'),
    ('normal',    '欢迎在评论区留言，我们一起探讨教育的另一种可能。'),
]

def format_paragraph(style, text):
    if style == 'normal':
        return '　　' + text
    elif style == 'heading':
        return '【' + text + '】'
    elif style == 'divider':
        return '————————————————————'
    elif style == 'highlight':
        return text
    elif style == 'list':
        return '• ' + text
    return text

# ── CDP helpers ──────────────────────────────────────────────────────────────
def get_editor_page_id():
    pages = json.loads(urllib.request.urlopen(f'http://{HOST}:{PORT}/json').read())
    for p in pages:
        url = p.get('url', '')
        if 'appmsg' in url and 'action=edit' in url:
            return p['id']
    return None

def ws_connect(page_id):
    import urllib.parse
    ws_url = f"ws://{HOST}:{PORT}/devtools/page/{page_id}"
    parsed = urllib.parse.urlparse(ws_url)
    sock = socket.create_connection((parsed.hostname, parsed.port or 80), timeout=15)
    key = 'dGhlIHNhbXBsZSBub25jZQ=='
    handshake = (
        f'GET {parsed.path} HTTP/1.1\r\n'
        f'Host: {parsed.hostname}:{parsed.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.send(handshake.encode())
    resp = b''
    while b'\r\n\r\n' not in resp:
        resp += sock.recv(4096)
    return sock

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

def recv_frame(sock):
    def rx(n):
        buf = b''
        while len(buf) < n:
            buf += sock.recv(n - len(buf))
        return buf
    h = rx(2)
    n = h[1] & 0x7f
    if n == 126:
        n = int.from_bytes(rx(2), 'big')
    elif n == 127:
        n = int.from_bytes(rx(8), 'big')
    return rx(n).decode('utf-8')

_msg_id = 0
def call(sock, expr, timeout=15):
    global _msg_id
    _msg_id += 1
    mid = _msg_id
    send_frame(sock, json.dumps({
        'id': mid,
        'method': 'Runtime.evaluate',
        'params': {'expression': expr, 'returnByValue': True}
    }))
    sock.settimeout(timeout)
    while True:
        raw = recv_frame(sock)
        obj = json.loads(raw)
        if obj.get('id') == mid:
            res = obj.get('result', {}).get('result', {})
            return res.get('value', res.get('description', ''))

# ── Main ─────────────────────────────────────────────────────────────────────
page_id = get_editor_page_id()
if not page_id:
    print("[ERROR] 未找到公众号编辑页面")
    sys.exit(1)
print(f"[OK] Page ID: {page_id}")

sock = ws_connect(page_id)
print("[OK] WebSocket connected")

# 1. 填写标题
r = call(sock, f"""
(function(){{
    var ta = document.querySelector('#title');
    if(!ta) return 'title_not_found';
    var nativeSetter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value').set;
    nativeSetter.call(ta, {json.dumps(TITLE)});
    ta.dispatchEvent(new Event('input', {{bubbles:true}}));
    ta.dispatchEvent(new Event('change', {{bubbles:true}}));
    return 'title_ok: ' + ta.value.substring(0,30);
}})()
""")
print(f"[title] {r}")
time.sleep(0.5)

# 2. 清空编辑器正文
r = call(sock, """
(function(){
    var editor = document.querySelector('.ProseMirror');
    if(!editor) return 'editor_not_found';
    editor.focus();
    document.execCommand('selectAll');
    document.execCommand('delete');
    return 'cleared';
})()
""")
print(f"[clear] {r}")
time.sleep(0.5)

# 3. 插入图片到正文最前面（构造 ClipboardEvent + base64）
print("[image] 读取图片并插入编辑器开头...")
if os.path.exists(IMAGE_PATH):
    with open(IMAGE_PATH, 'rb') as f:
        img_data = f.read()
    img_b64 = base64.b64encode(img_data).decode('utf-8')
    img_mime = 'image/jpeg'

    r = call(sock, f"""
(function(){{
    var editor = document.querySelector('.ProseMirror');
    if(!editor) return 'editor_not_found';
    editor.focus();

    // 把光标移到最开头
    var sel = window.getSelection();
    var range = document.createRange();
    range.setStart(editor, 0);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);

    // 构造包含图片的 DataTransfer
    var base64Data = '{img_b64}';
    var byteString = atob(base64Data);
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for(var i=0; i<byteString.length; i++) ia[i] = byteString.charCodeAt(i);
    var blob = new Blob([ab], {{type: '{img_mime}'}});
    var file = new File([blob], 'cover_image.jpg', {{type: '{img_mime}'}});

    var dt = new DataTransfer();
    dt.items.add(file);

    var pasteEvent = new ClipboardEvent('paste', {{
        bubbles: true,
        cancelable: true,
        clipboardData: dt
    }});
    editor.dispatchEvent(pasteEvent);
    return 'image_paste_dispatched';
}})()
""", timeout=30)
    print(f"[image] {r}")
    time.sleep(5)  # 等待图片上传完成
else:
    print(f"[image] 图片不存在，跳过插入: {IMAGE_PATH}")
    time.sleep(1)

# 4. 逐段插入正文（图片后面）
paragraphs = [(style, format_paragraph(style, text)) for style, text in PARAGRAPHS]
for i, (style, text) in enumerate(paragraphs):
    escaped = text.replace('\\', '\\\\').replace('"', '\\"').replace('\n', '\\n')
    expr = f"""
(function(){{
    var editor = document.querySelector('.ProseMirror');
    if(!editor) return 'editor_not_found';
    editor.focus();
    document.execCommand('insertParagraph');
    document.execCommand('insertText', false, "{escaped}");
    return 'ok';
}})()
"""
    r = call(sock, expr)
    if r != 'ok':
        print(f"[para {i}] {r}")
    time.sleep(0.05)

print(f"[OK] 正文共 {len(paragraphs)} 段插入完成")
sock.close()
print("[DONE] 填写完成")
