#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""年度目标守卫：断言所有载目标文件 = 6,650 万 且带注释「300万元北京科研目标空缺」；任一处不符即非 0 退出。

口径（用户 2026-10-15 拍板）：全司年度目标 6,650 万 = 大客户 2650 + 拓展 3400 + 生命科学 600。
原 6,950 万中的 300 万为北京科研目标空缺，单列注释、不计入 6650。
"""
import io
import json
import os
import re
import sys

W = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
NOTE = '300万元北京科研目标空缺'
YUAN = 66500000.0
fails = []

# ① 权威源
tb = json.load(io.open(W + '/数据/目标基础数据_2026.json', encoding='utf-8'))
meta_total = tb['meta'].get('total_annual')
dept_sum = sum(v.get('target_annual', 0) for v in tb['departments'].values())
print('① 权威源 目标基础数据_2026：meta=%s ｜ Σ部门=%s' % (meta_total, dept_sum))
if abs(meta_total - 6650) > 0.01:
    fails.append('① meta.total_annual=%s ≠ 6650' % meta_total)
if abs(dept_sum - 6650) > 0.01:
    fails.append('① Σ部门年度=%s ≠ 6650（部门目标被改动）' % dept_sum)
if not tb.get('target_note') and not (tb.get('meta') or {}).get('target_note'):
    fails.append('① 权威源缺 target_note')

# ② 元值载体
for rel, key in (('数据/kpi_dashboard.json', 'total_target'), ('数据/my_kpis.json', 'total_target')):
    d = json.load(io.open(os.path.join(W, rel), encoding='utf-8'))
    v = d.get(key)
    ok = v is not None and abs(v - YUAN) < 1
    print('② %-28s %s = %s ｜ 注释=%s' % (os.path.basename(rel), key, v, bool(d.get('target_note'))))
    if not ok:
        fails.append('② %s.%s=%s ≠ %s（万=%s）' % (rel, key, v, YUAN, None if v is None else round(v / 1e4, 1)))
    if d.get('target_note') != NOTE:
        fails.append('② %s 缺注释' % rel)

# ③ 万元级载体
for rel, keys in (('数据/year_targets_2026.json', ('annual',)),
                  ('数据/月度目标_2026统一.json', ('annual_total',))):
    d = json.load(io.open(os.path.join(W, rel), encoding='utf-8'))
    for k in keys:
        v = d.get(k)
        print('③ %-28s %s = %s' % (os.path.basename(rel), k, v))
        if v is None or abs(float(v) - 6650) > 0.01:
            fails.append('③ %s.%s=%s ≠ 6650' % (rel, k, v))
    if d.get('target_note') != NOTE:
        fails.append('③ %s 缺注释' % rel)

# ④ 聚合层
d = json.load(io.open(W + '/数据/workbench_data.json', encoding='utf-8'))
kd = d.get('kpiDashboard') or {}
print('④ workbench_data.kpiDashboard.total_target = %s ｜ 注释=%s' % (kd.get('total_target'), kd.get('target_note')))
if kd.get('total_target') is None or abs(kd['total_target'] - YUAN) > 1:
    fails.append('④ 聚合层 total_target ≠ 6650 万（需重跑 merge_workbench_data.py）')
if kd.get('target_note') != NOTE:
    fails.append('④ 聚合层缺注释')

# ⑤ 页面层
for rel in ('工具/index.html', '工具/app.html', '工具/仪表盘/目标基础数据管理台.html',
            '工具/仪表盘/宝锐综合管理仪表盘.html'):
    p = os.path.join(W, rel)
    if not os.path.exists(p):
        continue
    s = io.open(p, encoding='utf-8', errors='ignore').read()
    has_note = NOTE in s
    stale = len(re.findall(r'6950\s*万|>6848万<|69[,，]?500[,，]?000', s))
    print('⑤ %-34s 注释=%s ｜ 旧值残留=%d' % (os.path.basename(rel), has_note, stale))
    if not has_note:
        fails.append('⑤ %s 缺注释' % rel)
    if stale:
        fails.append('⑤ %s 仍有旧年度目标值 %d 处' % (rel, stale))

# ⑥ 看板·明细下钻·按销售员：全年目标 / 全年目标达成% 两列必须在（防页面被重生成覆盖）
_dr = os.path.join(W, '工具/仪表盘/三部门逐月拆分_demo.html')
if os.path.exists(_dr):
    t = io.open(_dr, encoding='utf-8', errors='ignore').read()
    checks = [("列定义 tgtA", "cols.push({k:'tgtA'"), ("列定义 rateA", "cols.push({k:'rateA'"),
              ("达成% 分子=1-9月金额", "pct(r.ytd, r.tgtA)"), ("1-9月金额累加", "o.ytd = addNum(o.ytd"),
              ("着色函数 annCls", "function annCls("), ("CSV 导出兼容", "c.k === 'rateA'")]
    miss = [n for n, pat in checks if pat not in t]
    print('⑥ 下钻页全年两列：%s' % ('✅ 齐全' if not miss else ('❌ 缺 ' + '、'.join(miss))))
    if miss:
        fails.append('⑥ 下钻页缺全年两列补丁：%s（页面被重生成覆盖？）' % '、'.join(miss))
    if 'pct(r.achA' in t:
        print('⑥ 反向断言：❌ 仍在用 achieve_ytd 当分子')
        fails.append('⑥ 全年目标达成% 分子又用回了 achieve_ytd（应为 1-9 月金额 pct(r.ytd, r.tgtA)）')
    else:
        print('⑥ 反向断言：✅ 分子为 1-9 月金额')
else:
    print('⑥ 下钻页不存在，跳过')

print()
if fails:
    print('❌ 年度目标守卫未通过：')
    for f in fails:
        print('   -', f)
    sys.exit(1)
print('✅ 年度目标守卫通过：全库 = 6,650.0 万元 + 注释齐全')
