#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""backfill_dis_batch.py —— Dis-db 回填昨日(09-24)新增 8 个栏目（dry-run 默认）

用户方法论（11b）：dump 全库→筛目标→枚举 config→匹配引擎（URL 精确 > 站名规范化 >
域名唯一 > 留空）→PATCH 后台+限速→抽查。宁缺毋滥。
用法: python3 backfill_dis_batch.py          # dry-run
      python3 backfill_dis_batch.py --apply  # 真写
"""
import json
import re
import subprocess
import sys
from concurrent.futures import ThreadPoolExecutor

DB = "22e399b8-3c5b-80f5-b6dc-c6d96cafa1bd"
TOK = "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"
APPLY = "--apply" in sys.argv

# 目标：URL(精确) → (Script_name, 配置编号)
TARGETS = [
    ("https://www.qdndz.gov.cn/zwgk/zdlygk/sthj/hjzfjg/", "crawl_danzhai_hjzf.py", 2106, "丹寨-环境执法监管"),
    ("https://www.yingquan.gov.cn/Content/showList/508/page_1.html", "crawl_yingquan_gggs.py", 2107, "颍泉-公告公示"),
    ("https://www.jcx.gov.cn/xwzx/tzgg.htm", "crawl_jcx_tzgg.py", 2108, "江城-通知公告"),
    ("http://www.binyang.gov.cn/gk/xxgkml/shgysyjslygk/hjbhly/jsxmhjyxpjsp/", "crawl_binyang_hjsp.py", 2109, "宾阳-项目环评审批"),
    ("https://www.dazu.gov.cn/qzfjz/glz_101897/zwgk_53321/fdzdgknr_53323/lzyj_100471/qzfjz/", "crawl_dazu_glz_qtgw.py", 2110, "大足古龙镇-其他公文"),
    ("https://www.ahqy.gov.cn/Jczwgk/opennessList/650/102002008/page_1.html", "crawl_ahqy_hjsp.py", 2111, "青阳-项目环评审批"),
    ("https://www.jinyun.gov.cn/col/col1229425888/index.html", "crawl_jinyun_hjgs.py", 2112, "缙云-行政许可（受理）公示"),
    ("https://www.jinyun.gov.cn/col/col1229856959/index.html", "crawl_jinyun_hjgs.py", 2113, "缙云-环评信息公示"),
]


def norm(u):
    u = (u or "").strip().lower()
    u = re.sub(r"^https?://", "", u)
    u = re.sub(r"^www\.", "", u)
    return u.rstrip("/")


def api(url, method="GET", body=None):
    cmd = ["curl", "-sS", "-X", method, "-H", "Authorization: Bearer " + TOK,
           "-H", "Notion-Version: 2022-06-28", "-H", "Content-Type: application/json",
           "--max-time", "60", url]
    if body is not None:
        cmd += ["-d", json.dumps(body, ensure_ascii=False)]
    p = subprocess.run(cmd, capture_output=True, timeout=90)
    try:
        return json.loads(p.stdout.decode("utf-8", "ignore"))
    except Exception:
        return {"_raw": p.stdout.decode("utf-8", "ignore")[:200]}


def getp(pr, k):
    v = pr.get(k) or {}
    t = v.get("rich_text") or v.get("title") or []
    return "".join(x.get("plain_text", "") for x in t).strip()


# ① dump 全库
print("=== dump Dis-db 全库 ===")
allrows, cur = [], None
while True:
    body = {"page_size": 100}
    if cur:
        body["start_cursor"] = cur
    r = api("https://api.notion.com/v1/databases/%s/query" % DB, "POST", body)
    allrows += r.get("results", [])
    if not r.get("has_more"):
        break
    cur = r.get("next_cursor")
print("  记录总数:", len(allrows))

# ② 匹配
print("\n=== 匹配（URL 精确）===")
plan = []
for tgt_url, script, num, label in TARGETS:
    hits = [pg for pg in allrows if norm((pg["properties"].get("Url") or {}).get("url")) == norm(tgt_url)]
    if not hits:
        # 退一步：同域名 + 同路径片段
        d = norm(tgt_url).split("/")[0]
        frag = "/".join(norm(tgt_url).split("/")[1:4])
        hits = [pg for pg in allrows
                if d == norm((pg["properties"].get("Url") or {}).get("url")).split("/")[0]
                and frag and frag in norm((pg["properties"].get("Url") or {}).get("url"))]
        tag = "（降级：域名+路径片段）"
    else:
        tag = "（URL 精确 ✅）"
    print("  %-28s → %d 条命中 %s" % (label, len(hits), tag))
    for pg in hits:
        pr = pg["properties"]
        cur_s, cur_n = getp(pr, "Script_name"), (pr.get("配置编号") or {}).get("number")
        state = "已填" if cur_s else "空"
        print("      [%s] %-22s %s | 现: %s / %s" % (state, getp(pr, "站点名")[:22],
                                                     ((pr.get("Url") or {}).get("url") or "")[:58], cur_s or "空", cur_n))
        plan.append((pg["id"], script, num, label, cur_s, cur_n))

print("\n=== 待写 %d 条 ===" % len([p for p in plan if not p[4]]))
for pid, script, num, label, cs, cn in plan:
    mark = "跳过(已有值)" if cs else "写入"
    print("  %-8s %-26s Script=%s 编号=%s" % (mark, label, script, num))

if not APPLY:
    print("\n(dry-run，加 --apply 才写)")
    raise SystemExit(0)

print("\n=== PATCH（限速 3 并发）===")


def do(p):
    pid, script, num, label, cs, cn = p
    if cs:
        return (label, "跳过")
    body = {"properties": {
        "Script_name": {"rich_text": [{"type": "text", "text": {"content": script}}]},
        "配置编号": {"number": num}}}
    r = api("https://api.notion.com/v1/pages/%s" % pid, "PATCH", body)
    return (label, "✅" if r.get("id") else "❌ " + str(r.get("code") or r.get("_raw"))[:60])


with ThreadPoolExecutor(max_workers=3) as ex:
    for label, res in ex.map(do, plan):
        print("  %-28s %s" % (label, res))

# ③ 抽查回读
print("\n=== 回读抽查 ===")
for tgt_url, script, num, label in TARGETS:
    r = api("https://api.notion.com/v1/databases/%s/query" % DB, "POST",
            {"filter": {"property": "Url", "url": {"contains": norm(tgt_url).split("/")[0]}}, "page_size": 50})
    for pg in r.get("results", []):
        if norm((pg["properties"].get("Url") or {}).get("url")) != norm(tgt_url):
            continue
        pr = pg["properties"]
        print("  %-28s Script_name=%-26s 编号=%s" % (
            label, getp(pr, "Script_name") or "❌空", (pr.get("配置编号") or {}).get("number")))
