#!/usr/bin/env python3
from pathlib import Path
import json,sys,shutil,re
root=Path(__file__).resolve().parent.parent
checks=[]
def ck(name,cond,detail=''): checks.append((name,bool(cond),detail))
node=shutil.which('node'); ck('node_available',bool(node),node or '')
needed=['learner/starter/index.html','learner/starter/scheduler-core.js','learner/starter/simulator.js','learner/starter/app.js','learner/transfer/index.html','learner/transfer/scheduler-core.js','learner/transfer/simulator.js','learner/transfer/app.js','learner/evidence/Async_Failure_Concurrency_Budget_Record_v1.template.json','learner/README.en-US.md','learner/README.fa-IR.md']
for f in needed: ck('present:'+f,(root/f).is_file())
try:
 ev=json.loads((root/'learner/evidence/Async_Failure_Concurrency_Budget_Record_v1.template.json').read_text())
 ck('evidence_schema',ev.get('schema')=='MAKNOW_ASYNC_FAILURE_CONCURRENCY_BUDGET_RECORD_v1')
 ck('evidence_mutations',len(ev.get('failure_mutations',[]))>=3)
 bd=ev.get('browser_dom_observation',{})
 ck('browser_dom_block_present',isinstance(bd,dict) and set(['claimed','environment','observation','inference']).issubset(bd))
 ck('browser_dom_default_unclaimed',bd.get('claimed') is False and not bd.get('observation') and not bd.get('inference'))
except Exception as e: ck('evidence_json',False,str(e))
text=(root/'learner/starter/scheduler-core.js').read_text()
ck('public_map_function','mapWithConcurrency' in text); ck('public_deadline_function','runWithDeadline' in text)
ck('starter_does_not_presolve_limit','items.map' in text and 'limit` is ignored' in text)
ck('starter_does_not_presolve_cancel','TIMEOUT_OBSERVED' in text and 'AbortController' not in text)
if '--self-test' in sys.argv:
 ok=sum(s for _,s,_ in checks); print(f'{ok}/{len(checks)} PASS')
 for n,s,d in checks: print(('PASS' if s else 'FAIL'),n,d)
 raise SystemExit(0 if ok==len(checks) else 1)
print(json.dumps({'structural_checks':f'{sum(s for _,s,_ in checks)}/{len(checks)}','node':node},indent=2))
