import os,sys,time,subprocess,json,urllib.request,urllib.error,pathlib,re,shutil
ROOT=pathlib.Path(os.environ['PROOF_SCRATCH'])
side=sys.argv[1]; work=pathlib.Path(sys.argv[2]); out=ROOT/(side+'-'+time.strftime('%Y%m%dT%H%M%S')); out.mkdir()
home=ROOT/'disk'/side; (home/'userdata/logs').mkdir(parents=True)
log=home/'userdata/logs/boot-service.log'; err=out/'stderr-private.log'; unit='t3-disk-proof-'+side
start=time.monotonic(); events=[]
def emit(s):
    line=f'{time.monotonic()-start:07.1f}s {s}'; print(line,flush=True);events.append([time.monotonic()-start,s]);(out/'transcript.txt').write_text('\n'.join(f'{t:07.1f}s {x}' for t,x in events)+'\n');(out/'events.json').write_text(json.dumps(events))
def state():
    return subprocess.check_output(['systemctl','--user','show',unit,'-p','ActiveState','-p','SubState','-p','ExecMainStatus','-p','MainPID'],text=True).strip().replace('\n','; ')
def probe():
    try:
        with urllib.request.urlopen('http://127.0.0.1:47913/',timeout=3) as r:return 'HTTP '+str(r.status)
    except urllib.error.HTTPError as e:return 'HTTP '+str(e.code)
    except Exception:return 'HTTP connection refused/unavailable'
cmd=['systemd-run','--user','--unit='+unit,'--property=StandardOutput=append:'+str(log),'--property=StandardError=append:'+str(err),'--working-directory='+str(work/'apps/server'),'--setenv=T3CODE_HOME='+str(home),'--setenv=T3CODE_DEV_SEED=0',shutil.which('node'),'src/bin.ts','serve','--port','47913','--no-browser','--log-level','debug']
emit('Linux; real Node server; transient systemd user unit; fresh synthetic T3 home; 256 MiB tmpfs.')
emit('Commit '+subprocess.check_output(['git','rev-parse','HEAD'],cwd=work,text=True).strip())
subprocess.run(cmd,check=True,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
try:
    for i in range(90):
        if log.exists() and 'startup phase: complete' in log.read_text(errors='replace'):break
        time.sleep(1)
    emit('Before filling disk: '+state()+'; '+probe())
    if 'startup phase: complete' not in log.read_text(errors='replace'):raise RuntimeError('server failed startup')
    # Align the existing stdout file to a page boundary so its next append needs a free page.
    with open(log,'ab',buffering=0) as f:
        f.write(b'\n'*(4096-os.stat(log).st_size%4096))
    filler=home/'filler'
    with open(filler,'wb',buffering=0) as f:
        try:
            while True:f.write(b'\0'*(1024*1024))
        except OSError as e:emit('Fill data filesystem: '+str(e))
    st=os.statvfs(home);emit('Disk available bytes='+str(st.f_bavail*st.f_frsize)+'; stdout log shares this filesystem.')
    for i in range(24):
        time.sleep(10)
        emit('Disk full: '+state()+'; '+probe())
    filler.unlink();emit('Freed filler file; available bytes='+str(os.statvfs(home).f_bavail*os.statvfs(home).f_frsize))
    for i in range(3):
        time.sleep(10);emit('After freeing space (no restart): '+state()+'; '+probe())
    if side == 'after-ready':
        emit('Descriptor endpoint after freeing space: '+str(urllib.request.urlopen('http://127.0.0.1:47913/.well-known/t3/environment').status))
        project=ROOT/'synthetic-project'; project.mkdir(exist_ok=True)
        cli=subprocess.run([shutil.which('node'),'src/bin.ts','project','add',str(project),'--title','Disk recovery proof'],cwd=work/'apps/server',env={**os.environ,'T3CODE_HOME':str(home)},capture_output=True,text=True,timeout=45)
        emit('Real CLI project add after freeing space: exit='+str(cli.returncode))
        (out/'cli-private.txt').write_text(cli.stdout+cli.stderr)
    # Keep only the crash evidence, stripping private filesystem locations.
    stderr=err.read_text(errors='replace') if err.exists() else ''
    stderr=re.sub(re.escape(str(work))+r'[^\s)]*','<checkout>',stderr)
    stderr=re.sub(re.escape(str(ROOT))+r'[^\s)]*','<isolated-data>',stderr)
    (out/'stderr.txt').write_text(stderr)
    for line in stderr.splitlines():
        if any(x in line for x in ['ENOSPC','writeSync','SyncWriteStream','console.log','serverLogger','Unhandled','throw er','error event']):emit('stderr: '+line)
    emit('Final stdout size='+str(log.stat().st_size)+' bytes')
finally:
    subprocess.run(['systemctl','--user','stop',unit],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
    emit('Stopped the proof unit.')
