#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""拉取飞书「销售订单」的 单据编号 → 关联一级课题组 映射（促销期）。

用途：ERP 只有机构客户名、没有课题组；飞书销售订单有 `field_2e119c 关联一级课题组`。
      按铁律「金额用 ERP、归属用飞书」，两边按单据编号连接。
输出：数据/order_ketizu_map.json
"""
import io
import json
import math
import os
import re
import subprocess
import sys
import time
from collections import Counter

ROOT = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
OUT = os.path.join(ROOT, '数据', 'order_ketizu_map.json')
PIPELINE = os.path.join(ROOT, '数据', 'mcp_activities_pipeline.py')
PK = '6593cd71471290e3cc6be6e6'
A, B = '2026-08-20', '2026-09-30'
# 允许命令行覆盖区间
if len(sys.argv) >= 3:
    A, B = sys.argv[1], sys.argv[2]


def token():
    src = io.open(PIPELINE, encoding='utf-8').read()
    m = re.search(r'TK\s*=\s*"([^"]+)"', src)
    if not m:
        raise SystemExit('❌ 取不到 MCP token')
    return m.group(1)


TK = token()


def mcp(method, args, timeout=90):
    r = subprocess.run(
        ['curl', '-s', '--resolve', 'project.feishu.cn:443:120.233.177.47', '-X', 'POST',
         'https://project.feishu.cn/mcp_server/v1',
         '-H', 'X-Mcp-Token: %s' % TK, '-H', 'Content-Type: application/json',
         '-d', json.dumps({"jsonrpc": "2.0", "method": "tools/call",
                           "params": {"name": method, "arguments": args}, "id": 1})],
        capture_output=True, text=True, timeout=timeout)
    try:
        d = json.loads(r.stdout)
    except Exception:
        return None
    for c in (d.get('result') or {}).get('content') or []:
        t = c.get('text', '')
        if t.strip().startswith('{'):
            try:
                return json.loads(t)
            except Exception:
                continue
    return None


def label(f):
    """按 value_type 取标量——单据编号是 string_value，课题组是 key_label_value，不能只认一种。"""
    vt = f.get('value_type')
    v = f.get('value') or {}
    if vt == 'key_label_value':
        return ((v.get('key_label_value') or {}).get('label') or '').strip()
    if vt == 'user_value':
        u = v.get('user_value') or {}
        return (u.get('name_cn') or u.get('name_en') or '').strip()
    if vt == 'long_value':
        return str(v.get('long_value', '')).strip()
    if vt == 'string_value':
        return (v.get('string_value') or '').strip()
    if vt == 'double_value':
        return str(v.get('double_value', '')).strip()
    for k in v:                                   # 兜底：取第一个非空标量
        x = v[k]
        if isinstance(x, dict):
            for kk in ('label', 'name_cn', 'string_value', 'long_value', 'double_value'):
                if x.get(kk) not in (None, ''):
                    return str(x[kk]).strip()
        elif x not in (None, ''):
            return str(x).strip()
    return ''


def rows_of(res):
    out = []
    data = (res or {}).get('data')
    groups = list(data.values()) if isinstance(data, dict) else (data if isinstance(data, list) else [])
    for g in groups:
        if isinstance(g, dict):
            g = g.get('moql_field_list') or []
        if not isinstance(g, list):
            continue
        for item in g:
            fld = {}
            for f in item.get('moql_field_list') or []:
                fld[f.get('name') or f.get('key')] = label(f)
            if fld:
                out.append(fld)
    return out


MQL = ("SELECT `单据编号`, `关联一级课题组`, `合同日期`, `价税合计`, `是否赠品`, `单据类型` "
       "FROM `销售管理`.`销售订单` WHERE `合同日期` between '%s' and '%s'" % (A, B))

print('飞书销售订单 → 课题组映射 ｜ 区间 %s ~ %s' % (A, B))
res = mcp('search_by_mql', {'project_key': PK, 'mql': MQL})
if not res:
    raise SystemExit('❌ 首页无返回')
rows = rows_of(res)
sid = res.get('session_id')
lst = (res.get('list') or [{}])[0]
count = lst.get('count') or len(rows)
gid = ((lst.get('group_infos') or [{}])[0].get('group_id')) or '1'
pages = max(1, int(math.ceil(count / 50.0)))
print('  总 %d 条 / %d 页' % (count, pages))
for p in range(2, pages + 1):
    r2 = mcp('search_by_mql', {'project_key': PK, 'session_id': sid,
                               'group_pagination_list': [{'group_id': gid, 'page_num': p}]})
    got = rows_of(r2) if r2 else []
    if not got:
        print('  第 %d 页空，收尾' % p)
        break
    rows.extend(got)
    if p % 10 == 0 or p == pages:
        print('  第 %d/%d 页 ｜ 累计 %d' % (p, pages, len(rows)))
    time.sleep(0.15)

# 归并：单据编号 → 课题组（同一单号多行，取非空）
m = {}
for r in rows:
    no = (r.get('单据编号') or '').strip()
    kt = (r.get('关联一级课题组') or '').strip()
    if not no:
        continue
    if kt:
        m[no] = kt
    else:
        m.setdefault(no, '')

have = {k: v for k, v in m.items() if v}
print('\n结果')
print('  单据 %d 个 ｜ 有课题组 %d 个（%.1f%%）｜ 无课题组 %d 个'
      % (len(m), len(have), len(have) * 100.0 / max(len(m), 1), len(m) - len(have)))
print('  课题组数 %d' % len(set(have.values())))
print('  课题组 top10:', dict(Counter(have.values()).most_common(10)))

payload = {'generated': time.strftime('%Y-%m-%d %H:%M:%S'), 'range': [A, B],
           'src': '飞书 销售管理.销售订单 field_2e119c 关联一级课题组',
           'orders': len(m), 'with_ketizu': len(have),
           'coverage': round(len(have) * 100.0 / max(len(m), 1), 1),
           'map': m}
io.open(OUT, 'w', encoding='utf-8').write(json.dumps(payload, ensure_ascii=False))
print('  → %s（%.0f KB）' % (OUT, os.path.getsize(OUT) / 1024.0))
