#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""验证「部门 → 销售 → 客户」三级下钻管道（注入测试活动，跑完清理）。

只写本地 数据/activities/（/api/visit/stats 读的就是本地索引），不碰云端。
"""
import io
import json
import os
import ssl
import sys
import urllib.request

ROOT = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
ACT = os.path.join(ROOT, '数据', 'activities')
IDX = os.path.join(ACT, '_index.json')
BASE = 'http://127.0.0.1:8765'
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

sess = json.load(open(ROOT + '/数据/auth_sessions.json', encoding='utf-8'))
mine = sorted([(v['expire_at'], k, v) for k, v in sess.items() if v.get('name') == '刘新元'], reverse=True, key=lambda x: x[0])
TOKEN = mine[0][1]

# 从用户表挑样本：大客户部 / 生命科学-浙江 / 生命科学-广东 / 不在表里
roles = json.load(io.open(ROOT + '/数据/user_roles.json', encoding='utf-8'))
by_dept = {}
for u in roles:
    by_dept.setdefault((u.get('dept') or '').strip(), []).append(u.get('name'))
pick = lambda d, i=0: (by_dept.get(d) or ['—'])[i]
P_BIG = pick('大客户部')
P_ZJ = pick('生命科学-浙江')
P_GD = pick('生命科学-广东')
print('样本：大客户部=%s ｜ 生命科学-浙江=%s ｜ 生命科学-广东=%s' % (P_BIG, P_ZJ, P_GD))

import time
M = time.strftime('%Y-%m')
IDS = ['ACTTESTDP1', 'ACTTESTDP2', 'ACTTESTDP3', 'ACTTESTDP4']
TEST = [
    (IDS[0], P_BIG, '测试客户甲', 'offline', M + '-05'),
    (IDS[1], P_BIG, '测试客户乙', 'online', M + '-06'),
    (IDS[2], P_ZJ, '测试客户丙', 'offline', M + '-07'),
    (IDS[3], '不存在的人', '测试客户丁', 'online', M + '-08'),
]

ok = True
def chk(label, cond, detail=''):
    global ok
    print('  %s %-42s %s' % ('✅' if cond else '❌', label, detail))
    if not cond:
        ok = False


def call(path):
    req = urllib.request.Request(BASE + path)
    req.add_header('Authorization', 'Bearer ' + TOKEN)
    with urllib.request.urlopen(req, timeout=60) as r:
        return json.load(r)


n0 = None
try:
    idx0 = json.load(io.open(IDX, encoding='utf-8'))
    n0 = len(idx0)
    idx = list(idx0)
    for aid, owner, cust, ch, date in TEST:
        rec = {'id': aid, 'created': date + ' 10:00:00', 'submitted_by': owner, 'sales': owner,
               'type': '项目跟进', 'subtype': '', 'customer': cust, 'contact': '', 'date': date,
               'channel': ch, 'order_refs': [], 'order_ref_nos': [], 'opportunities': [],
               'opp_names': [], 'opp_count': 0, 'blocks': [{'opp': '', 'need': 'x', 'plan': []}],
               'block_count': 1, 'order_no': '', 'sku_count': 0, 'att_count': 0,
               'feishu_text': '（测试）'}
        io.open(os.path.join(ACT, aid + '.json'), 'w', encoding='utf-8').write(
            json.dumps(rec, ensure_ascii=False, indent=1))
        idx.append({k: rec.get(k) for k in ('id', 'created', 'submitted_by', 'sales', 'type', 'subtype',
                                            'customer', 'contact', 'date', 'channel', 'order_no',
                                            'sku_count', 'att_count', 'opp_count', 'block_count')})
    io.open(IDX, 'w', encoding='utf-8').write(json.dumps(idx, ensure_ascii=False, indent=1))
    print('已注入 %d 条测试活动（%d → %d）\n' % (len(TEST), n0, len(idx)))

    j = call('/api/visit/stats?month=%s&scope=team' % M)
    chk('接口 ok', j.get('ok'), str(j.get('error', ''))[:60])
    deps = {d['dept']: d for d in (j.get('departments') or [])}
    chk('返回 departments 一级', len(deps) >= 2, '部门数 %d：%s' % (len(deps), list(deps)))

    big = deps.get('大客户部')
    chk('大客户部聚合正确', bool(big) and big['offline'] == 1 and big['online'] == 1 and big['total'] == 2,
        json.dumps({k: big.get(k) for k in ('offline', 'online', 'total', 'sales_count')}, ensure_ascii=False) if big else '缺')
    if big:
        s = [x for x in big['sales'] if x['name'] == P_BIG]
        chk('二级：该部门下按销售拆', len(s) == 1, '%d 个销售' % len(big['sales']))
        if s:
            chk('三级：该销售下按客户拆', s[0]['customer_count'] == 2 and len(s[0]['customers']) == 2,
                str([c['customer'] for c in s[0]['customers']]))
            chk('客户内带活动明细（可下钻）',
                all(c.get('acts') for c in s[0]['customers']), '')

    lsk = deps.get('生命科学销售部')
    chk('生命科学-浙江 已归一到「生命科学销售部」', bool(lsk) and any(
        x['name'] == P_ZJ for x in (lsk['sales'] if lsk else [])),
        ('部门=%s 销售=%s' % ('生命科学销售部', [x['name'] for x in lsk['sales']]) if lsk else '缺'))
    if lsk:
        zj = [x for x in lsk['sales'] if x['name'] == P_ZJ][0]
        chk('带出区域（浙江）', zj.get('region') == '浙江', zj.get('region'))
    unk = deps.get('未匹配部门')
    chk('不在用户表 → 未匹配部门', bool(unk) and any(x['name'] == '不存在的人' for x in unk['sales']),
        str([x['name'] for x in unk['sales']]) if unk else '缺')

    chk('扁平 customers 仍在（按客户维度）', len(j.get('customers') or []) >= 4, '%d 家' % len(j.get('customers') or []))
    chk('KPI 与分部门之和一致',
        j.get('offline_count') == sum(d['offline'] for d in deps.values()) and
        j.get('online_count') == sum(d['online'] for d in deps.values()),
        'KPI 线下 %s / 线上 %s ｜ 分部门合计 %s / %s'
        % (j.get('offline_count'), j.get('online_count'),
           sum(d['offline'] for d in deps.values()), sum(d['online'] for d in deps.values())))

    # 销售身份视角（scope=me 由服务端兜底）
    j2 = call('/api/visit/stats?month=%s&scope=me' % M)
    chk('scope=me 只算本人（刘新元=0）', j2.get('total') == 0 or all(
        d['dept'] != '大客户部' for d in (j2.get('departments') or [])),
        'total=%s depts=%s' % (j2.get('total'), [d['dept'] for d in (j2.get('departments') or [])]))
finally:
    print('\n=== 清理 ===')
    idx = json.load(io.open(IDX, encoding='utf-8'))
    idx = [x for x in idx if x.get('id') not in IDS]
    io.open(IDX, 'w', encoding='utf-8').write(json.dumps(idx, ensure_ascii=False, indent=1))
    for aid in IDS:
        p = os.path.join(ACT, aid + '.json')
        if os.path.exists(p):
            os.remove(p)
    left = [f for f in os.listdir(ACT) if f.startswith('ACTTEST')]
    print('  索引 %d → %d ｜ 残留测试文件 %d → %s' % (n0, len(idx), len(left), '✅ 干净' if not left else '❌'))
    ok = ok and not left and len(idx) == n0

print('\n%s' % ('✅ 三级下钻管道验证通过' if ok else '❌ 有失败项'))
sys.exit(0 if ok else 1)
