#!/usr/bin/env python3
"""
单线程顺序爬虫运行器 v2 — 只跑快速可重试的脚本。
跳过前次已超时600s的（日跑会处理），只跑 error_type=need_retry 或 elapsed<300 的。
"""
import json
import subprocess
import sys
import os
import time
import re
from datetime import datetime

WORK_DIR = "/root/gov_crawler"
RUN_STATUS_FILE = f"{WORK_DIR}/run_status.json"
TIMEOUT = 300
BATCH_DELAY = 2

def log(msg, end="\n"):
    print(msg, end=end, flush=True)

def load_status():
    with open(RUN_STATUS_FILE) as f:
        return json.load(f)

def save_status(status):
    with open(RUN_STATUS_FILE, "w") as f:
        json.dump(status, f, ensure_ascii=False, indent=2)

def run_script(script_name):
    script_path = os.path.join(WORK_DIR, script_name)
    if not os.path.exists(script_path):
        return False, 0, 0, "脚本文件不存在"

    log(f"\n{'='*60}")
    log(f">> [{datetime.now().strftime('%H:%M:%S')}] 运行: {script_name}")
    log(f"{'='*60}")

    start = time.time()
    try:
        result = subprocess.run(
            [sys.executable, script_path],
            cwd=WORK_DIR,
            capture_output=True,
            text=True,
            timeout=TIMEOUT
        )
        elapsed = time.time() - start
        output = result.stdout + result.stderr

        if result.returncode != 0:
            err_lines = [l for l in result.stderr.split('\n') if l.strip()][-5:]
            err_summary = "; ".join(err_lines[:3])
            return False, 0, elapsed, f"exit_code={result.returncode}, {err_summary}"

        new_count = 0
        for line in output.split('\n'):
            line = line.strip()
            m = re.search(r'新增\s*[:：]?\s*(\d+)\s*条', line)
            if m:
                new_count = int(m.group(1))
                break
            m = re.search(r'成功入库\s*(\d+)\s*条', line)
            if m:
                new_count = int(m.group(1))
                break

        if new_count > 0:
            log(f"  [OK] 新增 {new_count} 条 ({elapsed:.1f}s)")
        else:
            log(f"  [OK] 完成 (新增0条, {elapsed:.1f}s)")

        for line in output.split('\n')[-5:]:
            line = line.strip()
            if line:
                log(f"  | {line}")

        return True, new_count, elapsed, None

    except subprocess.TimeoutExpired:
        elapsed = time.time() - start
        log(f"  [TO] 超时 ({elapsed:.1f}s)")
        return False, 0, elapsed, f"timeout_{TIMEOUT}s"
    except Exception as e:
        elapsed = time.time() - start
        log(f"  [ER] 异常: {e}")
        return False, 0, elapsed, str(e)[:80]

def main():
    status = load_status()
    all_pending = [(k, v) for k, v in status.items()
                   if k.endswith(".py") and v.get("status") == "待重跑"]
    all_pending.sort(key=lambda x: x[1].get("ts", 0))

    # 分类：快速可重试 vs 上次超时的
    quick = []    # elapsed < 300 或 need_retry
    slow = []     # elapsed >= 300 (超时降页后需要长时间)
    for k, v in all_pending:
        elapsed = v.get("elapsed", 600)
        error_type = v.get("error_type", "")
        if error_type == "need_retry" or elapsed < 300:
            quick.append((k, v))
        else:
            slow.append((k, v))

    log(f"\n待重跑总计: {len(all_pending)}")
    log(f"  快速可重试: {len(quick)} (elapsed<300s 或 need_retry)")
    log(f"  上次超时: {len(slow)} (留给日跑处理)")

    if not quick:
        log("没有快速可重试的脚本。")
        log("将这些超时脚本状态改为'待重跑(超时降页)'等日跑评估...")
        for k, v in slow:
            v["status"] = "待重跑(超时降页)"
        save_status(status)
        return

    results = []
    success_count = 0
    fail_count = 0
    total_new = 0

    for i, (script_name, info) in enumerate(quick, 1):
        log(f"\n[{i}/{len(quick)}] ", end="")
        success, new_count, elapsed, error = run_script(script_name)

        if success and error is None:
            new_status = f"成功 +{new_count}条" if new_count > 0 else "成功 +0条"
            status[script_name] = {
                "status": new_status,
                "ts": time.time(),
                "elapsed": round(elapsed, 1)
            }
            success_count += 1
            total_new += new_count
            results.append((script_name, "OK", new_count, f"{elapsed:.1f}s", ""))
        else:
            if "retry_count" not in info:
                info["retry_count"] = 0
            info["retry_count"] += 1
            info["status"] = "待重跑"
            info["ts"] = time.time()
            info["last_error"] = error or "unknown"
            info["elapsed"] = round(elapsed, 1)
            fail_count += 1
            results.append((script_name, "FAIL", 0, f"{elapsed:.1f}s", error or ""))

        save_status(status)

        if i < len(quick):
            log(f"  等待 {BATCH_DELAY}s...")
            time.sleep(BATCH_DELAY)

    log(f"\n{'='*60}")
    log(f"运行完毕! 成功: {success_count}/{len(quick)}, 新增: {total_new} 条")
    if slow:
        log(f"超时脚本(跳过): {len(slow)}个")
    log(f"{'='*60}")

if __name__ == "__main__":
    main()
