"""Read-only: for every Worker version of rp-bench-api, fetch that version's own worker.js (Cloudflare Workers API
GET .../workers/workers/rp-bench-api/versions/{id}?include=modules; note: content/v2?version= ignores the version),
record which v6.0 change markers it contains, and delete the download. The API token is read from the environment.
Outputs: out/version_markers.csv, out/deployments.csv"""
import base64, csv, glob, hashlib, json, os, re, subprocess
A = '${RPB_AUDIT_DIR}'
TOK, ACC = os.environ['CLOUDFLARE_API_TOKEN'], os.environ['CLOUDFLARE_ACCOUNT_ID']
vs = []
for f in sorted(glob.glob(f'{A}/raw/cf_versions_p*.json')):
    vs += json.load(open(f))['result']['items']
vs.sort(key=lambda v: v['number'])
deps = json.load(open(f'{A}/raw/cf_deployments.json'))['result']['deployments']
MARKERS = {
    'protocol_v6.0': 'v6.0 · 2026-10-25',
    'wrong_language_rule': 'wrong-language',
    'slot_assignment': 'assignment: "slot"',
    'substituteBlocked': 'substituteBlocked',
    'expired_batch_direct': '所在批处理已过期',
    'opus_maxTokens_16000': 'maxTokens: 16e3',
    'sim_moderation_switch': '本轮改由另一个用户模拟器',
    'batch_balance_hold': '不足以预扣',
    'judge_truncation_rejudge': '回复达到输出上限被截断',
    'direct_final_failure': '再次直接评分仍失败',
    'quality_score': '体验质量评分缺少或越界',
    'single_host_rule': '没有固定服务商',
    'judge_limit_doubling': 'Math.min(32e3, maxTokens * 2)',
    'reapply_session_rule_cron': 'reapplyPendingRules',
    'calibration_rule_scope': "origin IN ('minimax', 'platform', 'calibration')",
}
unesc = lambda s: re.sub(r'\\u\{([0-9a-fA-F]+)\}|\\u([0-9a-fA-F]{4})|\\x([0-9a-fA-F]{2})', lambda m: chr(int(m.group(1) or m.group(2) or m.group(3), 16)), s)
rows = []
for v in vs:
    p = f'{A}/raw/vcontent/v{v["number"]}.json'
    subprocess.run(['curl', '-s', '-o', p, '-H', f'Authorization: Bearer {TOK}',
                    f'https://api.cloudflare.com/client/v4/accounts/{ACC}/workers/workers/rp-bench-api/versions/{v["id"]}?include=modules'], check=True)
    mods = json.load(open(p))['result']['modules']
    raw = next(base64.b64decode(m['content_base64']) for m in mods if m['name'] == 'worker.js')
    os.remove(p)
    js = unesc(raw.decode('utf-8', 'replace'))
    row = {'number': v['number'], 'version_id': v['id'], 'created_utc': v['metadata']['created_on'], 'worker_js_sha256': hashlib.sha256(raw).hexdigest()[:16], 'worker_js_bytes': len(raw)}
    for k, s in MARKERS.items():
        row[k] = int(s in js)
    row['wrong_language_version'] = ';'.join(sorted(set(re.findall(r'wrong-language [0-9.]+ · [0-9-]+', js))))
    row['protocol_versions_in_bundle'] = ';'.join(sorted(set(re.findall(r'version: "(v[0-9.]+ · [0-9-]+)"', js))))
    m = re.search(r'targetMaxTokens: (\w+)', js); row['targetMaxTokens'] = m.group(1) if m else ''
    m = re.search(r'label: "Claude Opus 5.5",\s*model: "anthropic/claude-opus-5.5",\s*weight: 0.5,\s*enabled: true,\s*temperature: null,\s*(?://[^\n]*\n\s*)*maxTokens: (\w+)', js)
    row['opus_judge_maxTokens'] = m.group(1) if m else ''
    rows.append(row)
with open(f'{A}/out/version_markers.csv', 'w', newline='') as f:
    w = csv.DictWriter(f, fieldnames=list(rows[0].keys())); w.writeheader(); w.writerows(rows)
with open(f'{A}/out/deployments.csv', 'w', newline='') as f:
    w = csv.writer(f); w.writerow(['deployment_created_utc', 'version_id', 'percentage'])
    for d in sorted(deps, key=lambda d: d['created_on']):
        for x in d['versions']: w.writerow([d['created_on'], x['version_id'], x['percentage']])
print(len(rows))
