import json,statistics
from pathlib import Path
p=Path(__file__).parent;d=json.loads((p/'observation.json').read_text());a=d['samples'][0];b=d['samples'][-1];dt=d['elapsed_s']
start={x['camera']:x for x in a['channels']};out=[]
def delta(x,y,k):return y.get(k,0)-x.get(k,0)
for row in sorted(b['channels'],key=lambda r:int(r['camera'])):
    old=start[row['camera']];m=row['metadata_path'];n=old['metadata_path'];c=m['performance']['counts'];oc=n['performance']['counts']
    providers={}
    for k,v in m['providers'].items():
        ov=n['providers'][k];responses=delta(ov,v,'responses');late=delta(ov,v,'late')
        providers[k]=dict(fps=round(responses/dt,2),accepted_fps=round((responses-late)/dt,2),late_pct=round(100*late/responses,1) if responses else None,responses=responses,late=late)
    out.append(dict(camera=row['camera'],state=row['state'],same_run=row['run_id']==old['run_id'],
      decoded=round(delta(oc,c,'decoded')/dt,2),selected=round(delta(oc,c,'selected')/dt,2),
      sampling_drop=delta(oc,c,'sampling_drop'),providers=providers,
      metadata=round(delta(n,m,'finalized')/dt,2),evicted=delta(n,m,'frame_evicted'),
      accepted=delta(n,m,'accepted_result'),duplicate=delta(n,m,'duplicate_or_unrequired_result'),
      events=delta(n,m,'events_created'),native_drop=delta(n.get('artifacts') or {},m.get('artifacts') or {},'native_drop'),
      capture_drop=delta(n.get('artifacts') or {},m.get('artifacts') or {},'metadata_queue_drop')))
models={}
for k,t1 in d['triton_after'].items():
    t0=d['triton_before'][k]
    if 'inference_stats' not in t1 or 'inference_stats' not in t0:continue
    ds={}
    for stage,v in t1['inference_stats'].items():
        q=t0['inference_stats'][stage];n=delta(q,v,'count');ns=delta(q,v,'ns')
        ds[stage]=dict(count=n,mean_ms=round(ns/n/1e6,2) if n else None)
    models[k]=dict(request_fps=round(ds['success']['count']/dt,2),stats=ds,
      inference_count=delta(t0,t1,'inference_count'),execution_count=delta(t0,t1,'execution_count'))
gpu=[[float(x) for x in s['gpu'].split(',')] for s in d['samples']]
cpu0=[int(x) for x in a['cpu'].split()[1:9]];cpu1=[int(x) for x in b['cpu'].split()[1:9]];cpu=[y-x for x,y in zip(cpu0,cpu1)]
summary=dict(seconds=round(dt,2),channels=out,triton=models,
 publisher_delta={k:delta(a['publisher'],b['publisher'],k) for k in ('sent_frames','sent_status','replaced','expired','send_error','byte_limit','message_limit','stale_generation_or_seq')},
 publisher_end={k:b['publisher'].get(k) for k in ('queued','queuedBytes','inflightBytes','workers')},
 gpu_util=dict(min=min(x[0] for x in gpu),mean=round(statistics.mean(x[0] for x in gpu),1),max=max(x[0] for x in gpu)),
 gpu_mem_mib=dict(min=min(x[2] for x in gpu),max=max(x[2] for x in gpu),total=gpu[0][3]),
 host_cpu_busy_pct=round(100*(sum(cpu)-cpu[3]-cpu[4])/sum(cpu),1),host_cpu_cores=d['cpu_cores'],
 cpu_pressure=b['cpu_pressure'],memory_pressure=b['memory_pressure'],memory=b['meminfo'],
 docker_before=d['docker_before'],docker_after=d['docker_after'],artifact_end=b['artifacts'])
(p/'summary.json').write_text(json.dumps(summary,indent=2))
print(json.dumps(summary,indent=2))
