import importlib.util
import csv
import json
from pathlib import Path
import subprocess
import time

R=Path(__file__).parent
s=importlib.util.spec_from_file_location('ops',R/'ops.py');o=importlib.util.module_from_spec(s);s.loader.exec_module(o)
plan=o.read('plan.json');built=o.read('fix-build.json');data=o.read('fix-window.json')
before=json.loads(Path('/tmp/codex/ltb1021/2026-09-15_contextra-channel-compare_r01/two.json').read_text())
rows=[]
for label,d in [('before',before),('after',data)]:
    for src in o.CHANNELS:
        a=d['samples'][0]['channels'][src];b=d['samples'][-1]['channels'][src];dt=b['at']-a['at']
        am,bm=a['state']['metadata_path'],b['state']['metadata_path'];du=bm['performance']['durations']
        row=dict(condition=label,source=src,seconds=dt)
        for stage in ['decoded','selected','infer','native','result','publisher_submit']:
            row[stage+'_fps']=(bm['performance']['counts'].get(stage,0)-am['performance']['counts'].get(stage,0))/dt
        row['redis_fps']=(b['redis']['entries']-a['redis']['entries'])/(b['redis']['at']-a['redis']['at'])
        queues=[x['channels'][src]['state']['metadata_path']['queued'] for x in d['samples']]
        row.update(queue_min=min(queues),queue_max=max(queues),queue_start=queues[0],queue_end=queues[-1])
        for key in ['stale','queue_drop','publisher_rejected']:row[key+'_delta']=bm.get(key,0)-am.get(key,0)
        for key in ['tracking','tracking_cpu','scenario','result_work','queue']:
            row[key+'_mean_ms']=du[key]['mean_ms'];row[key+'_p95_ms']=du[key]['p95_ms']
        row['publisher_queue_max']=max(x['publisher']['metrics']['queued'] for x in d['samples'])
        rows.append(row)
with (R/'comparison.csv').open('w',newline='') as f:
    w=csv.DictWriter(f,fieldnames=list(rows[0]));w.writeheader();w.writerows(rows)
current=o.base.inspect('contextra-analytics-1');assert current['Image']==built['image']
assert current['HostConfig']['RestartPolicy']==plan['analytics']['HostConfig']['RestartPolicy']
assert sorted(current['Mounts'],key=lambda x:x['Destination'])==sorted(plan['analytics']['Mounts'],key=lambda x:x['Destination'])
assert dict(x.split('=',1) for x in current['Config']['Env'])==dict(x.split('=',1) for x in plan['analytics']['Config']['Env'])
assert o.sha(o.prior.REG)==plan['registry_hash']
sender=o.prior.ops.prior.prev.sender();assert sender['executionId']==plan['sender']['executionId']
assert o.base.inspect('contextra-redis-1')['Id']==plan['redis']
settings={src:o.api('/api/v1/streams/'+sid+'/settings') for src,sid in o.CHANNELS.items()}
assert all(settings[src]['configured']==plan['settings'][src]['configured'] for src in o.CHANNELS)
states={src:o.prior.state(sid) for src,sid in o.CHANNELS.items()}
assert all(x['state']=='running' and x['last_error'] is None and not x['metadata_path']['performance']['detail'] for x in states.values())
assert all(x['metadata_path']['performance']['tracking_profiles']==[] for x in states.values())
inv=o.base.inventory()
def without_analytics(items):
    return [x for x in items if 'contextra-analytics-1' not in str(x)] if isinstance(items,list) else {k:v for k,v in items.items() if 'contextra-analytics-1' not in k and 'contextra-analytics-1' not in str(v)}
assert without_analytics(inv)==without_analytics(plan['inventory'])
checks=[];events=set()
for src in o.CHANNELS:
    frames=[x['channels'][src]['redis']['frame'] for x in data['samples']]
    assert len({f['runId'] for f in frames})==1
    assert all(f['sourceId']==src and f['frameTime']['rtpClockRate']==90000 and f['frameTime']['syncStatus']=='ESTIMATED' for f in frames)
    error=max(abs(f['frameTime']['detectedAtWallMs']-f['frameTime']['startWallTimeMs']-f['frameTime']['rtpTimestamp']/90) for f in frames)
    assert error<=2
    for f in frames:
        for event in f.get('events',[]):events.add(event['eventId'])
    checks.append(dict(source=src,samples=len(frames),anchor_math_max_ms=error,objects_min=min(len(f['objects']) for f in frames),objects_max=max(len(f['objects']) for f in frames)))
summary=dict(rows=rows,states=states,image=built,sender_execution_preserved=True,settings_preserved=True,
    config_registry_preserved=True,other_containers_preserved=True,normal_restart=current['HostConfig']['RestartPolicy'],
    time_checks=checks,unique_events_in_sparse_samples=len(events),resources=data['resources'],
    measurement_limit='Different scenes and new runs; no exact speedup claimed. Earlier cProfile function times invalid and excluded.',
    blockers=['Two channels still below10FPS; active-class update wall/CPU subdivision remains.'],
    unexpected_probe_failure='cProfile concurrent ownership error reproduced offline; all cProfile removed from final source/image',
    cleanup='Normal stopped workers confirmed before final reflection. Both final streams intentionally retained running; final Stop not executed.')
o.save('summary.json',summary)
print(json.dumps(dict(status='PARTIAL_IMPROVEMENT',image=built['tag'],channels={k:v['state'] for k,v in states.items()},time_checks=checks,events=len(events)),ensure_ascii=False))
