#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""顺丰路由注册（EXP_RECE_REGISTER_ROUTE, type=2 按运单号）

用途：把已有的月结运单注册进顺丰推送体系；注册成功后，顺丰会把该运单
      **后续产生的** 路由节点推到 RoutePushService 回调。

用法：
  python3 脚本/sf_register_routes.py --dry-run          # 只统计，不调接口
  python3 脚本/sf_register_routes.py --limit 50         # 小批试跑
  python3 脚本/sf_register_routes.py                    # 全量（30/s 限流，约 45 秒/千单）

状态：数据/sf_route_registered.json  {运单号: {"at": "...", "code": "S0000"}}
     已注册成功的跳过，重复跑安全。
"""
import argparse
import json
import os
import sys
import time
import urllib.parse
import urllib.request
import uuid

ROOT = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
sys.path.insert(0, os.path.join(ROOT, '脚本'))
import shipment_pipeline as sp  # noqa: E402

SHIP = os.path.join(ROOT, '数据', 'shipment_status.json')
STATE = os.path.join(ROOT, '数据', 'sf_route_registered.json')
RATE = 25          # 次/秒，低于顺丰上限 30/s


def call_register(tok, waybill):
    msg = {'type': '2', 'attributeNo': waybill, 'language': 'zh-CN'}
    body = urllib.parse.urlencode({
        'partnerID': sp.SF_PARTNER_ID,
        'requestID': str(uuid.uuid4()).replace('-', ''),
        'serviceCode': 'EXP_RECE_REGISTER_ROUTE',
        'timestamp': str(int(time.time())),
        'accessToken': tok,
        'msgData': json.dumps(msg, ensure_ascii=False),
    }).encode()
    req = urllib.request.Request(sp.SF_API_URL, data=body,
                                 headers={'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'})
    r = json.loads(urllib.request.urlopen(req, timeout=30).read().decode())
    code = r.get('apiResultCode')
    detail = r.get('apiResultData')
    try:
        inner = json.loads(detail) if isinstance(detail, str) else (detail or {})
    except Exception:
        inner = {}
    return code, r.get('apiErrorMsg') or '', inner


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument('--dry-run', action='store_true')
    ap.add_argument('--limit', type=int, default=0)
    ap.add_argument('--status', default='', help='只注册指定状态，如 delivered')
    a = ap.parse_args()

    orders = json.load(open(SHIP, encoding='utf-8')).get('orders') or []
    state = json.load(open(STATE, encoding='utf-8')) if os.path.exists(STATE) else {}
    todo, seen = [], set()
    for o in orders:
        tn = (o.get('trackingNo') or '').strip()
        if not tn or tn in seen or tn in state:
            continue
        if a.status and (o.get('shipment') or {}).get('status') != a.status:
            continue
        seen.add(tn)
        todo.append(tn)
    if a.limit:
        todo = todo[:a.limit]

    print('待注册运单 %d ｜ 已注册 %d ｜ 目标状态 %s' % (len(todo), len(state), a.status or '全部'))
    if a.dry_run:
        for tn in todo[:5]:
            print('  样例', tn)
        return

    tok = sp.sf_get_token()
    ok = fail = 0
    for i, tn in enumerate(todo, 1):
        try:
            code, err, inner = call_register(tok, tn)
            sc = str(inner.get('errorCode') or '')
            if code == 'A1000' and (sc in ('S0000', '')):
                state[tn] = {'at': time.strftime('%Y-%m-%d %H:%M:%S'), 'code': 'S0000'}
                ok += 1
            else:
                state.setdefault(tn, {})
                state[tn].update({'at': time.strftime('%Y-%m-%d %H:%M:%S'),
                                  'code': code, 'err': err or sc})
                fail += 1
                if code == 'A1004':
                    print('❌ A1004 无对应服务权限 —— 路由注册接口还没关联，停止')
                    break
        except Exception as e:
            fail += 1
            print('  异常', tn, e)
        if i % 50 == 0:
            print('  进度 %d/%d  成功 %d 失败 %d' % (i, len(todo), ok, fail), flush=True)
            json.dump(state, open(STATE, 'w', encoding='utf-8'), ensure_ascii=False, indent=0)
        time.sleep(1.0 / RATE)

    json.dump(state, open(STATE, 'w', encoding='utf-8'), ensure_ascii=False, indent=0)
    print('完成：成功 %d ｜ 失败 %d ｜ 状态文件 %s' % (ok, fail, STATE))


if __name__ == '__main__':
    main()
