# -*- coding: utf-8 -*-
"""
微信公众号一键批量发布脚本
===========================
用法：
    python batch_publish.py

功能：
    按顺序自动执行完整发布流程，每步执行完毕确认后继续：
    Step 1  start_chrome_debug.py    启动 Chrome 调试模式
    Step 2  cdp_navigate.py          导航到公众号后台（检测登录状态）
    Step 3  cdp_new_article.py       打开新建文章编辑界面
    Step 4  cdp_fill_article.py      填写标题和正文（含图片）
    Step 5  cdp_set_author_original.py  设置作者 + 原创声明
    Step 6  cdp_set_cover.py         设置封面（从正文第一张图）
    Step 7  cdp_save_draft.py        保存为草稿（等待 20 秒）

人工介入点（仅一个）：
    Step 2 检测到未登录时，需要扫码登录，扫完后按回车继续。

配置说明：
    - SCRIPTS_DIR：本文件所在目录（默认自动获取）
    - STEP_TIMEOUT：每步最长等待时间（秒），默认 120
"""

import subprocess
import sys
import os
import time
import json
import urllib.request
import socket

# 强制 UTF-8 输出，避免 Windows GBK 编码报错
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
sys.stderr.reconfigure(encoding='utf-8', errors='replace')

# ── 配置 ──────────────────────────────────────────────────────────────────────
SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
PYTHON = sys.executable
STEP_TIMEOUT = 120  # 每步最长等待时间（秒）

# ── 步骤定义 ──────────────────────────────────────────────────────────────────
STEPS = [
    {
        "name": "Step 1：启动 Chrome 调试模式",
        "script": "start_chrome_debug.py",
        "timeout": 15,
        "pause_after": False,
        "desc": "检测 9222 端口，未启动则自动启动 Chrome"
    },
    {
        "name": "Step 2：导航到公众号后台",
        "script": "cdp_navigate.py",
        "timeout": 30,
        "pause_after": True,   # 可能需要扫码登录
        "pause_msg": "⚠️  如果需要扫码登录，请现在扫码，登录完成后按回车继续...",
        "desc": "检测登录状态，未登录需扫码"
    },
    {
        "name": "Step 3：打开新建文章编辑界面",
        "script": "cdp_new_article.py",
        "timeout": 30,
        "pause_after": False,
        "desc": "自动提取 token，跳转到编辑页"
    },
    {
        "name": "Step 4：填写标题和正文",
        "script": "cdp_fill_article.py",
        "timeout": 60,
        "pause_after": False,
        "desc": "填入标题、段落文字、插入图片"
    },
    {
        "name": "Step 5：设置作者 + 原创声明",
        "script": "cdp_set_author_original.py",
        "timeout": 30,
        "pause_after": False,
        "desc": "填写作者名称并开启原创声明"
    },
    {
        "name": "Step 6：设置封面",
        "script": "cdp_set_cover.py",
        "timeout": 60,
        "pause_after": False,
        "desc": "从正文第一张图片设置封面"
    },
    {
        "name": "Step 7：保存为草稿",
        "script": "cdp_save_draft.py",
        "timeout": STEP_TIMEOUT,
        "pause_after": False,
        "desc": "点击保存，等待 20 秒确认保存完成"
    },
]

# ── 截图 & 登录检测 ───────────────────────────────────────────────────────────
def check_logged_in():
    """通过 HTTP /json 检测是否已登录（URL 含 token=）"""
    try:
        pages = json.loads(urllib.request.urlopen('http://localhost:9222/json', timeout=3).read())
        for p in pages:
            url = p.get('url', '')
            if 'mp.weixin.qq.com' in url and 'token=' in url:
                return True
        return False
    except Exception:
        return False

def take_screenshot(out_path):
    """
    用 CDP Page.captureScreenshot 截图并保存。
    返回 True/False 表示是否成功。
    """
    script = os.path.join(SCRIPTS_DIR, 'cdp_screenshot.py')
    if not os.path.exists(script):
        return False
    try:
        result = subprocess.run(
            [PYTHON, script],
            timeout=15,
            capture_output=True,
            encoding='utf-8',
            errors='replace'
        )
        # 解析输出中的 SCREENSHOT_SAVED:<path>
        for line in result.stdout.splitlines():
            if line.startswith('SCREENSHOT_SAVED:'):
                saved = line.split(':', 1)[1].strip()
                print(f"\n  [截图] 已保存：{saved}")
                return saved
        return False
    except Exception as e:
        print(f"  [截图失败] {e}")
        return False

def wait_for_login_with_screenshot(timeout=120, check_interval=4, screenshot_interval=8):
    """
    Step 2 后调用：
    1. 先检测是否已登录，若已登录直接返回 True
    2. 否则截图（显示二维码）并每 screenshot_interval 秒刷新一次截图
    3. 轮询登录状态，登录后返回 True
    4. 超时返回 False
    """
    # 已登录则免截图
    if check_logged_in():
        print("  [OK] 已检测到登录态，跳过扫码步骤")
        return True

    print("\n" + "=" * 60)
    print("  [需要扫码登录]")
    print("  正在截图二维码，请稍候...")
    print("=" * 60)

    screenshot_path = os.path.join(SCRIPTS_DIR, 'qrcode_login.png')
    shot = take_screenshot(screenshot_path)
    if shot:
        # 用系统默认图片查看器打开截图，方便用户扫码
        try:
            os.startfile(shot)
            print(f"  [提示] 截图已自动打开，请用微信扫描二维码登录")
        except Exception:
            print(f"  [提示] 请手动打开截图文件扫码：{shot}")
    else:
        print("  [警告] 截图失败，请切换到 Chrome 窗口手动扫码")

    print(f"\n  等待登录中（最多 {timeout} 秒，登录后自动继续）...\n")

    start = time.time()
    last_shot = start
    while time.time() - start < timeout:
        if check_logged_in():
            elapsed = int(time.time() - start)
            print(f"  [OK] 扫码成功！耗时 {elapsed} 秒，自动继续...")
            return True

        # 每 screenshot_interval 秒刷新一次截图（二维码可能刷新）
        if time.time() - last_shot >= screenshot_interval:
            shot = take_screenshot(screenshot_path)
            if shot:
                try:
                    os.startfile(shot)
                    print(f"  [截图刷新] 二维码已更新，请重新扫码")
                except Exception:
                    print(f"  [截图刷新] {shot}")
            last_shot = time.time()

        remaining = int(timeout - (time.time() - start))
        print(f"  等待中... 剩余 {remaining}s", end='\r', flush=True)
        time.sleep(check_interval)

    print(f"\n  [超时] {timeout} 秒内未检测到登录，请手动登录后重试")
    return False


def banner(text, char="=", width=60):
    print("\n" + char * width)
    print(f"  {text}")
    print(char * width)

def step_log(step_name, msg, icon="ℹ️"):
    print(f"  [{icon}] [{step_name}] {msg}")

def run_step(step):
    script_path = os.path.join(SCRIPTS_DIR, step["script"])
    if not os.path.exists(script_path):
        step_log(step["name"], f"脚本不存在：{script_path}", "❌")
        return False

    step_log(step["name"], f"执行：{step['script']}  —  {step['desc']}", "▶")
    start = time.time()

    try:
        result = subprocess.run(
            [PYTHON, script_path],
            timeout=step["timeout"],
            cwd=SCRIPTS_DIR,
            encoding="utf-8",
            errors="replace"
        )
        elapsed = time.time() - start
        if result.returncode == 0:
            step_log(step["name"], f"完成（{elapsed:.1f}s）", "✅")
            return True
        else:
            step_log(step["name"], f"退出码 {result.returncode}，视为失败", "❌")
            return False
    except subprocess.TimeoutExpired:
        step_log(step["name"], f"超时（>{step['timeout']}s），请检查 Chrome 状态", "⏰")
        return False
    except Exception as e:
        step_log(step["name"], f"异常：{e}", "❌")
        return False

# ── 主流程 ────────────────────────────────────────────────────────────────────
def main():
    banner("微信公众号一键发布脚本  v2.0", "=")
    print(f"  脚本目录：{SCRIPTS_DIR}")
    print(f"  共 {len(STEPS)} 个步骤")
    print(f"  人工介入点：Step 2（扫码登录，如 session 已有效则自动跳过）\n")

    total_start = time.time()
    failed_step = None

    for i, step in enumerate(STEPS):
        banner(f"{step['name']}", "-", 60)

        ok = run_step(step)

        if not ok:
            failed_step = step
            break

        # Step 2 完成后：检测登录状态，必要时截图并等待扫码
        if step.get("pause_after"):
            ok_login = wait_for_login_with_screenshot(timeout=120, check_interval=4, screenshot_interval=10)
            if not ok_login:
                banner("登录超时，发布中止", "!")
                sys.exit(1)

        time.sleep(1)  # 步骤间短暂停顿，避免页面响应不及

    total_elapsed = time.time() - total_start

    if failed_step:
        banner(f"❌ 发布失败，停在：{failed_step['name']}", "!")
        print(f"  请检查 Chrome 状态，或单独运行该步骤的脚本排查问题。")
        print(f"  总耗时：{total_elapsed:.1f}s")
        sys.exit(1)
    else:
        banner("🎉 全部步骤完成！草稿已保存", "=")
        print(f"  总耗时：{total_elapsed:.1f}s")
        print()
        print("  后续操作：")
        print("  1. 打开微信公众号后台 → 素材管理 → 草稿箱")
        print("  2. 确认封面、标题、正文无误")
        print('  3. 点击\u201c发表\u201d完成最终发布')
        print()
        print("  Chrome 保持打开，确认无误后可手动关闭。")

if __name__ == "__main__":
    main()
