import copy, fcntl, hashlib, json, math, os, shutil, signal, subprocess, sys, tarfile, tempfile, time, traceback
from pathlib import Path
from urllib.parse import quote
import yaml
os.umask(0o077)
R=Path(__file__).parent;R.chmod(0o700)
REPO=Path('/workspace/contextra-analytics-runtime-lifecycle-refactor');sys.path.insert(0,str(REPO))
from tools.stream_benchmark import load_config, preflight, Benchmark
from tools.stream_benchmark_runtime import Runtime, file_hash
OLD=Path('/workspace/contextra-runtime-config/test-run/pose8-fp16-tracking.yaml')
NEW=Path('/workspace/contextra-runtime-config/test-run/fire8-fp16-tracking.yaml')
TARGET=Path('/data/contextra/model-artifacts/deployments/fire-fp16-6367102/RFDETRFireSmoke')
CAND=Path('/data/contextra/model-artifacts/candidates/batch8-6367102')
ACTIVE=Path('/workspace/contextra-runtime-config/metadata-active/compose.yaml')
ENV=Path('/workspace/contextra-runtime-config/runtime.env')
SOURCES={
 'RFDETRFireSmoke':Path('/data/contextra/model-artifacts/builds/fire-smoke-193-20260913-r01/model.onnx'),
 'RFDETRMedium':Path('/data/contextra/model-artifacts/builds/medium-193-20260909-v4/export/model.onnx'),
 'RFDETRKeypointPreview':Path('/tmp/codex/ltb1021/2026-09-13_contextra-pose_r01/export/rfdetr-keypoint-preview.onnx')}
def save(n,v):
    p=R/n;p.parent.mkdir(parents=True,exist_ok=True);p.write_text(json.dumps(v,ensure_ascii=False,indent=2,allow_nan=False));p.chmod(0o600)
def read(n):return json.loads((R/n).read_text())
def cmd(a,t=30):
    p=subprocess.run(a,capture_output=True,text=True,timeout=t)
    if p.returncode:
        save('command-error-'+str(time.time_ns())+'.json',dict(command=a,code=p.returncode,stderr=p.stderr[-4000:]))
        raise RuntimeError('Command failure; private evidence retained')
    return p.stdout
def gate():
    assert not (R/'plan.json').exists() and not NEW.exists() and not TARGET.exists() and not CAND.exists()
    cfg=load_config(OLD);rt=Runtime(cfg,R);p=preflight(rt);assert len(p['rows'])==16
    inspect=rt.inspect();model_configs={n:rt.triton('v2/models/'+n+'/config') for n in SOURCES}
    model_files={}
    for m in inspect['Mounts']:
        if m['Destination'].startswith('/opt/contextra/models/triton/'):
            model_files.update({str(f):file_hash(f) for f in Path(m['Source']).rglob('*') if f.is_file()})
    for path,name in ((ACTIVE,'active.before'),(ENV,'env.before'),(Path(cfg['registration_file']),'registry.before'),(OLD,'benchmark.before')):shutil.copy2(path,R/name)
    p.update(config=cfg,inspect=inspect,head=cmd(['git','-C',str(REPO),'rev-parse','HEAD']).strip(),started=time.time(),
        active_hash=file_hash(ACTIVE),env_hash=file_hash(ENV),model_configs=model_configs,model_files=model_files,
        onnx_hashes={n:file_hash(f) for n,f in SOURCES.items()},
        sender=json.loads(cmd(['python3',str(REPO/'tools/rtsp_test.py'),'list'])),redis_id=json.loads(cmd(['docker','inspect','contextra-redis-1']))[0]['Id'],
        environment=cmd(['docker','exec',rt.container,'dpkg-query','-W','libnvinfer10','libnvinfer-plugin10']),
        gpu=cmd(['nvidia-smi','--query-gpu=name,uuid,driver_version,memory.total','--format=csv,noheader']),
        caps=dict(engine_seconds=900,image_seconds=900,free_disk_gib=15,conversion_cpu=4,conversion_ram_gib=12,quality_checks=False),
        presets={str(f):file_hash(f) for f in Path('/workspace/contextra-runtime-config/test-run/presets').rglob('*.yaml')})
    save('plan.json',p);(R/'unit').mkdir();print('Gate PASS: Pose3 running, sixteen identities fixed',flush=True)
def bounded(a,name,seconds):
    assert not (R/(name+'-start.json')).exists()
    save(name+'-start.json',dict(command=a,at=time.time()))
    with (R/(name+'.log')).open('x') as log:
        p=subprocess.Popen(a,stdout=log,stderr=log,start_new_session=True);start=time.monotonic()
        while p.poll() is None:
            mem={k:int(v.split()[0]) for k,v in (line.split(':',1) for line in Path('/proc/meminfo').read_text().splitlines())}
            gpu=int(cmd(['nvidia-smi','--query-gpu=memory.used','--format=csv,noheader,nounits']).splitlines()[0])
            if time.monotonic()-start>seconds or shutil.disk_usage('/var/lib/docker').free<15*1024**3 or mem['MemAvailable']<8*1024**2 or gpu>=40000:
                if '--name' in a:cmd(['docker','stop','-t','5',a[a.index('--name')+1]])
                else:os.killpg(p.pid,signal.SIGTERM)
                p.wait(20);raise RuntimeError('Resource/time bound')
            time.sleep(2)
    assert p.returncode==0,'Build/convert failed; no retry'
    return time.monotonic()-start
def engine(model='RFDETRFireSmoke',batch=4):
    batch=int(batch);name=f'{model}-b{batch}';source=SOURCES[model]
    assert file_hash(source)==read('plan.json')['onnx_hashes'][model]
    formats='fp32:chw,fp32:chw'+(',fp32:chw' if model=='RFDETRKeypointPreview' else '')
    a=['docker','run','--rm','--pull','never','--name','codex-'+name.lower(), '--network','none','--gpus','device=0',
       '--cpus','4','--memory','12g','--memory-swap','12g','--ulimit','core=0',
       '-v',str(source)+':/input/model.onnx:ro','-v',str(R)+':/evidence','--entrypoint','timeout',read('plan.json')['image'],
       '900','/usr/bin/trtexec','--onnx=/input/model.onnx','--saveEngine=/evidence/'+name+'.plan','--fp16',
       '--inputIOFormats=fp16:chw','--outputIOFormats='+formats,'--minShapes=input:1x3x576x576',
       f'--optShapes=input:{batch}x3x576x576',f'--maxShapes=input:{batch}x3x576x576','--memPoolSize=workspace:1024','--skipInference']
    elapsed=bounded(a,name,920)
    save(name+'.json',dict(model=model,sha256=file_hash(R/(name+'.plan')),elapsed=elapsed,profile=[1,batch,batch],input='FP16',output='FP32',source_hash=file_hash(source),quality='not evaluated'))
    print('Engine ready:',name,flush=True)
def image():
    paths=cmd(['git','-C',str(REPO),'diff','--name-only']).splitlines()+cmd(['git','-C',str(REPO),'ls-files','--others','--exclude-standard']).splitlines()
    hashes={p:file_hash(REPO/p) for p in paths};digest=hashlib.sha256(json.dumps(hashes,sort_keys=True).encode()).hexdigest()[:8]
    tag='contextra-analytics:fire-fp16-6367102-'+digest;snapshot=R/'snapshot';snapshot.mkdir()
    child=subprocess.Popen(['git','-C',str(REPO),'archive',read('plan.json')['head']],stdout=subprocess.PIPE)
    with tarfile.open(fileobj=child.stdout,mode='r|') as t:t.extractall(snapshot,filter='data')
    assert child.wait()==0
    for p in paths:(snapshot/p).parent.mkdir(parents=True,exist_ok=True);shutil.copy2(REPO/p,snapshot/p)
    save('snapshot.json',dict(tag=tag,hashes=hashes))
    cwd=os.getcwd();os.chdir(snapshot)
    try:elapsed=bounded(['docker','build','--pull=false','--network=default','-f','docker/Dockerfile','--target','app','-t',tag,'.'],'image',900)
    finally:os.chdir(cwd)
    ident=json.loads(cmd(['docker','image','inspect',tag]))[0]['Id']
    save('image.json',dict(image=ident,tag=tag,hashes=hashes,elapsed=elapsed));print('Image ready:',tag,flush=True)
if __name__=='__main__':globals()[sys.argv[1]](*sys.argv[2:])
