import os,sys,json,shutil,hashlib,sqlite3,subprocess,time,signal,pwd,urllib.request
from pathlib import Path
manifest=json.loads(Path(sys.argv[1]).read_text());stage=Path(manifest['stage']);root=Path('/opt/charter-live/live_deploy_v2')
names=['order_dispatch_guard.py','bybit_demo_client.py','webhook_receiver.py','sltp_engine.py']
for name,digest in manifest['original'].items():
 assert hashlib.sha256((root/name).read_bytes()).hexdigest()==digest,'Source changed '+name
for name in names:compile((stage/'v2_source'/name).read_text(encoding='utf-8-sig'),str(root/name),'exec')
with sqlite3.connect('file:'+str(root/'logs/webhook_queue.db')+'?mode=ro',uri=True) as db:
 assert db.execute("select count(*) from queue where status='pending'").fetchone()[0]==0,'Pending requests: deployment deferred'
proc=Path('/proc/2179617')
assert Path(os.readlink(proc/'cwd'))==root
cmd=[s.decode() for s in (proc/'cmdline').read_bytes().split(b'\0') if s]
assert cmd==['/opt/charter/venv/bin/python','-u','sltp_engine.py'],cmd
assert (proc/'status').read_text().split('Uid:')[1].split()[0]=='0'
env=dict(s.split('=',1) for s in (proc/'environ').read_text().split('\0') if '=' in s)
assert env.get('WEBHOOK_SECRET'),'Missing original runtime environment'
backup=stage/'original';backup.mkdir(exist_ok=True)
for name in names:
 if (root/name).exists():shutil.copy2(root/name,backup/name)
owner=pwd.getpwnam('charter')
def install(source,dest):
 temp=dest.with_name(dest.name+'.dispatch-stage');shutil.copyfile(source,temp)
 os.chown(temp,owner.pw_uid,owner.pw_gid);os.chmod(temp,0o644);os.replace(temp,dest)
def launch_sltp():
 with (root/'logs/sltp_engine_v2.out').open('ab') as out,(root/'logs/sltp_engine_v2.err').open('ab') as err:
  p=subprocess.Popen(cmd,cwd=root,env=env,stdin=subprocess.DEVNULL,stdout=out,stderr=err,start_new_session=True)
 return p
stopped=False;new=None
try:
 subprocess.run(['systemctl','stop','charter-v2-webhook'],check=True,timeout=30)
 os.kill(2179617,signal.SIGTERM)
 for _ in range(40):
  if not proc.exists():break
  time.sleep(.25)
 assert not proc.exists(),'SLTP did not terminate'
 stopped=True
 for name in names:install(stage/'v2_source'/name,root/name)
 audit=root/'logs/order_dispatch_audit.jsonl';audit.touch(exist_ok=True);os.chown(audit,owner.pw_uid,owner.pw_gid);os.chmod(audit,0o640)
 subprocess.run(['systemctl','start','charter-v2-webhook'],check=True,timeout=30)
 for _ in range(30):
  try:
   health=json.load(urllib.request.urlopen('http://127.0.0.1:5581/health',timeout=2));assert health['ok'];break
  except Exception:time.sleep(.3)
 else:raise RuntimeError('Webhook health failed')
 new=launch_sltp();time.sleep(2);assert new.poll() is None,'SLTP startup failed'
 result={'deployed':names,'backup':str(backup),'webhook_pid':subprocess.check_output(['systemctl','show','charter-v2-webhook','-p','MainPID','--value'],text=True).strip(),'sltp_pid':new.pid,'health':health}
 (stage/'deployment_result.json').write_text(json.dumps(result,indent=2));print(json.dumps(result))
except Exception:
 if new and new.poll() is None:new.terminate();new.wait(timeout=10)
 for name in names:
  if (backup/name).exists():install(backup/name,root/name)
 subprocess.run(['systemctl','restart','charter-v2-webhook'],check=True,timeout=30)
 if stopped:launch_sltp()
 raise
