import ast,base64,hashlib,importlib.util,json,os,shutil,sys,tarfile,io
from pathlib import Path
ROOT=Path(__file__).resolve().parent;REPO=Path('/workspace/contextra-analytics-runtime-lifecycle-refactor')
REMOTE='/storage/contextra/maintenance/r5-notification-20261101-r01'
os.umask(0o077)
sp=importlib.util.spec_from_file_location('remote',ROOT.parent/'2026-11-01_icerti-fight-r5_r01/remote.py');r=importlib.util.module_from_spec(sp);sp.loader.exec_module(r)
if sys.argv[1]=='package':
 d=json.loads((ROOT/'preflight.json').read_text());pkg=ROOT/'package';payload=pkg/'payload';payload.mkdir(parents=True,exist_ok=False)
 before={};after={}
 for name,encoded in d['files'].items():
  raw=base64.b64decode(encoded);before[name]=hashlib.sha256(raw).hexdigest();text=raw.decode()
  if name=='app/fight_r5/runtime.py':text=(REPO/name).read_text()
  elif name=='app/fight_runtime.py':
   needle='    def clear(self): self.engine.clear();self._events.clear()'
   assert text.count(needle)==1
   text=text.replace(needle,needle+"\n\n    def notification_status(self):\n        return self.engine.notification_status() if self.config.engine=='r5' else None")
  else:
   needle='artifacts=self.artifacts.status() if self.artifacts else None,'
   assert text.count(needle)==1 and 'self.fight' in text
   text=text.replace(needle,needle+'\n                fight_notification=self.fight.notification_status() if self.fight else None,')
  ast.parse(text);p=payload/name;p.parent.mkdir(parents=True,exist_ok=True);p.write_text(text);after[name]=hashlib.sha256(p.read_bytes()).hexdigest()
 name='app/fight_r5/notification.py';shutil.copy2(REPO/name,payload/name);after[name]=hashlib.sha256((payload/name).read_bytes()).hexdigest()
 (pkg/'manifest.json').write_text(json.dumps(dict(server='icerti-ai2',images=d['images'],before=before,after=after)))
 for name in ('artifact_install.py','trace_install.py'):shutil.copy2(REPO/'tools/icerti_offline'/name,pkg/name)
 shutil.copy2(ROOT/'install.py',pkg/'install.py')
 shutil.copytree(REPO/'tests',pkg/'tests',ignore=shutil.ignore_patterns('__pycache__'))
 stream=io.BytesIO()
 with tarfile.open(fileobj=stream,mode='w') as t:t.add(pkg,arcname='.')
 blob=stream.getvalue();digest=hashlib.sha256(blob).hexdigest();incoming='/home/si/r5-notification-20261101-r01.tar'
 r.run(['python3','-c','import sys;from pathlib import Path;p=Path('+repr(incoming)+');assert not p.exists();p.write_bytes(sys.stdin.buffer.read());p.chmod(0o600)'],payload=blob)
 code='import hashlib,tarfile;from pathlib import Path;p=Path('+repr(incoming)+');assert hashlib.sha256(p.read_bytes()).hexdigest()=='+repr(digest)+';d=Path('+repr(REMOTE)+');assert not d.exists();d.mkdir(mode=0o700);tarfile.open(p).extractall(d,filter="data")'
 r.python(code,sudo=True)
 (ROOT/'transfer.json').write_text(json.dumps(dict(sha256=digest,remote=REMOTE)))
 print('PACKAGE_TRANSFER_PASS')
else:
 action=sys.argv[1];assert action in ('prepare','build','test','activate','verify')
 out=r.run(['python3',REMOTE+'/install.py',REMOTE,action],sudo=True,timeout=420)
 (ROOT/(action+'.log')).write_bytes(out);print(out.decode()[-1500:])
