import csv
import hashlib
import json
from pathlib import Path

R=Path(__file__).parent
def read(name):return json.loads((R/name).read_text())
plan=read('plan.json');window=read('window.json');final=read('final-reconciled.json' if (R/'final-reconciled.json').exists() else 'final.json');cleanup=read('cleanup.json')
samples=window['samples'];rows=[];totals={}
profile=read('publisher-profile.json')['metrics']['profile'] if (R/'publisher-profile.json').exists() else {}
if len(samples)>=2:
    a,b=samples[0],samples[-1];dt=b['at']-a['at'];rd=0;pd=0
    for row in plan['channels']:
        sid=row['stream_id'];x,y=a['channels'][sid],b['channels'][sid]
        count=y['processed']-x['processed'];rd+=count
        r0,r1=x['redis'],y['redis'];available=not(r0.get('missing') or r1.get('missing'))
        published=r1['entries']-r0['entries'] if available else None
        if published is not None:pd+=published
        counts=profile.get('channels',{}).get(hashlib.sha256(row['source_id'].encode()).hexdigest(),{}).get('counts',{})
        aligned=True
        for item in (x,y):
            f=item['redis'].get('frame')
            if f:
                aligned &= (f['sourceId']==row['source_id'] and f['streamId']==sid and f['runId']==item['run_id']
                            and f['runGeneration']==item['generation'] and all(o.get('trackId') is None for o in f['objects']))
        rows.append(dict(channel=f"G{row['group']}-{row['copy']}",result_fps=count/dt,
            redis_fps=published/(r1['at']-r0['at']) if available else None,
            queue_min=min(s['channels'][sid]['queued'] for s in samples),queue_max=max(s['channels'][sid]['queued'] for s in samples),
            stale=y['stale']-x['stale'],drop=y['drop']-x['drop'],publisher_rejected=y['rejected']-x['rejected'],
            replaced=counts.get('replaced_by_frame',0),send_error=counts.get('send_error',0),
            stored=r1.get('length'),ttl_ms=r1.get('pttl'),identity_aligned=bool(aligned),
            observation_missed=y['observation_missed']-x['observation_missed']))
    aa,bb=a['publisher'],b['publisher'];ra,rb=a['resources'],b['resources']
    host_a,host_b=ra['host_cpu'],rb['host_cpu'];total=sum(host_b[:8])-sum(host_a[:8]);idle=(host_b[3]+host_b[4])-(host_a[3]+host_a[4])
    totals=dict(seconds=dt,result_frames=rd,redis_frames=pd,result_fps=rd/dt,redis_fps=pd/dt,
        publisher_delta={k:bb.get(k,0)-aa.get(k,0) for k in ('sent_frames','replaced','expired','send_error','byte_limit','message_limit')},
        publisher_queue=[min(s['publisher']['queued'] for s in samples),max(s['publisher']['queued'] for s in samples)],
        workers=[min(s['publisher']['workers'] for s in samples),max(s['publisher']['workers'] for s in samples)],
        analytics_cpu_cores=(rb['cpu']['usage_usec']-ra['cpu']['usage_usec'])/1e6/(rb['at']-ra['at']),
        host_cpu_percent=100*(total-idle)/total if total else None,
        memory_mib=[min(s['resources']['memory'] for s in samples)/1024**2,max(s['resources']['memory'] for s in samples)/1024**2],
        available_gib_min=min(s['resources']['available'] for s in samples)/1024**3,
        gpu_samples=[s['resources']['gpu'] for s in samples])
    with (R/'summary.csv').open('w') as f:
        writer=csv.DictWriter(f,fieldnames=list(rows[0]));writer.writeheader();writer.writerows(rows)
originals_same=all(final['states'].get(sid,{}).get('state')==v['state'] and final['states'].get(sid,{}).get('run_id')==v.get('run_id') for sid,v in plan['states'].items())
settings_same=final['original_settings']==plan['settings']
registry_same=final['registry']==plan['registry']
remaining=[sid for sid in plan['temporary'] if sid in final['states']]
result=dict(window=window['result'],rows=rows,totals=totals,original_runs_preserved=originals_same,settings_preserved=settings_same,
    registry_restored=registry_same,temporary_remaining=remaining,
    final_workers=final['publisher']['metrics']['workers'],final_channels=final['publisher']['metrics']['channels'],
    profile_off=not final['publisher']['metrics']['profile']['enabled'],
    sender_unchanged=final['sender']['executionId']==plan['sender']['executionId'],
    containers_unchanged=final['inventory']==plan['inventory'],cleanup=cleanup)
(R/('analysis-final.json' if (R/'final-reconciled.json').exists() else 'analysis.json')).write_text(json.dumps(result,indent=2));print(json.dumps({k:v for k,v in result.items() if k!='cleanup'},indent=2))
