"""Apply one verified c4070071 module; does not restart services or access DB."""
import argparse, hashlib, json, os, shutil, subprocess, tempfile
from pathlib import Path
from datetime import datetime
p=argparse.ArgumentParser()
p.add_argument('--app-root',default='/data/apps/foodsafe1')
p.add_argument('--node',default='/opt/foodsafe-node/bin/node')
a=p.parse_args()
kit=Path(__file__).resolve().parent
manifest=json.loads((kit/'manifest.json').read_text())
root=Path(a.app_root)
if not root.is_absolute():raise SystemExit('应用目录必须是绝对路径')
target=root/manifest['target']
if target.is_symlink() or not target.is_file():raise SystemExit('目标文件缺失或为符号链接，停止')
sha=lambda f:hashlib.sha256(f.read_bytes()).hexdigest()
payload=kit/'morning-check.service.js'
if sha(payload)!=manifest['patched_sha256']:raise SystemExit('补丁校验失败，停止')
current=sha(target)
if current==manifest['patched_sha256']:
 print('补丁已经应用，未重复修改；仍需核对运行进程和页面');raise SystemExit(0)
if current!=manifest['original_sha256']:raise SystemExit('当前模块不是已验证的c4070071原文件，停止，不覆盖')
subprocess.run([a.node,'--check',str(payload)],check=True)
backup=root/'.patch-backups'/('morning-images-'+datetime.now().strftime('%Y%m%d-%H%M%S-%f'))
backup.mkdir(parents=True,mode=0o700)
shutil.copy2(target,backup/target.name)
if sha(backup/target.name)!=current:raise SystemExit('备份校验失败，停止')
stat=target.stat()
fd,name=tempfile.mkstemp(prefix='.morning-patch-',dir=str(target.parent))
try:
 with os.fdopen(fd,'wb') as f:
  f.write(payload.read_bytes());f.flush();os.fsync(f.fileno())
 os.chmod(name,stat.st_mode & 0o777)
 os.chown(name,stat.st_uid,stat.st_gid)
 if sha(target)!=current:raise SystemExit('原文件在操作中发生变化，停止')
 os.replace(name,target)
finally:
 if os.path.exists(name):os.unlink(name)
(backup/'receipt.json').write_text(json.dumps({'target':str(target),'backup':str(backup/target.name),**manifest},indent=2)+'\n')
print('单模块补丁已安装，原文件备份：'+str(backup/target.name))
print('没有重启应用、修改数据库或.env。需定向重启foodsafe-1后验收。')
