#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
P0 v0.2 后端改造：_clean_block 白名单 + _block_empty
=====================================================
【为什么必须跑脚本】_clean_block 是硬编码白名单，新增 key 若漏改 → 销售填的当场静默消失且无报错。
本脚本每处改动前 assert 锚点唯一，替换后打印处数；锚点不命中立刻退出，不会留半成品。

用法：
    /usr/bin/python3 脚本/apply_p0v02_backend.py --dry-run   # 只看改动
    /usr/bin/python3 脚本/apply_p0v02_backend.py --apply     # 真改（自动先备份）
"""
import io
import os
import re
import shutil
import sys
import datetime

TARGET = os.path.expanduser('~/Desktop/Hermes输出-工作类/脚本/销售工作台服务.py')

# ── 新增块级 key（32 个，来自定版 DEMO 的 need_backend_whitelist，去掉 decision[] 子字段）──
NEW_KEYS_TEXT = [
    'founded_year', 'registered_capital_wan', 'financing_status', 'headcount_scale',
    'corp_nature', 'biz_type', 'license_status', 'license_count', 'registering_products',
    'risk_notes', 'risk_note_source',
    'price_priority', 'require_backup_vendor', 'vendor_decider', 'negotiation_rounds',
    'quote_buffer', 'phase_split_reg_opt',
    'pain_labor_shortage', 'pain_import_cost', 'pain_notes',
    'segment', 'product_line', 'annual_volume_t', 'volume_unit', 'reaction_system',
    'incumbent_brand', 'test_ref',
    'opportunity', 'challenge', 'support_needed', 'breakthrough',
]
# 多选（数组）字段：单独处理
NEW_KEYS_LIST = ['strategy_focus']

# decision[] 新增子字段（5 个）
NEW_DECISION_SUB = ['influence_tier', 'reports_to', 'attitude', 'comm_pref', 'focus_point']

ANCHOR_INSERT = "                    'plan': [x for x in (b.get('plan') or []) if (x.get('item') or '').strip()],"
ANCHOR_DECISION = """                    'decision': [{'name': (x.get('name') or '').strip(), 'title': (x.get('title') or '').strip(),
                                  'role': (x.get('role') or '').strip(), 'power': (x.get('power') or '').strip(),
                                  'phone': (x.get('phone') or '').strip(), 'note': (x.get('note') or '').strip()}
                                 for x in (b.get('decision') or [])
                                 if (x.get('name') or '').strip() or (x.get('title') or '').strip()],"""


def build_new_keys():
    lines = ['                    # ── 客户画像 P0（v0.2 新增，刘新元 2026-09-29 定版）──']
    for k in NEW_KEYS_TEXT:
        lines.append("                    '%s': (b.get('%s') or '').strip()," % (k, k))
    for k in NEW_KEYS_LIST:
        lines.append("                    '%s': [x for x in (b.get('%s') or []) if (x or '').strip()],"
                     % (k, k))
    lines.append('')
    return '\n'.join(lines) + '\n'


def build_decision():
    """decision[] 加 5 个子字段。最后一个不带拖尾逗号（与文件既有风格一致）。"""
    inner = ["                                  '%s': (x.get('%s') or '').strip()," % (k, k)
             for k in NEW_DECISION_SUB]
    inner[-1] = inner[-1].rstrip(',')
    return ("""                    'decision': [{'name': (x.get('name') or '').strip(), 'title': (x.get('title') or '').strip(),
                                  'role': (x.get('role') or '').strip(), 'power': (x.get('power') or '').strip(),
                                  'phone': (x.get('phone') or '').strip(), 'note': (x.get('note') or '').strip(),
""" + '\n'.join(inner) + """}
                                 for x in (b.get('decision') or [])
                                 if (x.get('name') or '').strip() or (x.get('title') or '').strip()],""")


ANCHOR_EMPTY = """            def _block_empty(bb):
                return not (bb['need'] or bb['params'] or bb['test_order'] or bb['issue'] or bb['feedback']
                            or bb['competitors'] or bb['prices'] or bb['decision'] or bb['biz_desc']
                            or bb['revenues'] or bb['suppliers'] or bb['self_share'] or bb['main_rival']
                            or bb['visit_goal'] or bb['visit_content'] or bb['plan'])"""

# _block_empty 必须覆盖【新】字段，否则只填新卡片 → 整块被判空丢弃
ALL_CHECK = ['need', 'params', 'test_order', 'issue', 'feedback', 'competitors', 'prices',
             'decision', 'biz_desc', 'revenues', 'suppliers', 'self_share', 'main_rival',
             'visit_goal', 'visit_content', 'plan'] + NEW_KEYS_TEXT + NEW_KEYS_LIST


def build_empty():
    terms = ["bb['%s']" % k for k in ALL_CHECK]
    body = []
    line = '                            '
    for i, t in enumerate(terms):
        piece = t
        if i < len(terms) - 1:
            piece += ' or'
        if len(line) + len(piece) + 1 > 108:
            body.append(line.rstrip())
            line = '                            '
        line += piece + ' '
    body.append(line.rstrip().rstrip('or').rstrip())
    return ("            def _block_empty(bb):\n"
            "                # v0.2：必须覆盖 P0 新字段，否则「只填新卡片」的块会被判空丢弃\n"
            "                return not (" + body[0].strip() + "\n" +
            "\n".join(body[1:]) + ")")


def main():
    apply = '--apply' in sys.argv
    s = io.open(TARGET, encoding='utf-8').read()
    orig = s

    # ⓪ 幂等守卫必须在所有断言之前 —— 否则第二次跑会先撞锚点断言而崩
    if 'v0.2 新增，刘新元 2026-09-29 定版' in orig:
        print('  ⚠️ 已应用过（发现 v0.2 标记），本次不重复写入')
        return

    # ① 插入块级新 key
    assert s.count(ANCHOR_INSERT) == 1, '锚点 plan 行命中 %d 次（应为 1）' % s.count(ANCHOR_INSERT)
    s = s.replace(ANCHOR_INSERT, build_new_keys() + ANCHOR_INSERT, 1)

    # ② decision[] 加子字段
    assert s.count(ANCHOR_DECISION) == 1, 'decision 锚点命中 %d 次' % s.count(ANCHOR_DECISION)
    s = s.replace(ANCHOR_DECISION, build_decision(), 1)

    # ③ _block_empty 覆盖新字段
    assert s.count(ANCHOR_EMPTY) == 1, '_block_empty 锚点命中 %d 次' % s.count(ANCHOR_EMPTY)
    s = s.replace(ANCHOR_EMPTY, build_empty(), 1)

    # ④ 语法检查
    import ast
    ast.parse(s)
    print('  ✅ 语法检查通过')

    print('  改动：')
    print('    ① _clean_block 新增块级 key : %d 个（文本 %d + 多选 %d）'
          % (len(NEW_KEYS_TEXT) + len(NEW_KEYS_LIST), len(NEW_KEYS_TEXT), len(NEW_KEYS_LIST)))
    print('    ② decision[] 新增子字段      : %d 个' % len(NEW_DECISION_SUB))
    print('    ③ _block_empty 覆盖字段       : %d 个（原 16 → %d）' % (len(ALL_CHECK), len(ALL_CHECK)))

    if not apply:
        print('\n  [dry-run] 未写入。加 --apply 执行。')
        return

    ts = datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
    bak = '%s.bak-p0v02be-%s' % (TARGET, ts)
    shutil.copy2(TARGET, bak)
    io.open(TARGET, 'w', encoding='utf-8').write(s)
    print('\n  ✅ 已写入 %s' % TARGET)
    print('     备份 %s' % bak)


if __name__ == '__main__':
    main()
