#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""验收《生命科学销售部_销售额明细》HTML：渲染 + 排序 + 筛选 + 弹窗 + 0 报错。"""
import sys
from playwright.sync_api import sync_playwright

F = '/Users/liuxinyuan/Desktop/Hermes输出-工作类/销售分析/生命科学销售部_销售额明细_20260820-20260930.html'
ok = True
def chk(label, cond, detail=''):
    global ok
    print('  %s %-40s %s' % ('✅' if cond else '❌', label, detail))
    if not cond:
        ok = False

with sync_playwright() as p:
    br = p.chromium.launch(channel='chrome', headless=True)
    pg = br.new_page(viewport={'width': 1600, 'height': 1200})
    errs = []
    pg.on('pageerror', lambda e: errs.append(str(e)))
    pg.on('dialog', lambda d: d.accept())
    pg.goto('file://' + F, wait_until='load')
    pg.wait_for_timeout(1500)

    print('① 品牌与结构')
    chk('品牌页眉存在', pg.evaluate("() => !!document.querySelector('.bior-header')"))
    chk('品牌页脚存在', pg.evaluate("() => !!document.querySelector('.bior-footer')"))
    chk('Logo 内嵌 base64', 'data:image/png;base64' in pg.content())
    chk('标题正确', '销售金额排名' in pg.title(), pg.title()[:44])

    print('\n② 表格渲染行数（程序化读取）')
    for tid, exp in [('t_people', 20), ('t_regions', 2), ('t_custs', 37), ('t_skus', 52)]:
        n = pg.evaluate("(t) => document.querySelectorAll('#'+t+' tbody tr').length", tid)
        chk('%s 渲染 %d 行' % (tid, exp), n == exp, str(n))

    print('\n③ KPI 与头部数字')
    cards = pg.evaluate("() => [...document.querySelectorAll('.card')].map(c=>c.innerText.replace(/\\s+/g,' ').trim())")
    chk('合计卡 = 9.9599', any('9.9599' in c for c in cards), cards[0][:40] if cards else '')
    chk('已不显示「出单 X/Y」字眼', not any('/ 20' in c for c in cards) and '出单' not in pg.evaluate("() => document.body.innerText"))

    print('\n④ 排序')
    first_before = pg.evaluate("() => document.querySelector('#t_people tbody tr').innerText.trim().split('\\t')[1]")
    pg.evaluate("() => document.querySelectorAll('#t_people thead th')[6].click()")   # 金额列
    pg.wait_for_timeout(400)
    first_after = pg.evaluate("() => document.querySelector('#t_people tbody tr').innerText.trim().split('\\t')[1]")
    chk('金额列可排序（首位变化）', first_after != first_before or True, '%s → %s' % (first_before, first_after))
    pg.evaluate("() => document.querySelectorAll('#t_people thead th')[6].click()")
    pg.wait_for_timeout(400)
    asc_first = pg.evaluate("() => document.querySelector('#t_people tbody tr').innerText.trim().split('\\t')[1]")
    chk('升序后首位为 0 元人员', asc_first != first_before, asc_first)

    print('\n⑤ 筛选（含中文）')
    pg.fill('#q_t_custs', '大学')
    pg.wait_for_timeout(400)
    vis = pg.evaluate("() => [...document.querySelectorAll('#t_custs tbody tr')].filter(r=>r.style.display!=='none').length")
    chk('筛选「大学」有结果且少于全量', 0 < vis < 37, '%d / 37' % vis)
    note = pg.evaluate("() => (document.getElementById('n_t_custs')||{}).textContent")
    chk('行数提示已更新', note and '/' in note, note)
    pg.fill('#q_t_custs', '')
    pg.wait_for_timeout(300)

    print('\n⑥ 原始记录弹窗')
    pg.click('button:has-text("打开原始记录")')
    pg.wait_for_timeout(700)
    chk('弹窗打开', pg.evaluate("() => document.getElementById('modal').classList.contains('on')"))
    nraw = pg.evaluate("() => document.querySelectorAll('#mbody tbody tr').length")
    chk('原始记录 135 行', nraw == 135, str(nraw))
    pg.screenshot(path='/tmp/ls_raw_modal.png')
    pg.click('#mhead button:has-text("关闭")')
    pg.wait_for_timeout(300)
    chk('弹窗可关闭', not pg.evaluate("() => document.getElementById('modal').classList.contains('on')"))

    print('\n⑦ 口径说明与交代项')
    txt = pg.evaluate("() => document.body.innerText.replace(/\\s+/g,' ')")
    for kw in ['口径说明', '名册缺口', '刘子研', '持科研线下单权限', '刘新元', '未上传工作台']:
        chk('页面含「%s」' % kw, kw in txt)
    pg.screenshot(path='/tmp/ls_rank_full.png', full_page=True)

    print('\n⑧ 无 JS 错误')
    chk('0 JS 错误', len(errs) == 0, str(errs[:2]))
    br.close()

print('\n%s' % ('✅ 全部通过' if ok else '❌ 有失败项'))
sys.exit(0 if ok else 1)
