import os,sys,pathlib,subprocess,sqlite3,time,urllib.request,json,re,signal
root=pathlib.Path(__file__).parent
run=root/time.strftime('run-%Y%m%d-%H%M%S');run.mkdir()
results=[]
for side,checkout in [('before',sys.argv[1]),('after',sys.argv[2])]:
 for kind in ['control','projection','event']:
  folder=run/f'{side}-{kind}';(folder/'home'/'userdata').mkdir(parents=True)
  db=folder/'home'/'userdata'/'statev2.sqlite'
  seed=subprocess.run(['node',str(root/'seed.mjs'),checkout,str(db),kind],capture_output=True,text=True)
  (folder/'seed.log').write_text(seed.stdout+seed.stderr)
  if seed.returncode: raise RuntimeError(seed.stderr)
  port=18764
  env=os.environ.copy();env.update(T3CODE_DEV_SEED='0',NO_COLOR='1')
  log=folder/'server.raw.log'
  with log.open('w') as out:
   proc=subprocess.Popen(['node','src/bin.ts','serve','--base-dir',str(folder/'home'),'--host','127.0.0.1','--port',str(port)],cwd=checkout+'/apps/server',env=env,stdout=out,stderr=subprocess.STDOUT,start_new_session=True)
   ready=False;status=None
   try:
    until=time.monotonic()+45
    while time.monotonic()<until:
     if proc.poll() is not None: break
     try:
      with urllib.request.urlopen(f'http://127.0.0.1:{port}/.well-known/t3/environment',timeout=1) as res:
       status=res.status;body=res.read().decode();ready=True;break
     except Exception: time.sleep(.3)
    code=proc.poll()
   finally:
    if proc.poll() is None:
     os.killpg(proc.pid,signal.SIGTERM)
     try:proc.wait(timeout=10)
     except subprocess.TimeoutExpired:os.killpg(proc.pid,signal.SIGKILL);proc.wait()
  con=sqlite3.connect(db)
  ledger=con.execute('select max(migration_id) from effect_sql_migrations').fetchone()[0]
  payload=con.execute("select payload_json from orchestration_v2_projection_threads where thread_id='valid'").fetchone()[0]
  ev=con.execute("select payload_json from orchestration_events where event_id='event:valid'").fetchone()[0]
  damaged=None
  if kind=='projection':damaged=con.execute("select payload_json from orchestration_v2_projection_threads where thread_id='damaged'").fetchone()[0]
  if kind=='event':damaged=con.execute("select payload_json from orchestration_events where event_id='event:damaged'").fetchone()[0]
  text=log.read_text()
  text=re.sub(r'\x1b\[[0-9;]*m','',text).replace(checkout,'<checkout>').replace(str(root),'<evidence>').replace(os.path.expanduser('~'),'<home>')
  text='\n'.join(line for line in text.splitlines() if not any(word in line for word in ['Token:', 'Pairing URL:', 'App link:', '█', '▀', '▄']))
  text=re.sub(r'https?://[^\s\"\']*(?:token|pair)[^\s\"\']*','<redacted pairing URL>',text,flags=re.I)
  (folder/'server.log').write_text(text)
  result=dict(side=side,case=kind,sha=subprocess.check_output(['git','rev-parse','HEAD'],cwd=checkout,text=True).strip(),ready=ready,httpStatus=status,exitCode=code,ledger=ledger,validProjection=json.loads(payload).get('pullRequests'),validEvent=json.loads(ev).get('pullRequests'),damagedPayload=damaged,malformedJSON='malformed JSON' in text)
  results.append(result);print(json.dumps(result),flush=True)
  (run/'observations.json').write_text(json.dumps(results,indent=2))
print(run)
