#!/usr/bin/env python3
"""北京展览飞书同步 v2（raw API 版）

用法: python3 feishu_sync_v2.py [YYYY-MM-DD]

流程:
1. 读取 feishu_config.json 中 doc_token
2. 读取当日数据 md 文件
3. 清空文档根级子块 -> 分批插入新块 -> 更新标题块
4. 回读验证: block_count >= 10 且包含当日日期标记

说明: feishu_doc 工具 action=write 对含 bullet 块的文档存在 clear 路径 400 bug，
本脚本直接调用飞书官方 API 绕过（credential 来自 openclaw.json travel-bot 账号）。
"""
import json
import re
import sys
import time
import urllib.error
import urllib.request
from pathlib import Path

BASE = Path(__file__).parent
CFG_PATH = BASE / "feishu_config.json"
OPENCLAW_CFG = Path("/root/.openclaw/openclaw.json")

date_arg = sys.argv[1] if len(sys.argv) > 1 else time.strftime("%Y-%m-%d")
md_path = BASE / "data" / f"beijing_exhibitions_{date_arg}.md"

if not md_path.exists():
    print(f"NO_DATA_FILE: {md_path}")
    sys.exit(2)

doc_token = json.loads(CFG_PATH.read_text())["doc_token"]
tb = json.loads(OPENCLAW_CFG.read_text())["channels"]["feishu"]["accounts"]["travel-bot"]

req = urllib.request.Request(
    "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
    data=json.dumps({"app_id": tb["appId"], "app_secret": tb["appSecret"]}).encode(),
    headers={"Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=15) as r:
    t = json.load(r)["tenant_access_token"]
H = {"Authorization": f"Bearer {t}", "Content-Type": "application/json"}


def api(url, data=None, method=None):
    req = urllib.request.Request(url, data=json.dumps(data).encode() if data else None, headers=H, method=method)
    with urllib.request.urlopen(req, timeout=20) as r:
        return json.load(r)


base = f"https://open.feishu.cn/open-apis/docx/v1/documents/{doc_token}"
blocks_url = f"{base}/blocks/{doc_token}/children"

# 1. clear existing children
items = api(f"{base}/blocks?page_size=500")["data"]["items"]
n = len([b for b in items if b["parent_id"] == doc_token and b["block_type"] != 1])
if n:
    res = api(f"{blocks_url}/batch_delete", {"start_index": 0, "end_index": n}, "DELETE")
    assert res.get("code") == 0, f"clear failed: {res}"
print(f"cleared: {n}")


# 2. md -> blocks
def els(s):
    return [{"text_run": {"content": s}}]


def rich(s):
    out, pos = [], 0
    for m in re.finditer(r"\*\*([^*]+)\*\*", s):
        if m.start() > pos:
            out.append({"text_run": {"content": s[pos:m.start()]}})
        out.append({"text_run": {"content": m.group(1), "text_element_style": {"bold": True}}})
        pos = m.end()
    if pos < len(s):
        out.append({"text_run": {"content": s[pos:]}})
    return out or els(s)


blocks = []
for raw in md_path.read_text().split("\n"):
    line = raw.rstrip()
    if not line.strip():
        continue
    if line.startswith("# "):
        blocks.append({"block_type": 3, "heading1": {"elements": els(line[2:])}})
    elif line.startswith("## "):
        blocks.append({"block_type": 4, "heading2": {"elements": els(line[3:])}})
    elif line.startswith("### "):
        blocks.append({"block_type": 5, "heading3": {"elements": els(line[4:])}})
    elif line.strip() == "---":
        blocks.append({"block_type": 22, "divider": {}})
    elif line.startswith("- "):
        blocks.append({"block_type": 12, "bullet": {"elements": rich(line[2:])}})
    elif line.startswith("|"):
        blocks.append({"block_type": 12, "bullet": {"elements": els(" ｜ ".join(c.strip() for c in line.strip("|").split("|")))}})
    else:
        blocks.append({"block_type": 2, "text": {"elements": rich(line)}})

for ci in range(0, len(blocks), 40):
    res = api(blocks_url, {"children": blocks[ci:ci+40], "index": ci}, "POST")
    assert res.get("code") == 0, f"insert failed @ {ci}: {res}"
    time.sleep(0.4)
print(f"inserted: {len(blocks)}")

# 3. title
res = api(
    f"{base}/blocks/{doc_token}",
    {"update_text_elements": {"elements": [{"text_run": {"content": f"北京展览推荐 {date_arg}"}}]}},
    "PATCH",
)
assert res.get("code") == 0, f"title failed: {res}"

# 4. verify
items = api(f"{base}/blocks?page_size=500")["data"]["items"]
texts = []
for b in items:
    for key in ("text", "heading1", "heading2", "heading3", "bullet", "page"):
        blk = b.get(key)
        if isinstance(blk, dict) and "elements" in blk:
            for e in blk["elements"]:
                tr = e.get("text_run", {}).get("content", "")
                if tr:
                    texts.append(tr)
j = "\n".join(texts)
ok_count = len(items) >= 10
ok_date = date_arg in j
print(f"FINAL block_count={len(items)} date_ok={ok_date} count_ok={ok_count}")
if not (ok_count and ok_date):
    sys.exit(1)
