#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""高价值精选 v2：客户信号提权 + 关联我方货号 + 建议动作到人名级。

权重（v2，客户优先）：
  客户拿证 +8 ｜ 客户中标/合同 +7 ｜ 客户经营风险(亏损/减值/集采/业绩下滑) +6
  客户产品获批 +5 ｜ 客户资本·合作·扩产 +3 ｜ 与我方有关联 +3 ｜ 人事变动 +2
  竞品命中我方赛道 +3（降权） ｜ 竞品新品 +2（降权） ｜ 竞品战略动作 +1
  时效：≤30 天 +2 ｜ ≤90 天 +1（>90 天不得进 S 档）
分档：S ≥ 13 ｜ A 9–12 ｜ B 5–8

货号建议：按信号关键词（核酸类型/技术方法/冻干/POCT/LAMP/单酶）匹配 `数据/diag_knowledge.json` 打分取前 4。
主责人：`customer_attribution.json` 的客户→销售反向映射（模糊匹配）。
"""
import json
import os
import re
import sys
from datetime import datetime, timedelta

W = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
RUNS = os.path.join(W, '数据/market_intel/runs')
DAY = datetime.now().strftime('%Y%m%d')
MODE = 'first'
for a in sys.argv[1:]:
    if a in ('first', 'daily'):
        MODE = a
    elif a.isdigit():
        DAY = a
SUF = '_enriched_first.jsonl' if MODE == 'first' else '_enriched.jsonl'
data = json.load(open(os.path.join(RUNS, DAY + SUF), encoding='utf-8'))
items = [i for i in data if isinstance(i.get('要点'), dict) and '错误' not in i['要点']]
SKUS = json.load(open(os.path.join(W, '数据/diag_knowledge.json'), encoding='utf-8'))['items']
ATTR = json.load(open(os.path.expanduser('~/.hermes/profiles/baorui-sales/customer_attribution.json'), encoding='utf-8'))
INV = {}
for sp, v in ATTR['salespeople'].items():
    for c in v.get('customers', []):
        INV[c] = sp
NOW = datetime.now()
DATE_HUMAN = NOW.strftime('%Y-%m-%d')
REC30 = (NOW - timedelta(days=30)).strftime('%Y-%m-%d')
REC90 = (NOW - timedelta(days=90)).strftime('%Y-%m-%d')


# ── 客户 → 销售（模糊匹配）──
def norm(s):
    return re.sub(r'(股份有限公司|有限公司|集团|公司|生物科技|生物技术|科技|技术|\(.*?\)|（.*?）)', '', s or '')


def owner_of(subject, title=''):
    for src in (subject, title):
        for k, v in INV.items():
            if k and (k in src or (len(norm(k)) >= 2 and norm(k) in norm(src))):
                return v, k
    return None, None


# ── 信号 → 货号建议（curated 分桶，只从真实 84 货号中取）──
SKU_NAME = {x['货号']: x['品名'] for x in SKUS}
BUCKET = [
    # (规则名, 命中条件, 货号列表)
    ('RNA病毒/RT-qPCR(探针法)', r'rna|逆转录|rt-?pcr|诺如|流感|rsv|呼吸道|新冠|病毒|mrna',
     ['FMD5104-000814', 'MD2104-000814', 'M2274-073702', 'M5244-014405']),
    ('DNA荧光PCR(探针法)', r'荧光\s*pcr|qpcr|探针|分型|定量|dna|基因',
     ['M216-3', 'M2181-4', 'M2161-096701', 'FM5161']),
    ('多重/熔解曲线', r'熔解|熔曲|mmca|超多重|多重\s*pcr',
     ['M3291', 'FM0301', 'FM0324-074901']),
    ('冻干/POCT交付', r'冻干|lyo|poct|常温储运|常温保存',
     ['FM5071', 'FM2181-4', 'FMD5084-000903', 'FMD2111']),
    ('直扩/免提取', r'直扩|释放剂|免提取|样本直接|direct',
     ['MD2091', 'MD2071', 'MD2111']),
    ('LAMP/恒温扩增', r'lamp|恒温|等温',
     ['HW205-P01', 'HW206-P01', 'HW205-M01', 'HW206-M01']),
    ('UNG防污染', r'ung|污染控制|防污染|气溶胶',
     ['E01', 'E04-2']),
    ('miRNA/科研', r'mirna|小rna|cdna合成',
     ['AS44-001902', 'AS44-234401']),
    ('高GC/缓冲体系', r'高gc|gc含量|缓冲',
     ['AS304']),
]


def sku_hits(text):
    txt = text.lower()
    res, seen = [], set()
    for name, pat, skus in BUCKET:
        if not re.search(pat, txt):
            continue
        for no in skus:
            if no in seen or no not in SKU_NAME:
                continue
            seen.add(no)
            res.append((no, SKU_NAME[no], name))
            if len(res) >= 4:
                return res
    return res


ACTION = [
    (r'注册证', '技术对接 + 送样：按证号产品线匹配上面货号，7 日内出选型建议'),
    (r'中标|成交', '投标跟进：核实招标参数与货号适配度，评估投标或配套供应'),
    (r'亏损|减值|商誉|业绩下降', '信用复核：调取财报明细（减值 vs 经营性亏损），复核账期与额度'),
    (r'收购|增资|投资|合作|签约|协议', '情报跟踪：判断资本动作是否牵动诊断板块采购，纳入下次拜访议题'),
    (r'总裁|CEO|任命|辞任', '关系维护：新任负责人背景摸底，安排礼节性拜访'),
    (r'冻干|无甘油|Lyo|glycerol', '竞品对标：更新可冻干原料参数对比表，检查我方冻干体系差异化话术'),
    (r'预混液|全预混|多重|熔解曲线|LAMP', '竞品对标：按品类更新对标表，准备性能-成本论证材料'),
    (r'新品|发布|推出|上市', '竞品跟踪：纳入月度竞品动态，评估是否需 PK 实验'),
]
DEPT = [(r'注册证', '技术支持部 + 客户对应销售'), (r'中标|成交', '销售拓展部')]


def score(i):
    t = i['标题']
    p = i['要点']
    s, why = 0, []
    if '注册证' in t:
        s += 8; why.append('客户拿证')
    if re.search(r'中标|成交|合同', t):
        s += 7; why.append('客户中标/合同')
    if re.search(r'亏损|减值|集采|业绩|下降|商誉', t):
        s += 6; why.append('客户经营/价格风险')
    if re.search(r'获得.*批准|获批|批准通知书', t):
        s += 5; why.append('产品获批')
    if re.search(r'投资|增资|收购|合作|协议|扩产', t):
        s += 3; why.append('资本/合作动作')
    is_comp = i['来源'].startswith('竞品官网')
    pltags = [x for x in i.get('标签', []) if x.startswith('产品线:')]
    if is_comp and pltags:
        s += 3; why.append('竞品命中我方赛道')
    if is_comp and re.search(r'新品|发布|上市|推出', t):
        s += 2; why.append('竞品新品')
    if is_comp and re.search(r'战略合作|签约|融资|并购', t):
        s += 1; why.append('竞品战略动作')
    if re.search(r'总裁|CEO|任命|辞任|董事长', t):
        s += 2; why.append('人事变动')
    rel = str(p.get('与我方关联', ''))
    if rel and rel != '无直接关联':
        s += 3; why.append('与我方有关联')
    d = (i.get('日期') or '')[:10]
    if d >= REC30:
        s += 2
    elif d >= REC90:
        s += 1
    return s, why


# ── 跨日去重（daily 模式）：已推送过的链接不再重复 ──
PUSHED = os.path.join(RUNS, '_pushed_links.json')
pushed = set(json.load(open(PUSHED, encoding='utf-8'))) if os.path.exists(PUSHED) else set()
if MODE == 'daily':
    before = len(items)
    items = [i for i in items if i['链接'] not in pushed]
    if before != len(items):
        print('跨日去重：跳过已推送 %d 条' % (before - len(items)))

rows = []
for i in items:
    s, why = score(i)
    i['_s'], i['_why'] = s, why
    d = (i.get('日期') or '')[:10]
    if s >= 12 and d < REC90:
        s -= 6
        i['_s'] = s
        i['_why'].append('（时效外降档）')
    if s >= 5:
        rows.append(i)
rows.sort(key=lambda x: x.get('日期', ''), reverse=True)
rows.sort(key=lambda x: -x['_s'])

S = [x for x in rows if x['_s'] >= 13]
A = [x for x in rows if 9 <= x['_s'] < 13]
B = [x for x in rows if 5 <= x['_s'] < 9]

L = ['# 高价值信息精选 v2 · %s\n' % DAY,
     '> 权重：客户信号优先（拿证 +8 / 中标 +7 / 经营风险 +6 / 获批 +5）｜竞品降权（赛道命中 +3 / 新品 +2）',
     '> 时效闸门：超 90 天不进 S 档 ｜ 货号建议：按信号匹配 84 货号库 ｜ 主责：客户归属表映射到销售本人',
     '> 分档：S %d ｜ A %d ｜ B %d（合计 %d）\n' % (len(S), len(A), len(B), len(rows)),
     '---\n', '## 一、S 档（%d 条，需本周动作）\n' % len(S)]

for n, i in enumerate(S, 1):
    p = i['要点']
    act = next((a for pat, a in ACTION if re.search(pat, i['标题'])), '情报归档：纳入季度行业分析')
    who_, matched = owner_of(i['主体'], i['标题'])
    skus = sku_hits(i['标题'] + ' ' + str(p.get('关键事项', '')) + ' ' + str(p.get('事件', '')))
    L.append('### S%d. [%d分] %s' % (n, i['_s'], i['标题'][:100]))
    L.append('')
    L.append('| 维度 | 内容 |')
    L.append('|---|---|')
    L.append('| 时间 | %s |' % p.get('时间', '未提及'))
    L.append('| 地点 | %s |' % p.get('地点', '未提及'))
    L.append('| 人物 | %s |' % p.get('人物', '未提及'))
    L.append('| 主体 | %s |' % p.get('主体', '未提及'))
    L.append('| **事件** | **%s** |' % p.get('事件', '未提及'))
    L.append('| 关键事项 | %s |' % str(p.get('关键事项', '未提及'))[:300])
    L.append('| 与我方关联 | %s |' % str(p.get('与我方关联', '未提及'))[:200])
    L.append('| **可推货号** | %s |' % ('；'.join('`%s` %s' % (no, nm[:30]) for no, nm, _ in skus) or '未匹配（需人工选型）'))
    L.append('| 评分理由 | %s |' % '；'.join(i['_why']))
    L.append('| **建议动作** | **%s** |' % act)
    L.append('| **主责（人名）** | **%s** |' % (('%s（归属客户：%s）' % (who_, matched)) if who_ else '无对应销售（竞品/行业信息 → 市场部）'))
    L.append('| 原文 | %s |' % i['链接'])
    L.append('')

L.append('---\n## 二、A 档（%d 条，30 天内跟进）\n' % len(A))
for n, i in enumerate(A[:60], 1):
    p = i['要点']
    act = next((a for pat, a in ACTION if re.search(pat, i['标题'])), '情报归档')
    who_, _ = owner_of(i['主体'], i['标题'])
    skus = sku_hits(i['标题'] + ' ' + str(p.get('关键事项', '')))
    L.append('### A%d. [%d分] %s' % (n, i['_s'], i['标题'][:96]))
    L.append('- **%s** ｜ %s ｜ 主责：%s' % (i.get('日期', '—'), i['来源'], who_ or '市场部'))
    L.append('- 事件：%s' % str(p.get('事件', ''))[:90])
    L.append('- 关键事项：%s' % str(p.get('关键事项', ''))[:200])
    L.append('- 关联：%s' % str(p.get('与我方关联', ''))[:130])
    if skus:
        L.append('- 可推货号：%s' % '；'.join('`%s` %s' % (no, nm[:26]) for no, nm, _ in skus))
    L.append('- 建议动作：%s' % act)
    L.append('- 原文：%s\n' % i['链接'])
L.append('---\n## 三、B 档索引（%d 条）\n' % len(B))
L.append('| 分 | 日期 | 主体 | 标题 | 命中理由 |')
L.append('|---|---|---|---|---|')
for i in B[:200]:
    L.append('| %d | %s | %s | %s | %s |' % (i['_s'], (i.get('日期') or '—')[:10], i['主体'][:14],
                                           i['标题'][:54], '；'.join(i['_why'])[:36]))
L.append('')
OUT = DAY + ('_高价值精选v2.md' if MODE == 'first' else '_高价值精选.md')
open(os.path.join(RUNS, OUT), 'w', encoding='utf-8').write('\n'.join(L))

# ── 结构化 JSON（供 HTML 报告渲染）──
def pack(i):
    p_ = i['要点']
    who_, matched = owner_of(i['主体'], i['标题'])
    skus = sku_hits(i['标题'] + ' ' + str(p_.get('关键事项', '')) + ' ' + str(p_.get('事件', '')))
    act = next((a for pat, a in ACTION if re.search(pat, i['标题'])), '情报归档：纳入季度行业分析')
    return {'分数': i['_s'], '档位': 'S' if i['_s'] >= 13 else ('A' if i['_s'] >= 9 else 'B'),
            '标题': i['标题'], '来源': i['来源'], '主体': i['主体'], '日期': i.get('日期', ''),
            '链接': i['链接'], '要点': p_, '理由': i['_why'], '建议动作': act,
            '主责': who_ or '市场部', '归属客户': matched or '',
            '货号': [{'货号': no, '品名': nm, '桶': b} for no, nm, b in skus]}


json.dump({'生成时间': datetime.now().isoformat(timespec='seconds'), '模式': MODE,
           'S': [pack(i) for i in S], 'A': [pack(i) for i in A[:120]], 'B': [pack(i) for i in B],
           'B总数': len(B)},
          open(os.path.join(RUNS, DAY + ('_highvalue.json' if MODE == 'first' else '_highvalue_daily.json')),
               'w', encoding='utf-8'), ensure_ascii=False, indent=1)
print('结构化 JSON 已输出：%s_highvalue.json（S %d ／ A %d ／ B %d）' % (DAY, len(S), len(A), len(B)))

# 推送文案（聊天可读）
P = ['【行业情报日报 · %s】' % DATE_HUMAN]
P.append('S 档 %d 条（需本周动作）｜A 档 %d 条 ｜B 档 %d 条' % (len(S), len(A), len(B)))
P.append('')
for n, i in enumerate(S, 1):
    p_ = i['要点']
    act = next((a for pat, a in ACTION if re.search(pat, i['标题'])), '情报归档')
    who_, _m = owner_of(i['主体'], i['标题'])
    skus = sku_hits(i['标题'] + ' ' + str(p_.get('关键事项', '')))
    P.append('■ S%d [%d分] %s' % (n, i['_s'], str(p_.get('事件', i['标题']))[:60]))
    P.append('   主体：%s ｜ %s' % (i['主体'][:26], (i.get('日期') or '')[:10]))
    P.append('   要点：%s' % str(p_.get('关键事项', ''))[:150])
    P.append('   关联：%s' % str(p_.get('与我方关联', ''))[:110])
    if skus:
        P.append('   可推货号：%s' % '；'.join('%s %s' % (no, nm[:20]) for no, nm, _ in skus[:3]))
    P.append('   动作：%s ｜ 主责：%s' % (act, who_ or '市场部'))
    P.append('   原文：%s' % i['链接'])
    P.append('')
if A:
    P.append('■ A 档摘要（30 天内跟进，前 10 条）')
    for i in A[:10]:
        w, _ = owner_of(i['主体'], i['标题'])
        P.append('· %s｜%s｜%s（主责 %s）' % ((i.get('日期') or '—')[:10] or '—', i['主体'][:14],
                                            str(i['要点'].get('事件', ''))[:44], w or '市场部'))
P.append('')
P.append('完整版：runs/%s' % OUT)
txt = '\n'.join(P)
open(os.path.join(RUNS, DAY + '_push_highvalue.txt'), 'w', encoding='utf-8').write(txt)
if MODE == 'daily':
    pushed |= {x['链接'] for x in rows}
    json.dump(sorted(pushed), open(PUSHED, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
    print('推送台账累计：%d 条链接' % len(pushed))
print(txt[:600])

print('✅ v2 已生成：S %d ｜ A %d ｜ B %d' % (len(S), len(A), len(B)))
cov = sum(1 for i in rows if owner_of(i['主体'], i['标题'])[0])
print('   主责人名覆盖率：%d/%d' % (cov, len(rows)))
print()
print('=== S 档（%d 条）===' % len(S))
for i in S:
    who_, m = owner_of(i['主体'], i['标题'])
    sk = sku_hits(i['标题'] + ' ' + str(i['要点'].get('关键事项', '')))
    print('  %2d分｜%s｜%s｜主责:%s｜货号:%s' % (i['_s'], i['主体'][:14], (i.get('日期') or '')[:10],
                                             who_ or '—', ','.join(x[0] for x in sk[:3]) or '—'))
    print('        %s' % i['标题'][:86])
