"""Reproduce aggregate-only research figures: python3 generate-figures.py."""
from pathlib import Path
import json,html,textwrap
P=Path(__file__).resolve().parent
D=json.loads((P/'plot-data.json').read_text())
def svg(name,width):
 mobile=width==300; E=html.escape; body=[]; y=28; left=8; right=width-8; span=right-left
 def t(x,y,s,anchor='start',bold=False):body.append(f'<text x="{x}" y="{y}" text-anchor="{anchor}" font-weight="{600 if bold else 400}">{E(str(s))}</text>')
 title={'reading':'Reading supplied values','rule':'Executing one supplied step','news':'Historical cohort: 110 review slots','exit':'Correct choices in the two-exit follow-up','timing':'Recorded news-call duration'}[name]
 rows=D['rule_accuracy'] if name in ['reading','rule'] else D['news_equal_budget'] if name=='news' else D['exit_matrix'] if name=='exit' else D['news_call_latency']; desc=[]
 if name=='timing':t(left,y,'● Median    ◆ p90 · seconds');y+=36
 for i,r in enumerate(rows):
  if name=='timing' and i in [0,4]:
   for line in textwrap.wrap('October · hosted HTTP' if i==0 else 'September · historical SDK',32 if mobile else 85):t(left,y,line,bold=True);y+=22
   y+=8
  label=r['label'];label+= ' · post-hoc' if name=='news' and r['provenance'].startswith('new') else ''
  lines=textwrap.wrap(label,30 if mobile else 80)
  # Same label/row spacing makes reading and rule panels directly aligned.
  for line in lines:t(left,y,line);y+=21
  if name in ['reading','rule'] and mobile:y+=(2-len(lines))*21 if len(lines)<2 else 0
  if name=='exit':
   cell=span/3
   for j,(condition,n) in enumerate(zip(['Present','Absent','Insufficient'],r['counts'])):
    x=left+j*cell;t(x+cell/2,y,condition,'middle');body.append(f'<rect x="{x+2}" y="{y+9}" width="{cell-4}" height="42" fill="{ "#e4edef" if n==12 else "#f5e8df"}"/>');t(x+cell/2,y+36,f'{n}/12','middle',True)
   desc.append(label+': '+', '.join(str(n)+'/12' for n in r['counts']));y+=83;continue
  if name=='timing':v=r['median_s'];p=r['p90_s'];maximum=3;value=f'Median {v:.3f} s · p90 {p:.3f} s'
  elif name=='news':v=r['tp'];maximum=79;value=f'{v} of 79 proxy-important'
  else:v=100 if name=='reading' else r['correct']/529*100;maximum=100;value='60/60 correct · 100%' if name=='reading' else f"{r['correct']}/529 correct · {v:.1f}%"
  t(left,y,value);desc.append(label+': '+value);y+=19
  body.append(f'<line x1="{left}" x2="{right}" y1="{y}" y2="{y}" stroke="#cbd5d9"/>')
  if name=='timing':
   x=left+v/maximum*span;xp=left+p/maximum*span;body.append(f'<line x1="{x}" x2="{xp}" y1="{y}" y2="{y}" stroke="#365d6d" stroke-width="2"/><circle cx="{x}" cy="{y}" r="5" fill="#365d6d"/><path d="M {xp} {y-6} l 6 6 -6 6 -6 -6 Z" fill="#365d6d"/>')
  else:
   body.append(f'<rect x="{left}" y="{y-6}" width="{v/maximum*span}" height="12" fill="#365d6d"/>')
   if name=='rule':x=left+.95*span;body.append(f'<line x1="{x}" x2="{x}" y1="{y-12}" y2="{y+12}" stroke="#925337" stroke-dasharray="3 2"/>')
  y+=36
 if name!='exit':
  ticks=[0,1,2,3] if name=='timing' else [0,20,40,60,79] if name=='news' else [0,25,50,75,100]
  for v in ticks:t(left+v/maximum*span,y,str(v)+('%' if name in ['reading','rule'] else ''),'start' if v==0 else 'end' if v==maximum else 'middle')
  y+=30
 if name=='rule':t(left,y,'Dashed: 95% overall reference');y+=28
 return f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {width} {y}" role="img" aria-labelledby="title desc"><title id="title">{E(title)}</title><desc id="desc">{E("; ".join(desc))}</desc><g font-family="Arial,sans-serif" font-size="16" fill="#19333b">'+''.join(body)+'</g></svg>'
for name in ['reading','rule','news','exit','timing']:
 for width in [300,700]:(P/f'{name}-{width}.svg').write_text(svg(name,width))
print('Generated ten responsive SVGs from sanitized aggregates.')
