import subprocess,json,pathlib,time,os,sys
from PIL import Image,ImageChops
root=pathlib.Path(__file__).parent
ab=str(root/'browser/node_modules/.bin/agent-browser')
out=root/('web-'+time.strftime('%Y%m%d-%H%M%S'));out.mkdir()
sessions={'base':'proof29788','head':'proof29788head'};tabs={'base':'t2','head':'t1'}
def call(side,*args):
    r=subprocess.run([ab,'--session',sessions[side],*args],capture_output=True,text=True)
    if r.returncode:raise RuntimeError(str(args)+r.stderr+r.stdout)
    return r.stdout.strip()
def ev(side,s):return json.loads(call(side,'eval',s))
def rows(side):return ev(side,'Array.from(document.querySelectorAll("[role=region] p")).map(p=>p.innerText)')
def stable(side,label):
    for i in range(10):
        a=out/(side+'-'+label+'-a.png');b=out/(side+'-'+label+'.png')
        call(side,'screenshot',str(a));time.sleep(.5);call(side,'screenshot',str(b))
        if ImageChops.difference(Image.open(a),Image.open(b)).getbbox() is None:return
    raise RuntimeError('screenshots not stable '+side+' '+label)
for side in sessions:
    call(side,'wait','--fn','!!window.Clerk?.session && document.querySelectorAll("[role=region] p").length >= 2 && !document.body.innerText.includes("Checking relay status")')
# Same actual account rows and settled statuses, without changing the app's discovery data.
initial={s:rows(s) for s in sessions}
if initial['base']!=initial['head']:raise RuntimeError('Starting rows differ')
print('Identical starting rows:',len(initial['base'])//2,flush=True)
blankTabs={}
for side in sessions:
    stable(side,'initial')
    ev(side,'window.__proofEvents=[];["focus","blur","visibilitychange"].forEach(type=>(type==="visibilitychange"?document:window).addEventListener(type,()=>window.__proofEvents.push({type,visible:document.visibilityState,time:Date.now()})));true')
    call(side,'record','start',str(out/(side+'-return-raw.mp4')),'--fps','5')
    call(side,'tab','new','about:blank')
    tablist=call(side,'tab')
    import re
    blankTabs[side]=re.search(r'→ \[(t\d+)\]',tablist).group(1)
time.sleep(11)
for side in sessions:
    call(side,'tab',tabs[side]);time.sleep(2);stable(side,'unchanged')
    if rows(side)!=initial[side]:raise RuntimeError('Unchanged registrations changed rows/status '+side)
print('Unchanged return retained rows and settled statuses on both sides',flush=True)
# Capture a token immediately before backgrounding; it is never uploaded.
token=ev('head','(async()=>await window.Clerk.session.getToken({template:'+json.dumps(os.environ['T3CODE_CLERK_JWT_TEMPLATE'])+'}))()')
(root/'clerk-token').write_text(token)
for side in sessions:call(side,'tab',blankTabs[side])
args=[sys.executable,str(root/'register.py'),'--browser',ab,'--session','proof29788','--server','http://127.0.0.1:14373','--bearer-file',str(root/'new-server-token'),'--receipt',str(root/'registration.json'),'--clerk-token-file',str(root/'clerk-token')]
r=subprocess.run(args,capture_output=True,text=True,check=True);print(r.stdout.strip(),flush=True)
time.sleep(11)
observations={}
for side in sessions:
    call(side,'tab',tabs[side]);time.sleep(3);stable(side,'returned')
    observations[side]={'returnedRows':len(rows(side))//2,'newServerVisible':'New Connect server' in rows(side),'events':ev(side,'window.__proofEvents')}
    print(side,'new server visible:',observations[side]['newServerVisible'],flush=True)
# Base reload is visibly separate, and proves successful registration after the stale return.
call('base','reload');call('base','wait','--text','New Connect server');call('base','wait','--fn','!document.body.innerText.includes("Checking relay status")');stable('base','after-reload')
time.sleep(2)
for side in sessions:call(side,'record','stop')
(out/'observations.json').write_text(json.dumps({'initialRows':len(initial['base'])//2,'unchangedRowsAndStatusesRetained':True,**observations},indent=2))
print('CAPTURE_DIR',out,flush=True)
