"""Evaluate two independent causal engines on the same saved observations."""
import hashlib
import json
import os
from pathlib import Path
import resource
import statistics
import time
from engines import Engine
from features import Settings,bodies

clip=os.environ['CLIP'];cache=Path('/cache');previous=Path('/previous')/clip/'data'
dest=Path('/download')/clip/'data';dest.mkdir(parents=True,exist_ok=True)
summary_path=dest/'engine_summary.json'
if summary_path.exists():
    raise RuntimeError('Existing completed evaluation protected; render/reuse it instead')
check=json.loads((previous/'cache_validation.json').read_text());raw=(cache/'inputs/pose.json').read_bytes()
assert check['status']=='PASS' and hashlib.sha256(raw).hexdigest()==check['cache_sha256']
all_rows=json.loads(raw);selected=[all_rows[i] for i in check['selected_samples']]
chosen={r['sample'] for r in selected};start=selected[0]['seconds']-3.
rows=[r for r in all_rows if start-1e-8<=r['seconds']<=selected[-1]['seconds']+1e-8]
assert all(b['seconds']>a['seconds'] and b['sample']>a['sample'] for a,b in zip(rows,rows[1:]))
cfg=Settings();engines={m:Engine(m,cfg) for m in ('A','B')};times={m:[] for m in engines};prep=[];outputs={m:[] for m in engines}
for row in rows:
    tick=time.perf_counter();prepared=bodies(row,cfg);prep.append((time.perf_counter()-tick)*1000)
    for mode,engine in engines.items():
        tick=time.perf_counter();result=engine.step(row,prepared);times[mode].append((time.perf_counter()-tick)*1000)
        assert all(p['input_end']==row['seconds'] and (p['input_start'] is None or p['input_start']<=row['seconds']) for p in result['pairs'])
        result['displayed']=row['sample'] in chosen;outputs[mode].append(result)

def stats(values):
    return dict(mean_ms=statistics.mean(values),p50_ms=statistics.median(values),p95_ms=statistics.quantiles(values,n=100,method='inclusive')[94],max_ms=max(values))

summary=dict(status='PASS',clip=clip,display_observations=len(selected),processed_observations=len(rows),
    preceding_observations=sum(r['sample'] not in chosen for r in rows),settings=cfg.payload(),
    cache_sha256=check['cache_sha256'],shared_preparation=stats(prep),max_rss_kib=resource.getrusage(resource.RUSAGE_SELF).ru_maxrss,modes={})
for mode,values in outputs.items():
    path=dest/(mode+'_diagnostics.jsonl')
    with path.open('w') as f:
        for value in values:f.write(json.dumps(value,separators=(',',':'))+'\n')
    shown=[r for r in values if r['displayed']]
    summary['modes'][mode]=dict(**stats(times[mode]),test_events_in_display=sum(len(r['events']) for r in shown),
        test_events_before_display=sum(len(r['events']) for r in values if not r['displayed']),
        current_condition_frames=sum(bool(r['current_rules']) for r in shown),display_highlight_frames=sum(r['episode']['display'] for r in shown),
        max_candidate_pairs=max(r['candidate_pairs'] for r in values),max_states=max(r['state_count'] for r in values),
        capped_pairs=sum(r['pairs_over_cap'] for r in values),unassigned_pairs=sum(r['unassigned_pairs'] for r in values),
        ambiguous_matches=sum(r['ambiguous_matches'] for r in values),diagnostics_sha256=hashlib.sha256(path.read_bytes()).hexdigest())
summary_path.write_text(json.dumps(summary,indent=2))
(dest/'input_contract.json').write_text(json.dumps(dict(selected_samples=check['selected_samples'],
    source_sha256=check.get('source_sha256'),cache_sha256=check['cache_sha256'],
    prior_rgb_hashes_sha256=hashlib.sha256((previous/'input_rgb_sha256.json').read_bytes()).hexdigest(),
    no_inference=True,no_tracking_reexecution=True),indent=2))
print(json.dumps(summary),flush=True)
