#!/usr/bin/python3
import os,sys,subprocess,time,json,datetime,pathlib,hashlib,signal
S=pathlib.Path('/tmp/lead-33554-r1/evidence-capture-1')
if len(sys.argv)>1 and sys.argv[1]=='--worker':
  checkout,row,side,run=sys.argv[2:]; r=pathlib.Path(run)
  subprocess.run(['ip','link','set','lo','up'],check=True)
  for i in range(10):subprocess.run(['iptables','-A','OUTPUT','-d',f'127.77.0.{11+i}','-p','tcp','--dport','42222','-j','DROP'],check=True)
  for name in ['host','client']:
    subprocess.run(['ssh-keygen','-q','-t','ed25519','-N','','-f',str(r/name)],check=True)
  (r/'authorized_keys').write_text((r/'client.pub').read_text())
  (r/'known_hosts').write_text('')
  (r/'sshd_config').write_text(f'''Port 42222
ListenAddress 127.0.0.1
HostKey {r}/host
PidFile {r}/sshd.pid
AuthorizedKeysFile {r}/authorized_keys
StrictModes no
PasswordAuthentication no
KbdInteractiveAuthentication no
UsePAM yes
AllowUsers ubuntu
AllowTcpForwarding yes
LogLevel VERBOSE
''')
  if row=='F05':
    (r/'sshd_config').write_text((r/'sshd_config').read_text().replace('Port 42222','Port 42223'))
  (r/'ssh_config').write_text(f'''Host *
  HostName fixture.invalid
  User ubuntu
  Port 42222
  IdentityFile {r}/client
  IdentitiesOnly yes
  UserKnownHostsFile {r}/known_hosts
  GlobalKnownHostsFile /dev/null
  StrictHostKeyChecking no
  UpdateHostKeys no
  LogLevel DEBUG1
''')
  processes=[]
  try:
    if row!='F10':
      log=(r/'sshd.raw.log').open('w'); p=subprocess.Popen(['/usr/sbin/sshd','-D','-e','-f',str(r/'sshd_config')],stdout=log,stderr=log);processes.append(p)
      (r/'sshd-spawn-pid').write_text(str(p.pid))
    env={**os.environ,'SCRATCH':str(S),'RUN_DIR':str(r),'ROW':row,'CHECKOUT':checkout,'TMPDIR':str(r/'tmp'),'PATH':str(S/'bin')+':'+os.environ['PATH'],'T3CODE_DEV_SEED':'0'}
    (r/'tmp').mkdir()
    start=time.monotonic()
    with (r/'runtime.log').open('w') as out:
      p=subprocess.Popen(['node',str(S/'driver.mjs')],cwd=checkout,env=env,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,text=True)
      processes.append(p);(r/'driver-pid').write_text(str(p.pid))
      for line in p.stdout:
        out.write(f'{datetime.datetime.now(datetime.timezone.utc).isoformat()} +{(time.monotonic()-start)*1000:.1f}ms {line}');out.flush()
      code=p.wait();(r/'driver-exit').write_text(str(code))
  finally:
    for p in reversed(processes):
      if p.poll() is None:p.terminate()
    for p in reversed(processes):
      try:p.wait(timeout=5)
      except subprocess.TimeoutExpired:p.kill();p.wait()
    (r/'worker-cleanup.json').write_text(json.dumps({'spawnedPids':[p.pid for p in processes],'allStopped':all(p.poll()!=None for p in processes)}))
  sys.exit(0)
rows=sys.argv[1:] or ['F03','F06','F04','F10','F11','F01','F07','F08','F05','F09','F16']
for row in rows:
  for side in ['before','after']:
    r=S/(datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%S%fZ')+'-'+row+'-'+side);r.mkdir()
    checkout='/home/ubuntu/wt/pr-33554-'+side
    manifest={'row':row,'side':side,'sha':subprocess.check_output(['git','rev-parse','HEAD'],cwd=checkout,text=True).strip(),'dirty':subprocess.check_output(['git','status','--porcelain'],cwd=checkout,text=True),'node':subprocess.check_output(['node','--version'],text=True).strip(),'ssh':subprocess.run(['/usr/bin/ssh','-V'],capture_output=True,text=True).stderr.strip(),'method':'real OpenSSH/sshd, injected ordered resolver answers, namespace-private iptables DROP','issuePairingToken':False,'serverKind':'external','remoteLaunch':'stubbed JSON; HTTP fixture is synthetic loopback backend','ports':{'ssh':42222,'backend':43333},'blackholeIps':[f'127.77.0.{11+i}' for i in range(10)],'F03Note':'Three dead addresses used explicitly for margin beyond 20s, as matrix permits','F05Note':'8 real 10s address retries plus 5s real TCP SSH proxy banner delay','scripts':{p.name:hashlib.sha256(p.read_bytes()).hexdigest() for p in S.iterdir() if p.is_file() and p.suffix in ['.py','.mjs','.c']}}
    (r/'manifest.json').write_text(json.dumps(manifest,indent=2))
    print('START',row,side,r,flush=True)
    with (r/'worker.log').open('w') as out:
      result=subprocess.run(['sudo','-n','unshare','--mount','--net','python3',str(S/'run-fixture.py'),'--worker',checkout,row,side,str(r)],stdout=out,stderr=subprocess.STDOUT)
    print('END',row,side,result.returncode,flush=True)
