#!/usr/bin/env python3
"""Freeze inputs, validate trial outputs and build transparent report summaries."""
import argparse, datetime, hashlib, json
from pathlib import Path

ROOT=Path(__file__).resolve().parent
INPUTS=['protocol.json','trial.py','repo-only/author_inputs.py','repo-only/evaluator.json',
        'packets/feedback.json','packets/cases.json','packets/frozen-stage1.md',
        'packets/history-stage1.md','packets/scoped-stage1.md','packets/stage2.md']
RUNS=[f'{arm}-{n}' for arm in ['frozen','history','scoped'] for n in [1,2]]

def read(p): return json.loads((ROOT/p).read_text())
def write(p,x): (ROOT/p).write_text(json.dumps(x,indent=2,ensure_ascii=False)+'\n')
def digest(p): return hashlib.sha256((ROOT/p).read_bytes()).hexdigest()
def verify():
 f=read('freeze.json')
 assert set(f['sha256'])==set(INPUTS),'Frozen inventory mismatch'
 for name,h in f['sha256'].items(): assert digest(name)==h, f'Frozen input modified: {name}'
 return f
def freeze():
 assert not (ROOT/'freeze.json').exists(),'Refusing to overwrite freeze'
 write('freeze.json',{'created_utc':datetime.datetime.now(datetime.timezone.utc).isoformat(),
 'state':'All inputs fixed before any participant preparation or test session.',
 'sha256':{p:digest(p) for p in INPUTS}})
 verify();print('Inputs frozen and verified. Participants may now begin.')
def summarize():
 f=verify();cases=read('packets/cases.json'); ids={c['id'] for c in cases}; rubric={c['id']:c for c in read('repo-only/evaluator.json')['rubric']}
 out={'protocol_id':read('protocol.json')['id'],'freeze_sha256':digest('freeze.json'),'state':'awaiting_participants','runs':[],'cases':cases,'feedback':read('packets/feedback.json')}
 for name in RUNS:
  p=ROOT/'reports'/f'{name}.json'
  if not p.exists():continue
  report=json.loads(p.read_text())
  assert isinstance(report,list) and len(report)==20,f'{name}: need exactly20 answers'
  assert {r['id'] for r in report}==ids,f'{name}: wrong or duplicate ids'
  for r in report:
   assert set(r)=={'id','action','reason','question','confidence'},f'{name}/{r.get("id")}: schema'
   assert r['action'] in ['keep','ask','reject'] and r['confidence'] in ['low','medium','high']
   assert isinstance(r['reason'],str) and (r['question'] is None or isinstance(r['question'],str))
  meta=read(f'reports/{name}.metadata.json') if (ROOT/'reports'/f'{name}.metadata.json').exists() else {'model':'unknown','execution_metadata':'not recorded'}
  row={'id':name,'arm':name.rsplit('-',1)[0],'replicate':int(name[-1]),'metadata':meta,'answers':report,'report_sha256':digest(f'reports/{name}.json'),'action_agreement':sum(r['action'] in rubric[r['id']]['admissible_actions'] for r in report),'output_words':sum(len(str(v).split()) for r in report for v in r.values()),'adequacy_state':'awaiting_blinded_adjudication'}
  prep=ROOT/'reports'/f'{name}.preparation.md'
  row['preparation_words']=len(prep.read_text().split()) if prep.exists() else None
  row['stage2_within_word_limit']=row['output_words']<=1800
  row['stage1_within_word_limit']=row['preparation_words']<=600 if row['preparation_words'] is not None else None
  adj=ROOT/'reports'/f'{name}.adjudication.json'
  if adj.exists():
   judgments=json.loads(adj.read_text()); assert len(judgments)==20 and {j['id'] for j in judgments}==ids
   answer={r['id']:r for r in report};by_id={c['id']:c for c in cases}
   for j in judgments:
    assert j['reason_score'] in [0,1,2] and j['question_score'] in [0,1]
    j['sound']=answer[j['id']]['action'] in rubric[j['id']]['admissible_actions'] and j['reason_score']==2
   row.update(adequacy_state='adjudicated',judgments=judgments,sound=sum(j['sound'] for j in judgments),by_use={use:sum(j['sound'] for j in judgments if by_id[j['id']]['use']==use) for use in ['EXPLORE','DECIDE']})
  out['runs'].append(row)
 if len(out['runs'])==6:out['state']='participants_complete' if not all(r['adequacy_state']=='adjudicated' for r in out['runs']) else 'adjudicated'
 write('results.json',out);print(json.dumps({'state':out['state'],'runs':[{'id':r['id'],'actions':r['action_agreement'],'sound':r.get('sound')} for r in out['runs']]},indent=2))

if __name__=='__main__':
 p=argparse.ArgumentParser();p.add_argument('action',choices=['freeze','verify','summarize']);a=p.parse_args()
 if a.action=='freeze':freeze()
 elif a.action=='verify':verify();print('All frozen input hashes match.')
 else:summarize()
