"""Scoped site package and staged installer; never call camera Start or rollback."""
import ast,base64,hashlib,importlib.util,json,os,shutil,sys,tarfile,io,subprocess,difflib
from pathlib import Path
ROOT=Path(__file__).resolve().parent;REPO=Path('/workspace/contextra-analytics-runtime-lifecycle-refactor')
REMOTE='/storage/contextra/maintenance/fall-r2-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)

def replace_once(text,old,new):
    assert text.count(old)==1,old[:100]
    return text.replace(old,new,1)

def site_fall(text):
    text=text.replace('direct_down_cooldown_seconds=None, contains=None):','direct_down_cooldown_seconds=None):')
    text=text.replace('        self.contains = contains\n','')
    text=text.replace('self.config=config;self.contains=contains;self._events={}','self.config=config;self._events={}')
    text=text.replace('R2Runtime(config,event_id_factory,wall_ns,contains)','R2Runtime(config,event_id_factory,wall_ns)')
    return text.replace('selected = tuple(i for i, point in enumerate(points)\n                         if (self.contains(point) if self.contains else inside(point, self.config.roi)))',
                        'selected = tuple(i for i, point in enumerate(points) if inside(point, self.config.roi))')

def package():
    d=json.loads((ROOT/'preflight.json').read_text());pkg=ROOT/'package';payload=pkg/'payload';payload.mkdir(parents=True,exist_ok=False)
    before={};after={};diffs=[]
    for name,encoded in d['files'].items():
        raw=base64.b64decode(encoded);before[name]=hashlib.sha256(raw).hexdigest();old=raw.decode();text=old
        head=subprocess.check_output(['git','show','HEAD:'+name],cwd=REPO).decode()
        if name=='app/fall_runtime.py':
            assert site_fall(head)==old
            text=site_fall((REPO/name).read_text())
        elif name=='app/scenario_parameters.py':
            assert head==old;text=(REPO/name).read_text()
        elif name=='app/secure_control.py':
            text=replace_once(text,'    from app.fight_r5.config import R5ConfigError','    from app.fight_r5.config import R5ConfigError\n    from app.fall_r2.config import R2ConfigError')
            text=replace_once(text,'(ControlError, R5ConfigError)','(ControlError, R5ConfigError, R2ConfigError)')
        elif name=='app/stream_settings.py':
            text=replace_once(text,'            configured = FallSettings.parse(dict(current, **value)).settings_payload()',
                "            merged=dict(current, **value)\n            if value.get('engine')=='legacy':merged.pop('r2',None)\n            configured = FallSettings.parse(merged).settings_payload()")
        else:
            line=next(line for line in text.splitlines() if 'fight_notification=self.fight.notification_status()' in line)
            text=replace_once(text,line,line+'\n'+line.replace('fight_notification=self.fight.notification_status() if self.fight','fall_notification=self.fall.notification_status() if self.fall'))
        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();diffs.extend(difflib.unified_diff(old.splitlines(True),text.splitlines(True),fromfile=name+'.before',tofile=name))
    for p in (REPO/'app/fall_r2').iterdir():
        if p.suffix not in ('.py','.json'):continue
        name=str(p.relative_to(REPO));dest=payload/name;dest.parent.mkdir(exist_ok=True);shutil.copy2(p,dest);after[name]=hashlib.sha256(dest.read_bytes()).hexdigest()
    (pkg/'manifest.json').write_text(json.dumps(dict(server='icerti-ai2',images=d['images'],before=before,after=after,config_sha256={k:v['sha256'] for k,v in d['configs'].items()}),indent=2))
    (ROOT/'site.patch').write_text(''.join(diffs))
    for name in ('artifact_install.py','trace_install.py'):
        shutil.copy2(REPO/'tools/icerti_offline'/name,pkg/name)
    # Existing import smoke is also CPU-only; no dependency or image configuration change.
    p=pkg/'artifact_install.py';p.write_text(p.read_text().replace("['docker','run','--rm','--pull=never','--name'","['docker','run','--rm','--runtime=runc','--pull=never','--name'"))
    for name in ('install.py','fall_r2_settings.py','test_fall_r2_site.py'):shutil.copy2(ROOT/name,pkg/name)
    shutil.copytree(REPO/'tests',pkg/'tests',ignore=shutil.ignore_patterns('__pycache__','*.pyc'))
    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/fall-r2-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)
    r.python('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")',sudo=True)
    (ROOT/'transfer.json').write_text(json.dumps(dict(sha256=digest,remote=REMOTE)))
    print('PACKAGE_TRANSFER_PASS',len(after))

if __name__=='__main__':
    action=sys.argv[1]
    if action=='package':package()
    else:
        assert action in ('prepare','build','test','activate','verify')
        try:
            out=r.run(['python3',REMOTE+'/install.py',REMOTE,'icerti-ai2',action],sudo=True,timeout=420)
            (ROOT/(action+'.log')).write_bytes(out);print(out.decode()[-2000:])
        except Exception as e:
            (ROOT/(action+'-failed.log')).write_text(str(e));raise
