from ops import *
import yaml
image=json.loads((HERE/'image-d086494ab6.json').read_text())
assert inspect('contextra-analytics-1')['Image']==image['id']
for p,h in image['hashes'].items():
    assert hashlib.sha256((REPO/p).read_bytes()).hexdigest()==h
    assert cmd(['docker','exec','contextra-analytics-1','sha256sum','/opt/contextra/'+p]).split()[0]==h
base=json.loads((HERE/'baseline.json').read_text());expected=json.loads((PRIVATE/'compose-before.json').read_text())
expected['services']['analytics']['image']=image['id'];assert json.loads(cmd(compose()+['config','--format','json']))==expected
before=yaml.safe_load((PRIVATE/'runtime-original.yaml').read_text())
saved=json.loads((PRIVATE/'settings-original.json').read_text());deltas=[]
for camera,original in saved.items():
    spec=next(s for s in before['added'] if str(s.get('camera_id'))==camera)
    current=api('/api/v1/streams/'+spec['stream_id']+'/settings')['configured']
    a={k:v for k,v in original.items() if k!='revision'};b={k:v for k,v in current.items() if k!='revision'}
    if 'scenario' not in a['fire'] and 'scenario' in b['fire']:
        assert b['fire']['scenario']['enabled'] is False
        del b['fire']['scenario'];deltas.append(dict(camera=camera,change='inactive Fire scenario defaults materialized by CAS'))
    assert a==b,camera
for name,old in base['containers'].items():
    if name in ('contextra-analytics-1','contextra-ai-gateway-1'):continue
    c=inspect(name);assert c['Id']==old['id'] and c['Image']==old['image'] and c['State']['StartedAt']==old['started']
rows=api('/api/v1/streams')['streams']
assert all(r['state']=='stopped' and not r.get('child_pid') and not r.get('cleanup_pending') for r in rows)
assert not inspect('contextra-ai-gateway-1')['State']['Running']
code='''import urllib.request,json,os
from pathlib import Path
r=urllib.request.Request("http://localhost:8080/internal/bridge-status",headers={"Authorization":"Bearer "+Path(os.environ["CONTEXTRA_CONTROL_TOKEN_FILE"]).read_text().strip()})
print(json.dumps(json.load(urllib.request.urlopen(r,timeout=3))))'''
bridge=json.loads(cmd(['docker','exec','contextra-ai-control-sidecar-1','python3','-B','-c',code],timeout=6))
models=json.loads(cmd(['docker','exec','contextra-analytics-1','python3','-B','-c',
 'import urllib.request,json;print(json.dumps({m:urllib.request.urlopen("http://127.0.0.1:8000/v2/models/"+m+"/ready",timeout=3).status for m in ["RFDETRMedium","RFDETRKeypointPreview","RFDETRFireSmoke","Awiros_person_and_head_detection"]}))']))
a=api('/api/v1/metadata/status')['artifacts']
save('final-state.json',dict(image=image['tag'],image_id=image['id'],models=models,stopped=len(rows),provider_pids=[],
    gateway='stopped',wss=bridge['wss']['connected'],reason='high-load startup stall and forced cleanup; inbound Start held',
    artifact={k:a[k] for k in ('enabled','http_enabled')},restored_effective_settings=True,normalized_inactive_settings=deltas,
    diagnostics_collecting=False,non_target_preserved=True,source_hashes_match=True,compose_only_image_changed=True,
    gpu=cmd(['nvidia-smi','--query-gpu=memory.used,utilization.gpu','--format=csv,noheader']).strip()))
print((HERE/'final-state.json').read_text())
