# -*- 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',  '近日，中国科学家成功克隆多年生水稻关键基因EBT1，成果登上《科学》封面，实现水稻「一次播种、连续多年收获」的重大突破。这不仅是农业科技里程碑，更是一条分数友好、就业稳定、国家刚需的升学新赛道。本文从学业规划视角，为中考、高考、考研、考公学生梳理完整路径。'),
    ('divider', ''),
    ('heading', '一、全学段升学路径规划'),
    ('heading', '中考／初高中衔接'),
    ('list',    '选科建议：生物＋化学必选，物理＋生物组合适配；重视实验探究与理科逻辑，为农学、生物育种打下基础。'),
    ('list',    '规划方向：避开分数内卷，提前锁定新农科赛道，低分也能上优质本科。'),
    ('heading', '高考报考（核心赛道）'),
    ('normal',  '对口本科专业：'),
    ('list',    '农学（作物遗传育种方向）'),
    ('list',    '生物育种科学（国家紧缺新农科）'),
    ('list',    '种子科学与工程'),
    ('list',    '植物科学与技术'),
    ('list',    '农业资源与环境'),
    ('normal',  '选科要求：必选化学＋生物，部分院校接受物理＋生物组合。只要达到本科线，都可规划。'),
    ('heading', '考研深造'),
    ('list',    '核心方向：作物遗传育种、作物栽培学与耕作学、分子生物学、农艺与种业（专硕好上岸）'),
    ('list',    '优势：国家线低、报录比友好、跨考门槛低，科研院所与高校资源充足。'),
    ('heading', '考公／就业方向'),
    ('list',    '体制内：农业农村局、农技推广中心、种业监管、乡村振兴局'),
    ('list',    '企业端：种业龙头、农业科技公司、农机与智慧农业企业'),
    ('list',    '科研端：农科院、高校实验室、国际农业合作项目'),
    ('divider', ''),
    ('heading', '二、重点院校专业推荐'),
    ('normal',  '顶尖科研梯队（双一流）'),
    ('list',    '中国农业大学：作物学A＋，育种全国顶尖'),
    ('list',    '南京农业大学、华中农业大学：农业生物技术强势'),
    ('list',    '西北农林科技大学：旱区作物育种优势明显'),
    ('normal',  '多年生水稻核心院校（首选）'),
    ('normal',  '云南大学'),
    ('list',    '胡凤益团队深耕多年生稻20余年，育成云大25、云大107等品种'),
    ('list',    '设有多年生稻研究中心、科技小院，产学研一体化'),
    ('list',    '本科：生物育种科学、植物科学与技术；硕博：作物学、智能设计育种'),
    ('normal',  '高性价比梯队（分数友好）'),
    ('normal',  '四川农业大学、山东农业大学、扬州大学、沈阳农业大学，作物学实力强、就业稳。'),
    ('divider', ''),
    ('heading', '三、学业规划关键建议'),
    ('list',    '本科线边缘考生：优先报考农学类专业，稳拿本科，就业竞争小。'),
    ('list',    '考研党：选择农艺与种业专硕，上岸易，对口多年生稻产业化岗位。'),
    ('list',    '考公党：农技、种业、乡村振兴岗位扩招，竞争远低于热门岗位。'),
    ('list',    '学术方向：主攻云南大学、中科院相关团队，直通顶尖科研平台。'),
    ('divider', ''),
    ('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')
    # 根据扩展名判断 mime 类型
    ext = os.path.splitext(IMAGE_PATH)[1].lower()
    img_mime = 'image/jpeg' if ext in ['.jpg', '.jpeg'] else 'image/png'

    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] 填写完成")
