#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""把刘贝欣 2026-10-31 转产进度邮件解析为结构化产品状态台账。

输入：资料/产销/转产进度_邮件正文_20261031.txt
输出：数据/transfer_progress_20261031.json
说明：只做解析与主数据匹配，不改写 product_master；应用脚本见 apply_transfer_progress.py。
"""
import json, os, re, pathlib
from datetime import date

ROOT = pathlib.Path('/Users/liuxinyuan/Desktop/Hermes输出-工作类')
TXT = ROOT / '资料/产销/转产进度_邮件正文_20261031.txt'
OUT = ROOT / '数据/transfer_progress_20261031.json'
PM = ROOT / '数据/product_master.json'
ORDERS = ROOT / '数据/orders_detail_2026.json'
LS = ROOT / '数据/ls_product_catalog.json'

# 邮件正文中出现的货号（按正文出现顺序；用于定位段落边界）
SUCCESS_CODES = ['FE16-NA-50', 'E13-020307', 'MD5104-000829', 'MD2104-056301',
                 'FMD5104-000829', 'FE105-32', 'FE105-32', 'BR1E202', 'BR1E202-4']
PROGRESS_CODES = ['M2244-NA-043801', 'M5244-NA-043801', 'MD5104-014104-5P', 'BR1E103-4',
                  'BR1D201', 'MD2104-003006-DB', 'FMD5104-000830', 'M2181-4-020303',
                  'FMD6104-020322', 'MD6104-020322', 'MD2091-001102', 'AS212-9-T1',
                  'FM5234-NA-009401', 'MD2104-092401', 'M2274-088901', 'M2274-088901-DB',
                  'HW205-P01', 'HW206-P01']

STATUS_GROUPS = {
    '待G597使用决议': ('pending_decision', '待决议'),
    '溶解曲线峰值异常': ('abnormal', '异常监测'),
    '物料采购中': ('material_purchase', '物料采购'),
    'QC排查异常中': ('qc_issue', 'QC异常'),
    '大样配制': ('large_scale_prep', '大样配制'),
    'PMC审核文件中': ('pmc_review', 'PMC审核'),
    '小样检验中': ('small_scale_test', '小样检验'),
    '大样检验中': ('large_scale_test', '大样检验'),
    '走小样': ('small_scale_start', '走小样'),
    '文件信息缺失': ('file_missing', '文件缺失'),
    '相关测试已完成': ('blocked_rd', '待研发补文件'),
    '转产成功': ('success', '转产成功'),
}

def clean(s):
    return re.sub(r'\s+', ' ', str(s or '')).strip()

def load_master():
    d = json.loads(PM.read_text(encoding='utf-8'))
    return d, {x['sku']: x for x in d.get('skus', []) if x.get('sku')}

def load_order_names():
    out = {}
    d = json.loads(ORDERS.read_text(encoding='utf-8')).get('orders', {})
    for o in d.values():
        for ln in o.get('lines', []):
            c = ln.get('sku')
            n = (ln.get('name') or '').strip()
            if c and n and c not in out:
                out[c] = n
    return out

def load_ls_names():
    out = {}
    d = json.loads(LS.read_text(encoding='utf-8'))
    for it in d.get('items', []):
        n = (it.get('name') or '').strip()
        if not n:
            continue
        for c in (it.get('skus') or []):
            out.setdefault(str(c).strip(), n)
        raw = str(it.get('sku_raw') or '')
        for c in re.findall(r'BR1[A-Z0-9-]+', raw):
            out.setdefault(c, n)
            # BR1E202-41 的族名归到 BR1E202，供无规格后缀的转产货号引用
            base = c.split('-')[0]
            if re.match(r'^BR1[A-Z]\d{3}$', base):
                out.setdefault(base, n)
    return out

def status_of(text):
    for kw, pair in STATUS_GROUPS.items():
        if kw in text:
            return pair
    return ('in_progress', '推进中')

def main():
    text = TXT.read_text(encoding='utf-8')
    # 成功段
    m = re.search(r'转产成功的有\s*(\d+)\s*个货号[：:](.*?)。', text, re.S)
    if not m:
        raise SystemExit('未找到转产成功段')
    declared = int(m.group(1))
    success_raw = clean(m.group(2))
    # 去掉括号备注，按顿号切
    success_tokens = [x.strip() for x in re.split(r'[、,，]', success_raw) if x.strip()]
    success_codes = []
    success_notes = {}
    for tok in success_tokens:
        mm = re.match(r'([A-Z0-9][A-Z0-9-]+?)(?:[（(](.+?)[）)])?$', tok)
        if not mm:
            continue
        code = mm.group(1)
        note = (mm.group(2) or '').strip()
        success_codes.append(code)
        if note:
            success_notes[code] = note
    unique_success = list(dict.fromkeys(success_codes))
    duplicates = [c for c in unique_success if success_codes.count(c) > 1]

    # 推进段
    sec = text[text.find('三、其余已收到转产文件的货号推进情况'):text.find('第四季度已在逐步恢复')]
    sec = sec.replace('BR1D201常规', 'BR1D201 常规')
    positions = []
    for c in PROGRESS_CODES:
        mm = re.search(r'(?<![A-Z0-9-])' + re.escape(c) + r'(?![A-Z0-9-])', sec)
        if not mm:
            raise SystemExit('推进段缺少货号：%s' % c)
        positions.append((mm.start(), c))
    positions.sort()
    progress = {}
    for n, (i, c) in enumerate(positions):
        end = positions[n + 1][0] if n + 1 < len(positions) else len(sec)
        t = clean(sec[i + len(c):end])
        progress[c] = t

    # 主数据/订单/LS 名称
    master, by_sku = load_master()
    order_names = load_order_names()
    ls_names = load_ls_names()

    def match(code):
        if code in by_sku:
            return {'master_match': 'exact', 'master_sku': code,
                    'master_name': by_sku[code].get('name', ''),
                    'name_source': 'product_master.json'}
        # 唯一前缀候选（只作候选，不视为精确命中）
        parts = code.split('-')
        cands = []
        for n in range(len(parts), 1, -1):
            pref = '-'.join(parts[:n])
            cands = [k for k in by_sku if k.startswith(pref)]
            if cands:
                break
        if len(cands) == 1:
            k = cands[0]
            return {'master_match': 'prefix_variant', 'master_sku': k,
                    'master_name': by_sku[k].get('name', ''),
                    'name_source': 'product_master.json（同族候选 %s）' % k}
        if code in order_names:
            return {'master_match': 'not_in_master', 'master_sku': None,
                    'master_name': order_names[code], 'name_source': 'orders_detail_2026.json'}
        if code in ls_names:
            return {'master_match': 'ls_catalog_only', 'master_sku': None,
                    'master_name': ls_names[code], 'name_source': 'ls_product_catalog.json'}
        # 规格后缀（如 BR1E202-4）归到 LS 同族基码
        base = code.split('-')[0]
        if base in ls_names and base != code:
            return {'master_match': 'ls_catalog_family', 'master_sku': None,
                    'master_name': ls_names[base],
                    'name_source': 'ls_product_catalog.json（同族基码 %s）' % base}
        # 单段族名（如 FE105-32 → 主数据 FE105 的同族名称）
        if base in by_sku and base != code:
            return {'master_match': 'family_base', 'master_sku': base,
                    'master_name': by_sku[base].get('name', ''),
                    'name_source': 'product_master.json（同族基码 %s）' % base}
        return {'master_match': 'not_found', 'master_sku': None,
                'master_name': '', 'name_source': ''}

    items = []
    for c in unique_success:
        st = status_of('转产成功')
        mt = match(c)
        items.append({'code': c, 'status_group': st[0], 'status_label': st[1],
                      'progress': '以 QA 发出确认转产成功邮件为准',
                      'next_plan': '', 'line_note': success_notes.get(c, ''),
                      'issue_note': '', 'source_section': '一、转产成功', **mt})
    for c in PROGRESS_CODES:
        t = progress[c]
        # 行内备注：生科原 / 常规 / 单组分
        note = ''
        for prefix in ('生科原', '常规', '单组分'):
            if t.startswith(prefix):
                note = prefix
                t = clean(t[len(prefix):])
                break
        st = status_of(t)
        mt = match(c)
        issue = ''
        if c in ('HW205-P01', 'HW206-P01'):
            issue = ('年初出现 buffer 沉淀异常，退回研发；生产端已完成测试观察并反馈研发端（10-28）；'
                     '请研发端尽快完成原因分析和补充风险提示。')
        items.append({'code': c, 'status_group': st[0], 'status_label': st[1],
                      'progress': t, 'next_plan': '', 'line_note': note,
                      'issue_note': issue, 'source_section': '三、推进中', **mt})

    # 统计
    by_status = {}
    for it in items:
        by_status[it['status_group']] = by_status.get(it['status_group'], 0) + 1
    exact = [x['code'] for x in items if x['master_match'] == 'exact']
    missing = [x['code'] for x in items if x['master_match'] != 'exact']

    pack = {
        'generated': date.today().isoformat(),
        'source': {
            'type': 'email',
            'subject': '回复：【会议纪要】产销协调会',
            'from': '刘贝欣 <liubeixin@biori.com>',
            'sent': '2026-10-31 16:24:21',
            'forwarded_by': '刘新元 <liuxinyuan@biori.com>',
            'forwarded_at': '2026-10-31 18:58:31',
            'scope': '生产部分子组（不涉及冻干及试剂盒）',
            'q4_target': '第四季度每月完成 10–12 个货号',
            'saved_email': '资料/产销/转产进度邮件_刘贝欣_20261031.eml',
            'saved_text': '资料/产销/转产进度_邮件正文_20261031.txt',
        },
        'summary': {
            'success_declared': declared,
            'success_unique': len(unique_success),
            'success_duplicates': duplicates,
            'success_note': ('邮件正文称 9 个，实际列出 %d 个条目、%d 个唯一货号；%s 重复出现。'
                             % (len(success_codes), len(unique_success), '、'.join(duplicates) or '无')),
            'in_progress_unique': len(PROGRESS_CODES),
            'total_unique': len(items),
            'by_status': by_status,
            'master_exact': len(exact),
            'master_missing': len(missing),
            'master_missing_codes': missing,
        },
        'items': items,
    }
    OUT.write_text(json.dumps(pack, ensure_ascii=False, indent=2), encoding='utf-8')
    print('WROTE', OUT)
    print('success declared/unique:', declared, '/', len(unique_success), 'dups:', duplicates)
    print('items:', len(items), 'by_status:', by_status)
    print('master exact:', len(exact), exact)
    print('master missing:', len(missing))
    for it in items:
        print('%-22s %-10s %-14s %-12s %s' % (it['code'], it['status_label'], it['master_match'],
                                              it['master_sku'] or '-', it['progress'][:50]))

if __name__ == '__main__':
    main()
