#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""KPI 当月刷新：MQL 拉本月销售订单 → 查销售员角色 → 按销售员汇总 → 写 kpi_progress.json → 部署 CloudBase"""
import subprocess, json, time, re, os, sys
from datetime import datetime, timezone

MCP_URL = 'https://project.feishu.cn/mcp_server/v1'
PK = '6593cd71471290e3cc6be6e6'
BASE = os.path.expanduser('~/Desktop/Hermes输出-工作类')
CACHE_PATH = os.path.join(BASE, '数据', 'order_role_cache.json')
KPI_PATH = os.path.join(BASE, '数据', 'kpi_progress.json')

# 1) 从 config.yaml 读 token
token = None
with open(os.path.expanduser('~/.hermes/config.yaml')) as f:
    for line in f:
        m = re.search(r'X-Mcp-Token:\s*(\S+)', line)
        if m:
            token = m.group(1).strip()
            break
if not token:
    print('FATAL: token not found in config.yaml'); sys.exit(2)
MCP_TOKEN = token

def call_mcp(method, args, max_retries=3):
    payload = {'jsonrpc': '2.0', 'method': 'tools/call',
               'params': {'name': method, 'arguments': args}, 'id': 1}
    for attempt in range(max_retries + 1):
        try:
            cmd = ['curl', '-s', '-X', 'POST', MCP_URL,
                   '-H', 'Content-Type: application/json',
                   '-H', 'X-Mcp-Token: ' + MCP_TOKEN,
                   '-d', json.dumps(payload), '--max-time', '30']
            r = subprocess.run(cmd, capture_output=True, text=True, timeout=35)
            resp = json.loads(r.stdout)
        except Exception:
            if attempt < max_retries:
                time.sleep(2); continue
            return None
        if 'result' in resp and 'content' in resp['result']:
            for c in resp['result']['content']:
                if c.get('type') == 'text':
                    t = c.get('text', '')
                    # 先直接解析，失败则剥离 log_id 尾部再试
                    try:
                        return json.loads(t)
                    except Exception:
                        t2 = re.sub(r'\nlog_id:.*$', '', t).strip()
                        try:
                            return json.loads(t2)
                        except Exception:
                            pass
        if attempt < max_retries:
            time.sleep(1)
    return None

def unwrap(result):
    """处理双层包裹 {"result": "<内层JSON字符串>"}"""
    if isinstance(result, dict) and isinstance(result.get('result'), str):
        try:
            inner = json.loads(re.sub(r'\nlog_id:.*$', '', result['result']).strip())
            if isinstance(inner, dict):
                return inner
        except Exception:
            pass
    return result

def parse_item(item):
    fields = {}
    for f in item.get('moql_field_list') or []:
        k = f.get('key'); v = f.get('value')
        if v is None:
            fields[k] = ''
        elif isinstance(v, list):
            if len(v) == 0:
                fields[k] = ''
            elif isinstance(v[0], dict):
                v0 = v[0]
                if 'label' in v0: fields[k] = v0['label']
                elif 'string_value' in v0: fields[k] = v0['string_value']
                elif 'double_value' in v0: fields[k] = v0['double_value']
                elif 'long_value' in v0: fields[k] = v0['long_value']
                else: fields[k] = str(v0)
            else:
                fields[k] = v[0]
        elif isinstance(v, dict):
            if 'string_value' in v: fields[k] = v['string_value']
            elif 'double_value' in v: fields[k] = v['double_value']
            elif 'long_value' in v: fields[k] = v['long_value']
            elif 'key_label_value' in v:
                klv = v['key_label_value']
                fields[k] = klv.get('label', '') if isinstance(klv, dict) else klv
            else: fields[k] = str(v)
        else:
            fields[k] = v
    return fields

month = datetime.now().strftime('%Y-%m')
month_start = month + '-01'
print(f'=== KPI 当月刷新 === 目标月份: {month} (创建时间 >= {month_start})')

# 2) OFFSET 分页拉取全月销售订单
mql_base = (f"SELECT `field_51d592`, `work_item_id`, `name` "
            f"FROM `销售管理`.`销售订单` WHERE `创建时间` >= '{month_start}'")

all_items = []
seen_wids = set()
total = 0
empty_streak = 0
max_pages = 400

for page in range(max_pages):
    offset = page * 50
    mql = f"{mql_base} LIMIT 50 OFFSET {offset}"
    result = call_mcp('search_by_mql', {'project_key': PK, 'mql': mql})
    if not result:
        time.sleep(1)
        result = call_mcp('search_by_mql', {'project_key': PK, 'mql': mql})
        if not result:
            empty_streak += 1
            if empty_streak >= 3:
                print(f'  [warn] 连续 {empty_streak} 页返回 None，停止 (offset {offset})')
                break
            offset = 0 if False else offset  # no-op
            continue
    lst = result.get('list') or []
    if lst:
        total = lst[0].get('count', total) or total
    data = result.get('data') or {}
    page_items = [parse_item(it) for _, items in data.items() for it in (items or [])]
    if not page_items:
        empty_streak += 1
        if empty_streak >= 2:
            break
        continue
    empty_streak = 0
    for it in page_items:
        wid = str(it.get('work_item_id', '')).strip()
        if wid and wid not in ('', '0', 'None'):
            if wid in seen_wids:
                continue
            seen_wids.add(wid)
        all_items.append(it)
    if (page + 1) % 5 == 0:
        print(f'  ... 已拉 {len(all_items)} 行 (第 {page+1} 页)')
    time.sleep(0.05)

print(f'[MQL] 总行数: {len(all_items)}，MCP 报告总数: {total}')

if not all_items:
    print('FATAL: 未拉到任何订单行，请检查 MQL / token'); sys.exit(3)

# 3) 单据编号 → work_item_id 映射（每唯一编号取一行 ID，供角色查询）
doc_to_wid = {}
for it in all_items:
    name = it.get('name', '')
    if name and name not in doc_to_wid and it.get('work_item_id'):
        doc_to_wid[name] = str(it['work_item_id'])
unique_docs = len(doc_to_wid)
print(f'[映射] 唯一单据编号: {unique_docs}')

# 4) 查缓存，未命中逐条 get_workitem_brief 查销售员
cache = {}
if os.path.exists(CACHE_PATH):
    with open(CACHE_PATH) as f:
        try:
            cache = json.load(f)
        except Exception:
            cache = {}
uncached = [d for d in doc_to_wid if d not in cache]
print(f'[缓存] 已缓存 {len(cache)}，未命中 {len(uncached)} 条需查询销售员')

for i, doc in enumerate(uncached):
    wid = doc_to_wid[doc]
    result = call_mcp('get_workitem_brief', {
        'project_key': PK, 'work_item_id': wid, 'fields': ['role_7dd6e0']
    })
    sales = '未知'
    if result:
        attr = unwrap(result).get('work_item_attribute', unwrap(result))
        for role in (attr.get('role_members') or []):
            if role.get('key') == 'role_7dd6e0' or role.get('name') == '销售员':
                members = role.get('members') or []
                if members:
                    sales = members[0].get('name', '未知')
                break
    cache[doc] = {'name_cn': sales, 'email': ''}
    if (i + 1) % 20 == 0:
        print(f'  ... 已查询 {i+1}/{len(uncached)}')
    time.sleep(0.08)

with open(CACHE_PATH, 'w') as f:
    json.dump(cache, f, ensure_ascii=False)

# 5) 所有行累加（不按单据编号去重），按销售员汇总
doc_to_sales = {}
for doc, info in cache.items():
    doc_to_sales[doc] = (info.get('name_cn') if isinstance(info, dict) else info) or '未知'

by_sales = {}
total_sum = 0.0
for it in all_items:
    doc = it.get('name', '')
    amt = it.get('field_51d592', 0)
    if amt in ('', None):
        amt = 0.0
    else:
        try:
            amt = float(amt)
        except Exception:
            amt = 0.0
    sales = doc_to_sales.get(doc, '未知')
    by_sales[sales] = by_sales.get(sales, 0.0) + amt
    total_sum += amt

total_sum = round(total_sum, 2)
by_sales = {k: round(v, 2) for k, v in by_sales.items()}
sorted_sales = dict(sorted(by_sales.items(), key=lambda x: x[1], reverse=True))

# 校验
check = round(sum(by_sales.values()), 2)
assert abs(check - total_sum) < 0.05, f"汇总校验失败: {check} vs {total_sum}"

kpi = {
    'updated_at': datetime.now(timezone.utc).isoformat(),
    'month': month,
    'total': total_sum,
    'by_salesperson': sorted_sales
}
with open(KPI_PATH, 'w') as f:
    json.dump(kpi, f, ensure_ascii=False, indent=2)
print(f'[写入] {KPI_PATH}')

# 6) 部署
deploy_cmd = (f"tcb hosting deploy {KPI_PATH} 数据/kpi_progress.json "
              f"-e bier-sales-d0gatbvlx288724e9")
print(f'[部署] {deploy_cmd}')
dr = subprocess.run(deploy_cmd, shell=True, capture_output=True, text=True, timeout=120)
deploy_out = (dr.stdout or '').strip() + '\n' + (dr.stderr or '').strip()
print(deploy_out)
deploy_ok = (dr.returncode == 0)

# 7) 输出 TOP10 + 合计 + 部署结果
print('\n================= 结果 =================')
print(f'月份: {month} | 订单行数: {len(all_items)} | 唯一单据: {unique_docs} | 销售员数: {len(sorted_sales)}')
print(f'本月合计: ¥{total_sum:,.2f}')
print(f'TOP10:')
for rank, (name, amt) in enumerate(list(sorted_sales.items())[:10], 1):
    print(f'  {rank:>2}. {name:<10} ¥{amt:>12,.2f}')
print(f'部署结果: {"✓ 成功" if deploy_ok else "✗ 失败"}')
