#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""2026 Q3 人员提炼（替换 deck ①章原来的 人员提炼_H1.json 半年度口径）
口径：达成率 = 3 季实绩 ÷ 2026 年度目标（用户 2026-10-15 拍板，同 H1 版算法仅换数据源）
数据源与分工：
  · 大客户部 2 人 + 销售拓展部 11 人 → 商机明细（2026目标 / 3季度实绩；原表为元 → 万元）
  · 生命科学销售部 8 人 → 四季度预测表（合计 / 3季度达成；原表为万元，该部门商机明细 0 行）
  · 逾期 → 逾期应收明细（按销售员）
  · 大客户部另有「个人业绩完成情况」原表字段（刘子研 Sheet2）
输出：数据/2026Q3人员提炼.json（schema 与 人员提炼_H1.json 对齐，deck 换源即用）
不估算：空值为 null / 空串；文本为原表摘录、不重写。
"""
import json
import os
from collections import OrderedDict

ROOT = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
SRC = os.path.join(ROOT, '数据', '2026Q3汇报_汇总.json')
OUT = os.path.join(ROOT, '数据', '2026Q3人员提炼.json')

DEPT = OrderedDict([
    ('刘子研', '大客户部'), ('赵云浩', '大客户部'),
    ('陈溪子', '销售拓展部'), ('黄文才', '销售拓展部'), ('林潇', '销售拓展部'),
    ('李江辉', '销售拓展部'), ('张文蝶', '销售拓展部'), ('徐卓凡', '销售拓展部'),
    ('宋学伟', '销售拓展部'), ('冯贤平', '销售拓展部'), ('刘虹晶', '销售拓展部'),
    ('杨天雄', '销售拓展部'), ('路要峰', '销售拓展部'),
    ('黄建华', '生命科学销售部'), ('王诗香', '生命科学销售部'), ('陈彩红', '生命科学销售部'),
    ('叶信福', '生命科学销售部'), ('侯海瑜', '生命科学销售部'), ('袁殿果', '生命科学销售部'),
    ('吴云', '生命科学销售部'), ('韩远怀', '生命科学销售部'),
])
LS8 = [n for n, dp in DEPT.items() if dp == '生命科学销售部']
REGION_WHO = {'广东区': '韩远怀', '浙江': '吴云'}    # 源文件按区域命名时的人员归属


def ls_who(r):
    t = str(r.get('汇报人') or r.get('来源文件') or '')
    for n in LS8:
        if n in t:
            return n
    for k, v in REGION_WHO.items():
        if k in t:
            return v
    return ''
PROD = ['建库试剂盒', '提取试剂', '反转录', 'qcpr', '高保真pcr', '普通pcr酶', '蛋白marker', '同源重组酶', '其他']


def num(v):
    if v is None:
        return None
    s = str(v).strip().replace(',', '').replace('%', '')
    if s in ('', '-', '/', 'nan', 'None', '—'):
        return None
    try:
        return float(s)
    except Exception:
        return None


def who_of(r):
    """人员归属：销售列优先，空则回落到 _汇报人（源文件名）contains 匹配。"""
    s = str(r.get('销售') or '').strip()
    if s and s != 'None':
        return s
    t = str(r.get('_汇报人') or '')
    for n in DEPT:
        if n in t:
            return n
    return ''


d = json.load(open(SRC, encoding='utf-8'))
T = d['tables']
OPP = T.get('商机明细') or []
OV = T.get('逾期应收明细') or []
LST = T.get('生命科学_月度目标') or []
LFC = T.get('生命科学_四季度预测') or []
BIGP = T.get('大客户部_个人业绩') or []
BIGA = T.get('大客户部_客户同比归因') or []

opp_by, ov_by = {}, {}
for r in OPP:
    opp_by.setdefault(who_of(r), []).append(r)
for r in OV:
    ov_by.setdefault(str(r.get('销售员') or '').strip(), []).append(r)

# 生命科学部：目标=四季度预测表「合计」，实绩=「3季度达成」（原表万元）
ls_tgt, ls_act, ls_q4, ls_prod = {}, {}, {}, {}
ls_m_tgt, ls_m_act = {}, {}
for r in LST:
    p = ls_who(r)
    if not p:
        continue
    ls_m_tgt[p] = round(ls_m_tgt.get(p, 0.0) + (num(r.get('销售目标')) or 0.0), 2)
    ls_m_act[p] = round(ls_m_act.get(p, 0.0) + (num(r.get('实际完成')) or 0.0), 2)
for r in LFC:
    p = ls_who(r)
    if not p:
        continue
    ls_tgt[p] = round(ls_tgt.get(p, 0.0) + (num(r.get('合计')) or 0.0), 2)
    ls_act[p] = round(ls_act.get(p, 0.0) + (num(r.get('3季度达成')) or 0.0), 2)
    ls_q4[p] = round(ls_q4.get(p, 0.0) + (num(r.get('4季度预测')) or 0.0), 2)
    for c in PROD:
        v = num(r.get(c))
        if v:
            m = ls_prod.setdefault(p, {})
            m[c] = round(m.get(c, 0.0) + v, 2)

people = []
for n, dept in DEPT.items():
    is_ls = dept == '生命科学销售部'
    o = opp_by.get(n, []) if not is_ls else []
    if is_ls:
        if ls_tgt.get(n) is not None:
            tgt, act = ls_tgt.get(n), ls_act.get(n)
            src_note = '生命科学_四季度预测（合计 / 3季度达成）'
        else:
            tgt, act = ls_m_tgt.get(n), ls_m_act.get(n)
            src_note = '生命科学_月度目标（销售目标 / 实际完成合计；该人未报送四季度预测）'
        # 单位归一：原表存在「元」与「万元」两种口径（如吴云/王诗香为元）。
        # 判定与折算规则公开：年度目标 > 10000 视为元，按 1万=10000元 折算（非估算）。
        if tgt and tgt > 10000:
            tgt, act = round(tgt / 10000.0, 2), (round(act / 10000.0, 2) if act is not None else None)
            src_note += '｜原表为元，已折算万元'
        else:
            src_note += '｜原表为万元'
    else:
        tgt = round(sum(num(r.get('2026目标')) or 0 for r in o) / 10000.0, 2) or None
        act = round(sum(num(r.get('3季度\n实绩')) or 0 for r in o) / 10000.0, 2)
        src_note = '商机明细（2026目标 / 3季度实绩，元→万元）'
    rate = round(act / tgt * 100, 1) if (tgt and act is not None) else None

    probs, sup, seen = [], [], set()
    for r in o:
        p = str(r.get('前三季度异常原因复盘') or '').strip()
        s = str(r.get('资源需求') or '').strip()
        c = str(r.get('客户名称') or '').strip()
        if p and p not in seen:
            seen.add(p)
            probs.append('%s：%s' % (c, p) if c else p)
        if s and s not in seen:
            seen.add(s)
            sup.append('%s：%s' % (c, s) if c else s)

    cs = {}
    for r in o:
        c = str(r.get('客户名称') or '').strip()
        if c:
            cs[c] = round(cs.get(c, 0.0) + (num(r.get('3季度\n实绩')) or 0.0) / 10000.0, 2)
    cust_txt = '、'.join('%s %.2f万' % (k, v) for k, v in sorted(cs.items(), key=lambda kv: -kv[1])[:12] if v) \
        or '（原表未列金额）'

    ovl = ov_by.get(n, [])
    ov_amt = round(sum(num(r.get('逾期金额')) or 0 for r in ovl), 2)
    coll = ('逾期 %d 笔 · %s 元（原表口径）' % (len(ovl), format(ov_amt, ',.2f'))) if ovl else ''

    rec = OrderedDict([
        ('name', n), ('group', dept), ('dept', dept),
        ('target', tgt), ('actual', act), ('rate', rate),
        ('opp_count', len(o)), ('overdue_n', len(ovl)), ('overdue_amt', ov_amt),
        ('source', src_note), ('collection', coll),
        ('problems', probs[:8]), ('support', sup[:8]),
        ('customers', cust_txt), ('monthly', {}), ('recon', None),
    ])
    if is_ls:
        rec['q4_plan'] = ls_q4.get(n, 0.0)
        rec['product_mix'] = ls_prod.get(n, {})
    # ── H1 同名字段（deck ①章换源即用，避免改动其下游算法）──
    rec['target_h1_wan'] = rec['target']
    rec['actual_h1_wan'] = rec['actual']
    rec['rate_pct'] = rec['rate']
    rec['dept_area'] = dept
    rec['support_needed'] = rec['support']
    rec['monthly_wan'] = None
    if is_ls and rec['target'] and rec['target'] < 10000:      # 仅万元口径者输出月度序列
        mm = OrderedDict()
        for r in LST:
            if ls_who(r) == n:
                mv = num(r.get('月份'))
                if mv and 1 <= mv <= 12:
                    mm[int(mv)] = round(mm.get(int(mv), 0.0) + (num(r.get('销售目标')) or 0.0), 2)
        if mm:
            rec['monthly_wan'] = str(dict(mm))
    for r in BIGP:
        if n in str(r.get('汇报人') or ''):
            rec['big_personal'] = {k: r.get(k) for k in (
                '年度目标（万元）', '月度实绩', '月度合计（万元）',
                '2026年剩余目标（万元）', '已完成（万元）', '完成率')}
    for r in BIGA:
        if n in str(r.get('汇报人') or ''):
            rec.setdefault('big_attr', []).append({k: r.get(k) for k in ('客户', '原因分析', '重点货号', '备注')})
    people.append(rec)

tot_t = round(sum(p['target'] or 0 for p in people), 2)
tot_a = round(sum(p['actual'] or 0 for p in people), 2)
res = OrderedDict([
    ('_meta', {
        'generated': d['meta']['生成'],
        'task': '2026 Q3 人员提炼（替换「半年度」人员提炼_H1.json）',
        'source': '数据/2026Q3汇报_汇总.json',
        'caliber': '达成率 = 3季实绩 ÷ 2026年度目标（同 H1 版算法，仅换数据源）；金额单位=万元；文本为原表摘录、未重写',
        'scope': 'Q3 实际报送 %d 人（大客户部 2 / 销售拓展部 11 / 生命科学销售部 8）' % len(people),
        'source_split': '两部门 13 人取商机明细（元→万元）；生命科学部 8 人取其四季度预测表（该部门商机明细 0 行）',
        'note': '未估算：空值为 null / 空串；未报送项不虚构。合计：目标 %s 万 / 3季实绩 %s 万' % (tot_t, tot_a),
    }),
    ('people', people),
])
json.dump(res, open(OUT, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)

print('→ %s' % OUT)
print('  人数 %d ｜ 合计 目标 %s 万 / 3季实绩 %s 万' % (len(people), tot_t, tot_a))
for p in people:
    print('   %-5s %-8s 目标 %9s ｜ 3季实绩 %9s ｜ 达成 %6s%% ｜ 商机 %2d 条 ｜ 逾期 %2d 笔' % (
        p['name'], p['group'], p['target'], p['actual'], p['rate'], p['opp_count'], p['overdue_n']))
