import json,subprocess,time,datetime

SIDE='contextra-icerti-ai-control-sidecar-1'
def cmd(args,**kw):
    p=subprocess.run(args,capture_output=True,text=True,timeout=kw.pop('timeout',30),**kw)
    if p.returncode:raise RuntimeError('COMMAND_FAILED rc='+str(p.returncode)+' '+p.stdout[-500:])
    return p.stdout
def inspect(n):return json.loads(cmd(['docker','inspect',n]))[0]
before={n:inspect(n) for n in cmd(['docker','ps','--format','{{.Names}}']).splitlines()}
c=before[SIDE];assert c['State']['Running']
networks=list(c['NetworkSettings']['Networks']);assert networks==['contextra-icerti_default']
env=dict(x.split('=',1) for x in c['Config']['Env'])
assert env['AI_CONTROL_MANAGER_BASE_URL']=='https://192.168.30.68'
assert env['AI_CONTROL_SERVER_ID']=='AI-01'
stamp=datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ')
login='''import asyncio,json,os,ssl,hashlib,stat,time
from pathlib import Path
from control_sidecar.config import Config
from control_sidecar.auth import Auth
from control_sidecar.transport import Transport
from control_sidecar.protocol import BoundaryError
async def main():
 cfg=Config.from_env()
 assert cfg.server_id=='AI-01' and cfg.manager=='https://192.168.30.68'
 ca=hashlib.sha256(ssl.PEM_cert_to_DER_cert(Path(cfg.manager_ca).read_text())).hexdigest()
 assert ca=='dccbf62f489f6defe36a946bc35c99cf1f5aa0cf4e7cb6c50e12f85d04821b59'
 p=Path(cfg.session_file)
 assert not p.is_symlink() and not p.parent.is_symlink()
 if p.exists():
  assert stat.S_IMODE(p.stat().st_mode)==0o600
  backup=p.with_name(p.name+'.before-relogin-STAMP')
  with backup.open('xb') as f:
   os.fchmod(f.fileno(),0o600);f.write(p.read_bytes())
 tx=Transport(cfg.manager_ca);a=Auth(cfg,tx)
 try:
  await a.login()
  assert a.token and a.failure is None and p.exists()
  print(json.dumps({'login':'PASS','server_id':cfg.server_id,'tls_verified':True,
   'session_saved':True,'session_sha256':hashlib.sha256(p.read_bytes()).hexdigest()}))
 finally:await tx.close()
try:asyncio.run(main())
except BoundaryError as e:print(json.dumps({'login':'FAILED','code':e.code,'http_status':e.status}));raise SystemExit(1)
except Exception as e:print(json.dumps({'login':'FAILED','code':type(e).__name__}));raise SystemExit(1)
'''.replace('STAMP',stamp)
check='''import json,ssl,hashlib
from pathlib import Path
from control_sidecar.config import Config
c=Config.from_env();assert c.server_id=='AI-01';assert c.manager=='https://192.168.30.68'
assert hashlib.sha256(ssl.PEM_cert_to_DER_cert(Path(c.manager_ca).read_text())).hexdigest()=='dccbf62f489f6defe36a946bc35c99cf1f5aa0cf4e7cb6c50e12f85d04821b59'
print('CONFIG_CA_PASS')'''
assert 'CONFIG_CA_PASS' in cmd(['docker','exec',SIDE,'python3','-B','-c',check])
status_script='''import urllib.request,json,os
from pathlib import Path
r=urllib.request.Request("http://127.0.0.1:8080/internal/bridge-status",headers={"Authorization":"Bearer "+Path(os.environ['CONTEXTRA_CONTROL_TOKEN_FILE']).read_text().strip()})
b=json.load(urllib.request.urlopen(r,timeout=3));print(json.dumps(b))'''
before_bridge=json.loads(cmd(['docker','exec',SIDE,'python3','-B','-c',status_script]))
print(json.dumps({'phase':'preflight','auth':before_bridge['auth'],'wss':before_bridge['wss']['connected']}),flush=True)
cmd(['docker','stop','--signal=SIGTERM','--timeout=-1',SIDE],timeout=45)
assert not inspect(SIDE)['State']['Running']
helper='contextra-relogin-once-'+stamp.lower()
args=['docker','run','--rm','-i','--name',helper,'--pull=never','--network',networks[0],
 '--volumes-from',SIDE,'--cpus=1','--memory=512m','--pids-limit=64','--cap-drop=ALL',
 '--security-opt=no-new-privileges','--read-only','--tmpfs','/tmp:rw,nosuid,nodev,size=16m',
 '--env','NVIDIA_VISIBLE_DEVICES=void','--entrypoint','python3']
for k,v in env.items():
 if k.startswith('AI_CONTROL_') or k in ('CONTEXTRA_CONTROL_TOKEN_FILE','CONTEXTRA_RELAY_TOKEN_FILE'):
  assert '\n' not in v
  args+=['--env',k+'='+v]
args += [c['Image'],'-B','-']
try:
 result=json.loads(cmd(args,input=login,timeout=30).strip())
except Exception:
 names=cmd(['docker','ps','--format','{{.Names}}']).splitlines()
 if helper in names:cmd(['docker','stop','--signal=SIGTERM','--timeout=-1',helper],timeout=15)
 print(json.dumps({'phase':'STOPPED','reason':'LOGIN_FAILED_OR_UNKNOWN_NO_RETRY','sidecar_running':False}),flush=True)
 raise
assert result['login']=='PASS'
print(json.dumps(dict(result,phase='login')),flush=True)
cmd(['docker','start',SIDE])
deadline=time.monotonic()+90
while True:
 try:
  bridge=json.loads(cmd(['docker','exec',SIDE,'python3','-B','-c',status_script]))
  if bridge['auth'] is None and bridge['wss']['connected']:break
 except Exception:pass
 if time.monotonic()>deadline:raise RuntimeError('LOGIN_PASS_WSS_NOT_CONFIRMED_NO_RELOGIN')
 time.sleep(3)
hash_script="import os,hashlib;from pathlib import Path;print(hashlib.sha256(Path(os.environ['AI_CONTROL_SESSION_FILE']).read_bytes()).hexdigest())"
assert cmd(['docker','exec',SIDE,'python3','-B','-c',hash_script]).strip()==result['session_sha256']
for n,old in before.items():
 new=inspect(n);assert new['Id']==old['Id'] and new['Image']==old['Image']
 if n!=SIDE:assert new['State']['StartedAt']==old['State']['StartedAt']
print(json.dumps({'phase':'complete','login':'PASS','wss':bridge['wss'],
 'auth':bridge['auth'],'session_restored_without_relogin':True,'non_target_preserved':True}),flush=True)
