# -*- coding: utf-8 -*-
"""开箱校验仿真：把 install.ps1 里所有「检查安装包自身文件」的语句抽出来，
逐个对照 zip 里的实际内容，避免出现「脚本要 zip、包里给的是目录」这类错配。

用法: python3 verify_preflight.py <安装包.zip> <顶层目录名>
"""
import re
import sys
import zipfile

pkg = sys.argv[1]
top = sys.argv[2].rstrip('/') + '/'

z = zipfile.ZipFile(pkg)
names = z.namelist()
files = {n[len(top):] for n in names if not n.endswith('/')}

# 判断某个「包内相对路径」是否存在（精确文件，或以它为前缀的目录）
def exists(rel):
    rel = rel.replace('/', '\\')
    if any(f.replace('/', '\\') == rel for f in files):
        return True
    pref = rel.rstrip('\\') + '\\'
    return any(f.replace('/', '\\').startswith(pref) for f in files)

ps1 = z.read(top + 'install.ps1')
text = (ps1[3:] if ps1[:3] == b'\xef\xbb\xbf' else ps1).decode('utf-8')

fails = []


def check(desc, good, extra=''):
    print(('  ✔ ' if good else '  ✖ ') + desc + (('  ' + extra) if extra else ''))
    if not good:
        fails.append(desc)


print('包：%s（%d 个文件）\n' % (pkg.split('/')[-1], len(files)))

print('=== 1) 脚本里 Test-Path "$Src\\..." 的检查项 ===')
paths = sorted(set(re.findall(r'Test-Path\s+"\$Src\\([^"]+)"', text)))
paths += sorted(set(re.findall(r"Test-Path\s+\(Join-Path\s+\$Src\s+'([^']+)'\)", text)))
for p in paths:
    p = p.replace('\\\\', '\\')
    check('需要存在：%s' % p, exists(p))

print('\n=== 2) 脚本里 Get-ChildItem "$Src\\thirdparty" -Filter 的检查项 ===')
# 区分两种语义：
#   必需 —— 取不到就 Fail（属于开箱校验，包内必须有）
#   可选 —— 取不到只是跳过该功能（如 LibreOffice，缺失时 Word→PDF 不可用但有提示）
filters = sorted(set(re.findall(r"Get-ChildItem\s+\"\$Src\\([^\"]+)\"\s+-Filter\s+'([^']+)'", text)))
for folder, flt in filters:
    folder = folder.replace('\\\\', '\\')
    pattern = '^' + re.escape(flt).replace(r'\*', '.*').replace(r'\?', '.') + '$'
    hits = [f for f in files
            if f.replace('/', '\\').startswith(folder.rstrip('\\') + '\\')
            and re.match(pattern, f.replace('/', '\\').split('\\')[-1], re.I)]

    # 看这行之后紧邻的几行有没有 Fail —— 有就是必需
    lines = text.split('\r\n')
    required = False
    for i, ln in enumerate(lines):
        if re.search(r"-Filter\s+'%s'" % re.escape(flt), ln):
            window = '\r\n'.join(lines[i:i + 4])
            if 'Fail' in window:
                required = True
            break

    detail = ('命中：' + ', '.join(h.split('/')[-1] for h in hits[:3])) if hits else '包内没有匹配文件'
    if hits:
        check('%s\\%s' % (folder, flt), True, detail)
    elif required and '-or' not in window:
        check('%s\\%s（必需）' % (folder, flt), False, detail)
    else:
        print('  ○ 可选（缺失有兜底 / 与其它形态二选一）：%s\\%s' % (folder, flt))

print('\n=== 2b) Win7 开箱校验的语义断言（对照脚本真实逻辑）===')
# 脚本里的实际判断：app\\Rmsys.Api.exe 必须存在；MySQL 允许「预解压目录 或 压缩包」
check('app\\Rmsys.Api.exe 存在', exists('app\\Rmsys.Api.exe'))
mysql_dir = exists('thirdparty\\mysql\\bin\\mysqld.exe')
mysql_zip = any(f.endswith('.zip') and 'mysql' in f.lower() and 'thirdparty' in f.lower() for f in files)
check('MySQL 二选一满足（目录 或 压缩包）', mysql_dir or mysql_zip,
      '目录 %s / 压缩包 %s' % ('有' if mysql_dir else '无', '有' if mysql_zip else '无'))

print('\n=== 3) 兜底用的目录/文件是否齐备 ===')
for must in ['app/Rmsys.Api.exe', 'sql/schema.sql', '说明.txt', '安装.bat', 'uninstall.ps1']:
    check(must, exists(must))

print('\n结果：%s' % ('全部满足 ✔' if not fails else '有 %d 项不满足 ✖' % len(fails)))
sys.exit(0 if not fails else 1)
