#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""统一口径：研究方向 → 技术路径 → 宝锐产品线
1) 两省统一：项目名称 → 研究方向（同一套关键词）；浙江学科代码仅做校验
2) 每个研究方向给：宝锐可及率 + 6 条产品线内部分布（经验值）
3) 输出：数据/科研经费_统一口径_产品线.json + Excel
"""
import csv
import io
import json
import re
from collections import Counter, defaultdict

BASE = '/Users/liuxinyuan/Desktop/Hermes输出-工作类/销售分析/基金源文件'
OUT = '/Users/liuxinyuan/Desktop/Hermes输出-工作类/数据/科研经费_统一口径_产品线.json'
LINES = ['分子·RT-qPCR', '分子·PCR/克隆', '分子·核酸提取', '分子·LAMP/恒温', '分子·其他', 'WB']

# ── 研究方向（优先级从上到下，命中即归类；两省同一套）──
TOPICS = [
    ('生物材料/传感器', ['生物材料', '生物传感器', '水凝胶', '纳米药物', '纳米递送', '载药', '组织工程', '支架', '抗菌', '生物相容', '微流控', '生物芯片', '荧光探针', '核酸探针', '生物传感']),
    ('肿瘤', ['肿瘤', '癌', '转移', '耐药', '微环境', '白血病', '淋巴瘤', '肉瘤']),
    ('免疫/感染/炎症', ['免疫', '炎症', '感染', '病毒', '细菌', '菌', '疫苗', '脓毒症', '巨噬', 'T细胞', 'B细胞', '抗体', '过敏', '自身免疫']),
    ('神经科学', ['神经', '脑', '认知', '突触', '阿尔茨海默', '帕金森', '卒中', '脊髓', '癫痫', '抑郁', '焦虑', '胶质']),
    ('代谢/内分泌', ['代谢', '糖尿', '肥胖', '脂', '胰岛素', '肝', '肾', '甲状腺', '骨代谢', '肠道菌']),
    ('遗传/表观/基因组', ['基因', '基因组', '表观', '甲基化', '遗传', '突变', '编辑', '染色体', '转录组', '单细胞', '测序']),
    ('蛋白/信号/修饰', ['蛋白', '信号', '通路', '磷酸化', '乙酰化', '泛素', '受体', '激酶', '互作', '复合物', '酶', '结构', '折叠']),
    ('细胞生物学', ['细胞', '凋亡', '自噬', '增殖', '迁移', '侵袭', '分化', '干细胞', '周期', '衰老', '氧化应激']),
    ('药理/中药', ['药', '中药', '成分', '靶点', '治疗', '干预', '方剂', '活性']),
    ('临床/流行病', ['临床', '患者', '队列', '流行病', '诊断', '预后', '样本', '影像', '护理', '公共卫生']),
    ('农学/动植', ['植物', '作物', '水稻', '玉米', '小麦', '畜禽', '水产', '林木', '育种', '抗逆', '园艺', '兽医']),
]
BIO_ANY = ['基因', '蛋白', '细胞', '免疫', '分子', '微生物', '病毒', '菌', '肿瘤', '癌', '疾病', '临床', '药物', 'RNA', 'DNA', '代谢', '神经',
           '干细胞', '组织', '器官', '病理', '药理', '中医', '中药', '护理', '公共卫生', '流行病', '诊断', '治疗', '靶点', '受体', '酶', '抗体',
           '疫苗', '感染', '炎症', '心血管', '糖尿', '肝', '肾', '肺', '脑', '骨', '眼', '口腔', '生殖', '发育', '遗传', '表观', '转录', '信号',
           '通路', '生物', '医学', '药学', '检验', '影像', '健康', '衰老', '氧化', '应激', '凋亡', '自噬', '分化', '增殖', '迁移', '侵袭', '转移',
           '生物材料', '生物传感器', '水凝胶', '载药', '组织工程']

# ── 研究方向 → 宝锐可及率 + 6 条产品线内部分布（经验值）──
# 依据：PubMed 2024-25 条件提及率（数据/技术路径_条件渗透率.json）+ 实验室试剂支出结构经验
STRUCT = {
    '蛋白/信号/修饰': (0.62, {'WB': 50, '分子·RT-qPCR': 13, '分子·PCR/克隆': 12, '分子·核酸提取': 10, '分子·LAMP/恒温': 1, '分子·其他': 14}),
    '肿瘤': (0.55, {'WB': 35, '分子·RT-qPCR': 20, '分子·PCR/克隆': 12, '分子·核酸提取': 15, '分子·LAMP/恒温': 5, '分子·其他': 13}),
    '免疫/感染/炎症': (0.50, {'WB': 25, '分子·RT-qPCR': 25, '分子·PCR/克隆': 10, '分子·核酸提取': 15, '分子·LAMP/恒温': 10, '分子·其他': 15}),
    '神经科学': (0.55, {'WB': 40, '分子·RT-qPCR': 20, '分子·PCR/克隆': 10, '分子·核酸提取': 15, '分子·LAMP/恒温': 3, '分子·其他': 12}),
    '代谢/内分泌': (0.55, {'WB': 35, '分子·RT-qPCR': 25, '分子·PCR/克隆': 10, '分子·核酸提取': 15, '分子·LAMP/恒温': 2, '分子·其他': 13}),
    '遗传/表观/基因组': (0.65, {'WB': 20, '分子·RT-qPCR': 30, '分子·PCR/克隆': 15, '分子·核酸提取': 25, '分子·LAMP/恒温': 2, '分子·其他': 8}),
    '生物材料/传感器': (0.40, {'WB': 25, '分子·RT-qPCR': 20, '分子·PCR/克隆': 10, '分子·核酸提取': 20, '分子·LAMP/恒温': 5, '分子·其他': 20}),
    '细胞生物学': (0.55, {'WB': 35, '分子·RT-qPCR': 25, '分子·PCR/克隆': 12, '分子·核酸提取': 15, '分子·LAMP/恒温': 3, '分子·其他': 10}),
    '药理/中药': (0.50, {'WB': 35, '分子·RT-qPCR': 25, '分子·PCR/克隆': 8, '分子·核酸提取': 15, '分子·LAMP/恒温': 2, '分子·其他': 15}),
    '临床/流行病': (0.50, {'WB': 15, '分子·RT-qPCR': 35, '分子·PCR/克隆': 6, '分子·核酸提取': 25, '分子·LAMP/恒温': 10, '分子·其他': 9}),
    '农学/动植': (0.55, {'WB': 15, '分子·RT-qPCR': 35, '分子·PCR/克隆': 15, '分子·核酸提取': 25, '分子·LAMP/恒温': 2, '分子·其他': 8}),
    '其他生医': (0.50, {'WB': 30, '分子·RT-qPCR': 25, '分子·PCR/克隆': 10, '分子·核酸提取': 15, '分子·LAMP/恒温': 5, '分子·其他': 15}),
}


BIO_ANCHOR = ['生物', '医学', '医', '细胞', '蛋白', '基因', '抗菌', '组织', '药物', '递送', '诊断', '检测', '核酸', '免疫', '肿瘤', '癌', '菌', '病毒']


def topic_of(title):
    t = title or ''
    for name, kws in TOPICS:
        if not any(k in t for k in kws):
            continue
        if name == '生物材料/传感器' and not any(k in t for k in BIO_ANCHOR):
            continue  # 纯材料/纳米/器件，不算生医
        return name
    return '其他生医' if any(k in t for k in BIO_ANY) else None


ZJ_AMT = [('重大', 100), ('杰青', 80), ('重点', 30), ('探索', 10), ('青基', 10), ('青稞', 10), ('地区', 10), ('延续', 80)]


def zj_amt(cat):
    for k, v in ZJ_AMT:
        if k in cat:
            return v
    return 10


zj = [r for r in csv.DictReader(io.open(BASE + '/parsed/浙江_2026省基金_明细.csv', encoding='utf-8-sig'))
      if r['no'].isdigit() and int(r['no']) <= 2667]
gd = list(csv.DictReader(io.open(BASE + '/parsed/广东_2026省基金_明细.csv', encoding='utf-8-sig')))

for r in zj:
    r['amt'] = zj_amt(r['cat'])
    r['topic'] = topic_of(r['name'])
    m = re.search(r'26([A-H])', r['code'] or '')
    r['disc'] = m.group(1) if m else '?'
for r in gd:
    r['amt'] = int(r['amt']); r['topic'] = topic_of(r['name']); r['disc'] = ''


def run(rows):
    bio = [r for r in rows if r['topic']]
    by_topic = defaultdict(lambda: {'n': 0, 'amt': 0})
    for r in bio:
        by_topic[r['topic']]['n'] += 1
        by_topic[r['topic']]['amt'] += r['amt']
    total_addr = 0.0
    by_line = {l: 0.0 for l in LINES}
    for tp, d in by_topic.items():
        rate, mix = STRUCT[tp]
        addr = d['amt'] * rate
        d['addr'] = addr
        total_addr += addr
        for l in LINES:
            by_line[l] += addr * mix[l] / 100
    units = defaultdict(lambda: {'n': 0, 'amt': 0, 'bio_amt': 0, 'addr': 0, 'lines': {l: 0.0 for l in LINES}})
    for r in rows:
        u = units[r['unit']]
        u['n'] += 1; u['amt'] += r['amt']
        if r['topic']:
            u['bio_amt'] += r['amt']
            rate, mix = STRUCT[r['topic']]
            a = r['amt'] * rate
            u['addr'] += a
            for l in LINES:
                u['lines'][l] += a * mix[l] / 100
    return bio, by_topic, total_addr, by_line, units


res = {}
for prov, rows in (('浙江', zj), ('广东', gd)):
    bio, by_topic, total_addr, by_line, units = run(rows)
    ul = [{'unit': k, **v} for k, v in units.items()]
    ul.sort(key=lambda x: -x['addr'])
    res[prov] = {
        'total_n': len(rows), 'total_amt': sum(r['amt'] for r in rows),
        'bio_n': len(bio), 'bio_amt': sum(r['amt'] for r in bio),
        'addr': round(total_addr, 1),
        'by_line': {k: round(v, 1) for k, v in by_line.items()},
        'by_topic': {k: {'n': v['n'], 'amt': v['amt'], 'addr': round(v['addr'], 1)} for k, v in
                     sorted(by_topic.items(), key=lambda x: -x[1]['amt'])},
        'units': ul,
    }
    print('\n█ %s ｜总 %d 项 / %.0f 万 ｜生医 %d 项 / %.0f 万 ｜宝锐可及 %.0f 万（%.0f%%）' % (
        prov, len(rows), res[prov]['total_amt'], len(bio), res[prov]['bio_amt'], total_addr,
        total_addr / max(1, sum(r['amt'] for r in bio)) * 100))
    for tp, v in sorted(by_topic.items(), key=lambda x: -x[1]['amt']):
        print('   %-14s %4d 项  %8.0f 万  可及 %7.0f 万' % (tp, v['n'], v['amt'], v['addr']))
    print('   产品线：', ' ｜ '.join('%s %.0f' % (l, by_line[l]) for l in LINES))

# 浙江：学科代码 vs 名称口径 一致率
both = [r for r in zj if r['disc'] in ('C', 'H') or r['topic']]
agree = sum(1 for r in zj if (r['disc'] in ('C', 'H')) == bool(r['topic']) and r['topic'])
print('\n█ 浙江 学科代码(C/H) vs 名称口径：一致 %d / 2,667（%.1f%%）；代码为 C/H 但名称未命中 %d；名称命中但代码非 C/H %d' % (
    agree, agree / len(zj) * 100,
    sum(1 for r in zj if r['disc'] in ('C', 'H') and not r['topic']),
    sum(1 for r in zj if r['disc'] not in ('C', 'H') and r['topic'])))

io.open(OUT, 'w', encoding='utf-8').write(json.dumps({
    'meta': {'caliber': '两省统一：项目名称 → 研究方向 → 技术路径 → 产品线',
             'struct_note': '可及率与产品线分布为经验值；PubMed 提及率仅作技术路径排序锚点',
             'zj_n': len(zj), 'gd_n': len(gd)},
    'struct': {k: {'rate': v[0], 'mix': v[1]} for k, v in STRUCT.items()},
    'provinces': res}, ensure_ascii=False, indent=1))
print('\n✅', OUT)
