from pathlib import Path
import re,json,math,shutil,hashlib
out=Path('/home/jakub/devel/escapistsrereintroduced/docs/evidence/convoy-hq-coordination-2026-10-04')
base=Path('/tmp/erri-convoy-scene-native')
metrics={}
for label,name in [('baseline','baseline-combat'),('combat','final-combat'),('arrival-stall','qualified'),('final','arrival-fixed')]:
 run=base/name;session=next((run/'profile/logs').glob('logs_*'))
 text=(session/'console.log').read_text(errors='replace');events=(session/'rereintroduced-hq.log').read_text(errors='replace')
 cars={};teams={}
 for m in re.finditer(r'ERR CONVOY SCENE CAR: t=([\d.]+) id=(\d+) pos=<([^>]+)> goal=<([^>]+)> released=(\d)',text):
  t,i,xyz,goal,released=m.groups();x,y,z=map(float,xyz.split(','));cars.setdefault(int(i),[]).append((float(t),x,z,int(released)))
 for m in re.finditer(r'ERR CONVOY SCENE MAN: t=([\d.]+) team=(\d+) man=(\d+) pos=<([^>]+)> mounted=(\d)',text):
  t,i,n,xyz,mounted=m.groups();x,y,z=map(float,xyz.split(','));teams.setdefault(int(i),{}).setdefault(int(n),[]).append((float(t),x,z,int(mounted)))
 summary={}
 for i,members in teams.items():
  detail={}
  for n,samples in members.items():
   foot=[p for p in samples if p[0]>=(180 if label in ('final','arrival-stall') else 90) and not p[3]]
   detail[n]={'seen_on_foot':bool(foot),'first_on_foot_seconds':foot[0][0] if foot else None,'max_displacement_after_dismount_m':round(max((math.hypot(p[1]-foot[0][1],p[2]-foot[0][2]) for p in foot),default=0),1),'mounted_at_end':bool(samples[-1][3])}
  summary[i]=detail
 travel={}
 for i,samples in cars.items():
  before=[p for p in samples if p[0]<(180 if label in ('final','arrival-stall') else 90)];first=samples[0]
  travel[i]={'travel_max_displacement_m':round(max((math.hypot(p[1]-first[1],p[2]-first[2]) for p in before),default=0),1),'released_at_end':bool(samples[-1][3])}
 metrics[label]={'complete':'ERR SCENE COMPLETE' in text,'synthetic_initial_contact':True,'cars':travel,'teams':summary,'script_errors':[l for l in text.splitlines() if 'SCRIPT    (E)' in l],'stranded_events':[l for l in events.splitlines() if 'CONVOY STRANDED' in l]}
 shutil.copy2(session/'console.log',out/(label+'-console.log'))
 (out/(label+'-events.log')).write_text('\n'.join(l for l in events.splitlines() if any(w in l for w in ['CONVOY','flanking search','ORDER DELIVERED','ORDER QUEUED']))+'\n')
 if (run/'result.json').exists():shutil.copy2(run/'result.json',out/(label+'-result.json'))
 scripts={str(p.relative_to(run/'source')):hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted((run/'source/Scripts').rglob('*.c'))}
 (out/(label+'-script-sha256.json')).write_text(json.dumps(scripts,indent=2)+'\n')
(out/'metrics.json').write_text(json.dumps(metrics,indent=2)+'\n')
print(json.dumps({k:{'complete':v['complete'],'cars':v['cars'],'teams':{i:{'off':sum(m['seen_on_foot'] for m in members.values()),'max_displacement_m':max(m['max_displacement_after_dismount_m'] for m in members.values()),'end_aboard':sum(m['mounted_at_end'] for m in members.values())} for i,members in v['teams'].items()},'script_errors':len(v['script_errors'])} for k,v in metrics.items()},indent=2))
