#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
「💰 价格查询」面板 · 渲染功能验证（不依赖浏览器）
=====================================================
做两件事，都是真跑、不是静态审读：

① 从 工具/index.html / 工具/app.html 里把注入的面板 JS 抽出来，交给 Node 执行
   renderPricePanel()，喂**真实接口响应**（由 price_api.query 现取），断言渲染结果含：
   年份切换四档、年度分布表、全库区间、按客户表、明细表；并且
   —— own 口径渲染结果里不得出现任何其他客户名（UI 层防泄漏）
   —— all 口径明细才带「销售员」列
② 断言两个前端文件里都确实有年份切换（priceQueryYearSet / 2024/2025/2026）与面板入口。

用法：/usr/bin/python3 脚本/verify_price_panel.py
退出码：0 全通过；1 有断言失败
"""

import json
import os
import re
import subprocess
import sys
import tempfile

HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(HERE)
sys.path.insert(0, HERE)

import price_api  # noqa: E402

FILES = ['工具/index.html', '工具/app.html']
FAILS = []
TARGET = 'FE13'
SALES = '刘子研'
ADMIN = '刘新元'


def check(cond, msg, detail=''):
    print('  %s %s%s' % ('✅' if cond else '❌', msg, (' | ' + detail) if detail else ''))
    if not cond:
        FAILS.append(msg + (' | ' + detail if detail else ''))


def extract_panel(html):
    """抓取注入的面板 JS 段（从 💰 价格查询 注释块到 renderPricePanel 结束）"""
    i = html.find('/* ===== 💰 价格查询')
    j = html.find('function renderPricePanel(')
    assert i > 0 and j > i, '没找到注入的面板 JS'
    k = html.find('\n}\n', j)
    assert k > j, 'renderPricePanel 结尾没找到'
    seg = html[i:k + 3]
    assert 'priceQueryYearSet' in seg, '面板 JS 缺年份切换函数'
    return seg


HARNESS = r'''
var state = { user: { name: %(viewer)s, role: %(role)s } };
var esc = function(s) { return String(s || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); };
function render() {}
function apiFetch() { return Promise.resolve({ json: function() { return Promise.resolve({}); } }); }
var AUTH_APIS = ['http://127.0.0.1:8765'];
%(panel)s
priceQueryHotTried = true;
priceQueryData = %(data)s;
priceQueryKw = %(kw)s;
priceQueryYear = %(year)s;
var html = renderPricePanel();
process.stdout.write(html);
'''


def render(panel_js, data, viewer, role, kw='FE13', year='all'):
    src = HARNESS % {'panel': panel_js, 'data': json.dumps(data, ensure_ascii=False),
                     'kw': json.dumps(kw, ensure_ascii=False), 'year': json.dumps(year),
                     'viewer': json.dumps(viewer, ensure_ascii=False),
                     'role': json.dumps(role)}
    d = tempfile.mkdtemp(prefix='pricepanel_')
    p = os.path.join(d, 'h.js')
    open(p, 'w', encoding='utf-8').write(src)
    r = subprocess.run(['node', p], capture_output=True, text=True)
    if r.returncode != 0:
        raise RuntimeError('node 执行失败：%s' % (r.stderr or '')[:500])
    return r.stdout


def main():
    idx = json.load(open(os.path.join(ROOT, '数据', 'price_index.json'), encoding='utf-8'))
    rec = idx['skus'][TARGET]
    all_custs = set(r['customer'] for r in rec['records'] if r.get('customer'))
    mine_custs = set(r['customer'] for r in rec['records'] if r.get('sales') == SALES)
    others = all_custs - mine_custs
    print('靶子 %s：全库 %d 条 / %d 家客户；%s 自己 %d 条 / %d 家客户\n'
          % (TARGET, len(rec['records']), len(all_custs), SALES,
             len([r for r in rec['records'] if r['sales'] == SALES]), len(mine_custs)))

    admin_res, code_a = price_api.query({'name': ADMIN, 'role': 'admin'}, TARGET, year='2024')
    sales_res, code_s = price_api.query({'name': SALES, 'role': 'sales'}, TARGET, year='2024')
    assert code_a == 200 and code_s == 200, '接口取数失败'

    for rel in FILES:
        print('【%s】' % rel)
        html = open(os.path.join(ROOT, rel), encoding='utf-8').read()
        check(html.count('function renderPricePanel(') == 1, '面板函数注入一次')
        check('renderPricePanel() +' in html, '面板已挂进 renderCustomers（在客户清单之前）')
        check("priceQueryYearSet('all')" in html or 'priceQueryYearSet' in html,
              '年份切换处理函数存在')
        check("priceQueryYear = 'all'" in html, '默认年份 = 全部')
        check("'2024'" in html and "'2025'" in html and "'2026'" in html, '年份选项含 2024/2025/2026')
        panel_js = extract_panel(html)

        # ① admin / 2024
        out = render(panel_js, admin_res, ADMIN, 'admin', kw=TARGET, year='2024')
        check('💰 价格查询' in out, '面板标题渲染')
        check('年度分布' in out, '含「年度分布」区块')
        check('全库区间' in out, '含「全库区间」区块')
        check('按客户' in out and '成交明细' in out, '含「按客户」与「成交明细」表')
        check('全量明细' in out, 'scope 标签显示「全量明细」', 'admin')
        check('<th>销售员</th>' in out, 'all 口径明细带销售员列')
        ychips = re.findall(r"priceQueryYearSet\('(\w+)'\)", out)
        check(ychips == ['all', '2024', '2025', '2026'], '年份四档渲染', str(ychips))
        check('3902' in out, '「全部」档显示 2024-2026 总条数（year_options）')
        check(str(admin_res['sku_by_year']['2024']['deal_count']) in out, '年度分布显示 2024 成交次数')
        for y in ('2024', '2025', '2026'):
            check(y in out, '年度分布含 %s' % y)

        # ② 普通销售 / 2024 —— UI 层不得出现其他客户名
        out2 = render(panel_js, sales_res, SALES, 'sales', kw=TARGET, year='2024')
        check('仅自己客户' in out2, 'scope 标签显示「仅自己客户」')
        check('<th>销售员</th>' not in out2, 'own 口径明细不带销售员列')
        leaked = [c for c in others if c and c in out2]
        check(not leaked, 'own 口径渲染结果无其他客户名（候选 %d 家）' % len(others),
              ('泄漏 %s' % leaked[:3]) if leaked else '干净')
        check(all(c in out2 for c in mine_custs) if mine_custs else True,
              'own 口径渲染结果含本人客户', '、'.join(sorted(mine_custs)) or '本人该年该货号 0 条')
        # 年份切换后仍是 own
        y = '2026'
        s6, c6 = price_api.query({'name': SALES, 'role': 'sales'}, TARGET, year=y)
        out3 = render(panel_js, s6, SALES, 'sales', kw=TARGET, year=y)
        check(('仅自己客户' in out3) and ('<th>销售员</th>' not in out3), '%s 年 own 口径渲染一致' % y)
        lk3 = [c for c in others if c and c in out3]
        check(not lk3, '%s 年 own 口径渲染无其他客户名' % y, (str(lk3[:2]) if lk3 else '干净'))
        print()

    print('%s' % ('✅ 全部断言通过' if not FAILS else '❌ 失败 %d 条：%s' % (len(FAILS), FAILS)))
    return 1 if FAILS else 0


if __name__ == '__main__':
    sys.exit(main())
