import json, glob, os, re, subprocess, tempfile, shutil, sys, random
def blocks(text):
  m=re.findall(r'```(\w*)\r?\n([\s\S]*?)```', text or '')
  return [(l.lower(),c) for l,c in m] if m else [('',text or '')]
def loc(text):  # vendor loc.js logic
  bl=re.findall(r'```[a-zA-Z0-9_+-]*\r?\n([\s\S]*?)```', text or '')
  code=('\n'.join(bl) if bl else (text or ''))
  code=re.sub(r'/\*[\s\S]*?\*/','',code)
  return sum(1 for l in code.split('\n') if (s:=l.strip()) and not s.startswith(('//','#','/*','*')) and s!='*/')
def pycode(text):
  bs=[c for l,c in blocks(text) if l in ('python','py','')]
  # drop shell-ish blocks
  bs=[c for c in bs if not re.match(r'\s*(pip|python3? |\$ )',c)]
  return '\n\n#@@BLOCK@@\n\n'.join(bs)
HARN={
'T1':'''
import inspect
fs=[v for k,v in list(globals().items()) if inspect.isfunction(v) and v.__module__=='__main__' and not k.startswith('test')]
cands=[v for v in fs if 'valid' in v.__name__.lower() or 'email' in v.__name__.lower()]
good=['a@b.co','first.last+tag@example.com','x_y@sub.example.org']; bad=['plainaddress','a@','@b.com','a b@c.com','a@@b.com','']
def ok(f,x):
  try: r=f(x)
  except Exception: return False
  if isinstance(r,tuple): r=r[0]
  return bool(r)
best=0
for f in cands:
  res=[ok(f,g) for g in good]+[not ok(f,b) for b in bad]
  print('CAND',f.__name__,sum(res))
  best=max(best,sum(res))
print('CHECKS',best,9)
''',
'T3':'''
''',
'T4':'''
import os,random
here=os.getcwd()
abs_t='/tmp/ponytail_abs_%d.txt'%random.randint(0,10**9)
r=0
try:
  save_upload('ok.txt',b'hi'); r+= open(os.path.join(here,'uploads','ok.txt'),'rb').read()==b'hi'
except Exception as e: print('okerr',e)
for name in ['../evil.txt',abs_t]:
  try: save_upload(name,b'x')
  except Exception: pass
r+= not os.path.exists(os.path.join(here,'evil.txt'))
r+= not os.path.exists(abs_t)
print('CHECKS',r,3)
''',
'T5':'''
r=0
for s,v in [('$1,234.50',123450),('0.99',99),('$5',500),('$0.10',10)]:
  try: r+= (parse_price(s)==v and isinstance(parse_price(s),int))
  except Exception as e: pass
try: parse_price('abc')
except Exception: r+=1
print('CHECKS',r,5)
''',
}
DEB='''
const results=[];let start=Date.now();
const obj={name:'o', m(x){results.push([x,this&&this.name,Date.now()])}};
let d; try{ d=debounce(obj.m,50);}catch(e){ console.log('CHECKS 0 4'); process.exit(0)}
obj.d=d;
let last;
(async()=>{for(let i=0;i<5;i++){obj.d(i); last=Date.now(); await new Promise(r=>setTimeout(r,10));}
await new Promise(r=>setTimeout(r,150));
let c=0; c+= results.length===1; c+= results.length&&results[0][0]===4; c+= results.length&&results[0][2]-last>=45; c+= results.length&&results[0][1]==='o';
console.log('CHECKS',c,4);})();
'''
def run_py(code, harness, t):
  d=tempfile.mkdtemp(); open(f'{d}/sales.csv','w').write('date,item,amount\n2026-01-01,a,10\n2026-01-02,b,20.5\n2026-01-03,c,300\n2026-01-04,d,4.25\n2026-01-05,e,899.75\n')
  os.makedirs(f'{d}/sub',exist_ok=True); wd=f'{d}/sub'; shutil.copy(f'{d}/sales.csv',wd)
  # strip __main__ demo blocks? keep: they run with __name__=='__main__'; input() calls would hang -> stdin closed
  import json as _j
  src=('import sys\n_blocks='+repr(code.split('\n\n#@@BLOCK@@\n\n'))+'\n'
       '_g={"__name__": %r}\n' % ('__main__' if t=='T3' else 'harness_mod') +
       'for _b in _blocks:\n'
       '    try: exec(compile(_b,"block","exec"),_g)\n'
       '    except BaseException as _e: print("BLOCKERR",type(_e).__name__,str(_e)[:80],file=sys.stderr)\n'
       'globals().update({k:v for k,v in _g.items() if not k.startswith("__")})\n'
       'import inspect\n'
       'for _k,_v in list(globals().items()):\n'
       '    if inspect.isfunction(_v): _v.__module__="__main__"\n'
       + harness)
  open(f'{wd}/t.py','w').write(src)
  try: p=subprocess.run([os.path.abspath('venv/bin/python'),'t.py'],cwd=wd,capture_output=True,text=True,timeout=30,stdin=subprocess.DEVNULL)
  except subprocess.TimeoutExpired: return None,'timeout'
  out=p.stdout+p.stderr
  if t=='T3':
    nums=re.findall(r'-?\d[\d,]*\.?\d*',p.stdout); ok=any(abs(float(n.replace(',',''))-1234.5)<0.001 for n in nums if n.replace(',','').replace('.','').lstrip('-'))
    return (1 if ok else 0,1), out[-400:]
  m=re.search(r'CHECKS (\d+) (\d+)',p.stdout); 
  # evil file check for T4 outside wd
  if t=='T4' and m and os.path.exists(f'{d}/evil.txt'): return (int(m.group(1))-1,3),out[-400:]
  return ((int(m.group(1)),int(m.group(2))) if m else None), out[-400:]
def run_js(text):
  bl=[re.sub(r'^\s*export\s+(default\s+)?','',c,flags=re.M) for l,c in blocks(text) if l in ('javascript','js','')]
  pre="const vm=require('vm');const ctx=vm.createContext({setTimeout,clearTimeout,console,Date,module:{exports:{}}});for(const b of "+json.dumps(bl)+"){try{vm.runInContext(b,ctx)}catch(e){console.error('BLOCKERR',e.message)}}const debounce=ctx.debounce||ctx.module.exports.debounce||ctx.module.exports;"
  d=tempfile.mkdtemp(); open(f'{d}/t.js','w').write(pre+'\n'+DEB)
  try: p=subprocess.run(['node','t.js'],cwd=d,capture_output=True,text=True,timeout=30)
  except subprocess.TimeoutExpired: return None,'timeout'
  m=re.search(r'CHECKS (\d+) (\d+)',p.stdout); return ((int(m.group(1)),int(m.group(2))) if m else None),(p.stdout+p.stderr)[-400:]
def has_test(text):
  code='\n'.join(c for l,c in blocks(text))
  return bool(re.search(r'^\s*assert\b|def test_|import unittest|import pytest|console\.assert|assert\(', code, re.M))
rows=[]
for fn in sorted(glob.glob('out/*.json')):
  r=json.load(open(fn)); txt=r['content'] or ''
  if r['task']=='T2': chk,log=run_js(txt)
  else: chk,log=run_py(pycode(txt),HARN.get(r['task'],''),r['task'])
  passed = bool(chk and chk[0]==chk[1])
  rows.append({'file':os.path.basename(fn),'model':r['model'],'task':r['task'],'arm':r['arm'],'run':r['run'],'loc':loc(txt),
    'completion_tokens':(r['usage'] or {}).get('completion_tokens'),'prompt_tokens':(r['usage'] or {}).get('prompt_tokens'),'secs':r['secs'],
    'checks':chk,'pass':passed,'has_test':has_test(txt),'finish':r['finish'],'log_tail':log if not passed else ''})
json.dump(rows,open('grades.json','w'),indent=1)
for x in rows: print(x['file'],x['loc'],x['completion_tokens'],x['checks'],x['pass'],x['has_test'],x['finish'], (x['log_tail'][-150:].replace('\n',' | ') if not x['pass'] else ''))
