import hashlib
import importlib.util
import json
import os
import pathlib
import shutil
import subprocess
import time

os.umask(0o077)
R = pathlib.Path(__file__).parent
R.chmod(0o700)
for p in R.iterdir():
    if p.is_file():
        p.chmod(0o600)
spec = importlib.util.spec_from_file_location('gate', '/tmp/codex/ltb1021/2026-09-11_service-stop_r01/gate.py')
g = importlib.util.module_from_spec(spec)
spec.loader.exec_module(g)
assert not (R/'ledger.json').exists(), 'experiment already recorded'
c = g.inspect()
assert c['State']['Status'] == 'running' and c['State']['Health']['Status'] == 'healthy'
assert c['Image'] == 'sha256:4b66199af0d3ce1b6528e8b6857a6064ed2e80db599e710cf374aacbbd315fca'
assert g.sh(['git','-C',str(g.REPO),'rev-parse','HEAD']).strip() == '7a3daf972681af952a623df8e96cbd1a4adeb935'
model = g.CFG.parent
assert g.sha(g.CFG) == '5e5a4d22786f8b5218ac821379fbec091d9e3006f62c880ee3e3da5ced2f0e0b'
assert g.sha(model/'2/model.plan') == '5fdf1e9c55ab947a3b7d5f7f6bdceaf1e36a9e52efdf86f5a15d3b99b8631d1e'
overlay = pathlib.Path('/data/contextra/runtime-state/streams_runtime.yaml')
data = g.yaml.safe_load(overlay.read_text())
assert all(not s.get('autostart',False) for s in data['added'])
configs = pathlib.Path('/tmp/codex/ltb1021/2026-09-10_preprocess-isolation_r01/generated')
before = {'container':c['Id'], 'image':c['Image'], 'env_hash':g.sha(g.ENV), 'overlay_hash':g.sha(overlay),
          'model_hash':g.sha(model/'2/model.plan'), 'model_config_hash':g.sha(g.CFG),
          'others':g.others(), 'gpu_mib':g.gpu(), 'config_hashes':{p.name:g.sha(p) for p in configs.iterdir() if p.is_file()}}
assert before['gpu_mib'] < 16000
mem = {l.split(':')[0]:int(l.split()[1]) for l in pathlib.Path('/proc/meminfo').read_text().splitlines()}
assert mem['MemAvailable'] > 10*1024**2 and shutil.disk_usage(R).free > 2*1024**3
ledger = {'tool_start_utc':'2026-09-12T15:29:45Z','basic_runs':1,'contrast_runs':0,'model_load_budget_s':120,
          'warmup_s':5,'measure_s':15,'first_frame_s':30,'cleanup_s':60,'total_s':250,
          'cpu_quota':None,'ram_cap_gib':8,'gpu_delta_cap_mib':8192,'input':'synthetic_live_15fps_1920x1080',
          'retained':['custom_gpu_preprocess','custom_infer_processor','Flow.retrieve_RGB'],
          'removed':['RTP','identity','PTS_selection','Tracking','Redis','visual','service_manager'],
          'baseline':before}
(R/'ledger.json').write_text(json.dumps(ledger,indent=2))
name = 'codex-ltb1021-sdk-baseline-r01'
cmd = ['docker','run','--rm','--pull','never','--name',name,'--network','none','--gpus','device=0',
       '--memory','8g','--memory-swap','8g','--ulimit','core=0',
       '-e','NVIDIA_VISIBLE_DEVICES=0','-e','NVIDIA_DRIVER_CAPABILITIES=compute,video,utility',
       '-e','PYTHONDONTWRITEBYTECODE=1',
       '--mount',f'type=bind,src={model},dst=/models/RFDETRMedium,readonly',
       '--mount',f'type=bind,src={configs},dst=/configs,readonly',
       '--mount',f'type=bind,src={R},dst=/evidence',
       '--entrypoint','timeout',c['Image'],'240s','python3','/evidence/inside.py']
start = time.monotonic()
peak = before['gpu_mib']
samples = []
reason = None
with (R/'container.log').open('w') as log:
    proc = subprocess.Popen(cmd,stdout=log,stderr=subprocess.STDOUT)
    try:
        while proc.poll() is None:
            used = g.gpu()
            peak = max(peak,used)
            if used-before['gpu_mib'] > 8192 or time.monotonic()-start > 250:
                reason = 'resource_or_time_cap'
                break
            if len(samples) < 100:
                r = subprocess.run(['docker','stats','--no-stream','--format','{{.CPUPerc}}|{{.MemUsage}}',name],
                                   capture_output=True,text=True,timeout=5)
                samples.append({'t':time.monotonic()-start,'gpu_mib':used,'cpu_mem':r.stdout.strip()})
            time.sleep(2)
    finally:
        if proc.poll() is None:
            subprocess.run(['docker','kill',name],capture_output=True,timeout=10)
        proc.wait(timeout=15)
        subprocess.run(['docker','rm','-f',name],capture_output=True,timeout=10)
after = g.inspect()
result = {'elapsed_s':time.monotonic()-start,'exit':proc.returncode,'cap_reason':reason,
          'gpu_before_mib':before['gpu_mib'],'gpu_peak_mib':peak,'gpu_after_mib':g.gpu(),
          'analytics_unchanged':after['Id']==before['container'] and after['Image']==before['image'],
          'analytics_status':after['State']['Status'],'analytics_health':after['State']['Health']['Status'],
          'env_unchanged':g.sha(g.ENV)==before['env_hash'],'overlay_unchanged':g.sha(overlay)==before['overlay_hash'],
          'others_unchanged':g.others()==before['others'],'resource_samples':samples,
          'task_container_remaining':bool(g.sh(['docker','ps','-aq','--filter','name=^/'+name+'$']).strip())}
(R/'resources.json').write_text(json.dumps(result,indent=2))
print(json.dumps({k:v for k,v in result.items() if k != 'resource_samples'}))
for filename in ['result.json','probe.json']:
    if (R/filename).exists():
        print(filename, (R/filename).read_text())
