#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""客户档案聚合 → 作战地图数据源

读 「客户档案完善」活动记录（数据/activities/*.json），按客户聚合，
与 customer_360 的 ERP 采购数据交叉，算出渗透率 / 竞对盘子 / 完善度，
输出 数据/customer_dossier.json 供客户360 页面渲染。

口径说明：
  · 金额一律换算为「万元」输出（customer_360 与 ERP 源为「元」）
  · 竞对盘子 gap_wan = 录入的 TOP3 供应商份额金额合计（我司可争取上限）
  · 渗透率 penetration = 我司累计采购额 / 客户最新年营收
  · 完善度 score：5 维度各 20 分（决策链 / 主营业务 / 营收 / 供应商 / 我司份额）
"""
import glob
import json
import os
from datetime import datetime

WS = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ACT_DIR = os.path.join(WS, '数据', 'activities')
C360 = os.path.join(WS, '数据', 'customer_360.json')
OUT = os.path.join(WS, '数据', 'customer_dossier.json')

DIM_LABEL = {'decision': '决策链', 'biz_desc': '主营业务', 'revenues': '近三年营收',
             'suppliers': 'TOP3供应商', 'self_share': '我司份额'}


def fnum(v):
    """宽容数值解析：'3,200万' / '3200' / '约3200' → 3200.0"""
    if v is None:
        return None
    s = str(v).replace(',', '').replace('，', '').strip()
    digits = ''
    for ch in s:
        if ch.isdigit() or ch == '.':
            digits += ch
        elif digits:
            break
    try:
        return float(digits) if digits else None
    except ValueError:
        return None


def collect():
    """扫描活动记录，取「客户档案完善」按客户聚合（后者覆盖前者=最新优先）"""
    files = sorted(glob.glob(os.path.join(ACT_DIR, 'ACT*.json')))
    agg = {}
    for fp in files:
        try:
            r = json.load(open(fp, encoding='utf-8'))
        except Exception:
            continue
        if r.get('type') != '客户档案完善':
            continue
        cust = (r.get('customer') or '').strip()
        if not cust:
            continue
        b0 = (r.get('blocks') or [{}])[0]
        date = (r.get('date') or r.get('created_at') or '')[:10]
        cur = agg.get(cust)
        if cur and date and cur['last_date'] and date < cur['last_date']:
            continue                      # 已有更新的记录
        # 合并：新记录为主，空字段回落到旧记录（避免分次录入互相覆盖）
        def pick(key, default):
            v = b0.get(key)
            if isinstance(v, list):
                return v if v else (cur or {}).get('_raw', {}).get(key) or default
            v = (v or '').strip() if isinstance(v, str) else v
            return v or (cur or {}).get('_raw', {}).get(key) or default
        raw = {k: pick(k, [] if k in ('decision', 'revenues', 'suppliers') else '')
               for k in ('decision', 'biz_desc', 'revenues', 'suppliers', 'self_share', 'main_rival')}
        agg[cust] = {
            'name': cust,
            'last_date': date or (cur or {}).get('last_date', ''),
            'record_count': ((cur or {}).get('record_count', 0) + 1),
            'sales': (r.get('sales') or (cur or {}).get('sales') or ''),
            '_raw': raw,
        }
    return agg


def score(raw):
    """完善度 5×20=100"""
    s, missing = 0, []
    dec = [x for x in (raw.get('decision') or []) if x.get('name') or x.get('title')]
    if dec:
        s += 20
    else:
        missing.append(DIM_LABEL['decision'])
    if raw.get('biz_desc'):
        s += 20
    else:
        missing.append(DIM_LABEL['biz_desc'])
    rev = [x for x in (raw.get('revenues') or []) if x.get('year') or x.get('amt')]
    if len(rev) >= 3:
        s += 20
    elif rev:
        s += 10
        missing.append('近三年营收（缺 %d 年）' % (3 - len(rev)))
    else:
        missing.append(DIM_LABEL['revenues'])
    sup = [x for x in (raw.get('suppliers') or []) if x.get('name') or x.get('project') or x.get('amt')]
    if len(sup) >= 3:
        s += 20
    elif sup:
        s += 10
        missing.append('TOP3供应商（缺 %d 家）' % (3 - len(sup)))
    else:
        missing.append(DIM_LABEL['suppliers'])
    if raw.get('self_share') or raw.get('main_rival'):
        s += 20
    else:
        missing.append(DIM_LABEL['self_share'])
    return s, missing


def main():
    c360 = json.load(open(C360, encoding='utf-8'))
    by_name = {}
    for c in (c360.get('customers') or []):
        by_name[c.get('name')] = c

    agg = collect()
    out = {}
    for cust, rec in agg.items():
        raw = rec.pop('_raw')
        sc, missing = score(raw)
        # —— 竞争格局 ——
        sup = [x for x in (raw.get('suppliers') or []) if (fnum(x.get('amt')) or fnum(x.get('pct')))]
        top_amts = [fnum(x.get('amt')) for x in sup if fnum(x.get('amt')) is not None]
        top_pcts = [fnum(x.get('pct')) for x in sup if fnum(x.get('pct')) is not None]
        gap_wan = round(sum(top_amts), 1) if top_amts else None
        top3_pct = round(sum(top_pcts), 1) if top_pcts else None
        # —— 营收 ——
        revs = []
        for x in (raw.get('revenues') or []):
            a = fnum(x.get('amt'))
            if a is not None:
                revs.append({'year': str(x.get('year') or ''), 'amt_wan': a, 'source': x.get('source') or ''})
        revs.sort(key=lambda x: x['year'])
        rev_latest = revs[-1] if revs else None
        # —— 我司采购额（ERP）——
        erp = by_name.get(cust) or {}
        my_wan = round((erp.get('total') or 0) / 10000.0, 1)
        pen = round(my_wan / rev_latest['amt_wan'] * 100, 1) if (rev_latest and rev_latest['amt_wan']) else None
        dec = [x for x in (raw.get('decision') or []) if x.get('name') or x.get('title')]
        ties = [x for x in dec if x.get('role') == '决策人'] or [x for x in dec if x.get('power') == '高']

        out[cust] = {
            'name': cust,
            'sales': rec.get('sales') or erp.get('sales') or '',
            'last_date': rec.get('last_date', ''),
            'record_count': rec.get('record_count', 1),
            'decision': dec,
            'biz_desc': raw.get('biz_desc') or '',
            'revenues': revs,
            'suppliers': sup,
            'self_share': raw.get('self_share') or '',
            'main_rival': raw.get('main_rival') or '',
            'score': sc,
            'grade': '完整' if sc >= 80 else ('部分' if sc >= 40 else '起步'),
            'missing': missing,
            'metrics': {
                'my_amt_wan': my_wan,
                'erp_y2026_wan': round((erp.get('y2026') or 0) / 10000.0, 1),
                'revenue_latest_wan': rev_latest['amt_wan'] if rev_latest else None,
                'revenue_latest_year': rev_latest['year'] if rev_latest else '',
                'penetration': pen,
                'top3_amt_wan': gap_wan,
                'top3_pct': top3_pct,
                'gap_wan': gap_wan,
                'decision_count': len(dec),
                'tie_names': [x.get('name') for x in ties if x.get('name')],
                'in_erp': bool(erp),
            },
        }

    doc = {
        'updated_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        'count': len(out),
        'customers': out,
    }
    json.dump(doc, open(OUT, 'w', encoding='utf-8'), ensure_ascii=False, separators=(',', ':'))
    print('DOSSIER_OK=1 count=%d out=%s (%d KB)' % (len(out), OUT, os.path.getsize(OUT) // 1024))
    if out:
        top = sorted(out.values(), key=lambda x: -(x['metrics']['gap_wan'] or 0))[:5]
        print('  竞对盘子 TOP%d：' % len(top))
        for x in top:
            m = x['metrics']
            print('    %-28s 完善度%3d ｜ 我司%8.1f万 ｜ 营收%s ｜ 份额%s%% ｜ 竞对盘子%s万'
                  % (x['name'][:28], x['score'], m['my_amt_wan'],
                     (m['revenue_latest_wan'] if m['revenue_latest_wan'] else '—'),
                     x['self_share'] or '—', m['gap_wan']))


if __name__ == '__main__':
    main()
