#!/usr/bin/env python3
"""Drive the two isolated real web apps with agent-browser. Pass side session, app log and output.
The only DOM changes redact local paths/environment labels from public captures.
"""
import json, pathlib, re, subprocess, sys, time, urllib.request
operation, session, app_log_arg, output_arg = sys.argv[1:5]
root=pathlib.Path(__file__).parent
out=pathlib.Path(output_arg)
meta=json.loads((out/'meta.json').read_text())
log=pathlib.Path(app_log_arg).read_text()
url=re.search(r'pairingUrl: (\S+)',log).group(1)
origin=url.split('/pair')[0]
ab_path=root/'browser-tools/node_modules/.bin/agent-browser'
def ab(*args,stdin=None):
    p=subprocess.run([str(ab_path),'--session',session,*args],input=stdin,capture_output=True,text=True)
    if p.returncode: raise RuntimeError(p.stderr or p.stdout)
    return p.stdout
def evaluate(js): return ab('eval','--stdin',stdin=js)
if operation in ['open','navigate','cleanup-open']:
    if operation=='open':
        ab('open','about:blank')
        ab('open',url)
    ab('set','viewport','1280','800')
    ab('set','media','light')
    ab('wait','--text','Restore leftover checkout')
    env=json.load(urllib.request.urlopen(origin+'/.well-known/t3/environment'))
    thread_id=meta['cleanupThreadId'] if operation=='cleanup-open' else meta['threadId']
    ab('open',origin+'/'+env['environmentId']+'/'+thread_id)
    ab('wait','--text','first fixture turn complete')
    print('Opened seeded thread, Chromium, 1280x800, light theme.')
elif operation=='privacy':
    # Redact local directory prefixes and the environment's machine label, preserving outcomes.
    prefixes=[str(out),str(root),str(pathlib.Path(app_log_arg).parent)]
    checkout=meta['checkout']
    prefixes.append(checkout)
    js='''(() => {
      const button = document.querySelector('[aria-label^="Run context:"]');
      const host = button?.getAttribute('aria-label')?.split('Run context: ')[1]?.split(',')[0];
      const replacements = PREFIXES.map(p => [p, '<local>']);
      if (host) replacements.push([host, 'Proof environment']);
      const redact = () => {
        const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
        for (let node; node = walker.nextNode();) {
          if (['SCRIPT','STYLE'].includes(node.parentElement?.tagName)) continue;
          let value = node.nodeValue;
          for (const [from, to] of replacements) value = value.replaceAll(from, to);
          if (value !== node.nodeValue) node.nodeValue = value;
        }
      };
      window.proofRedaction?.disconnect();
      window.proofRedaction = new MutationObserver(redact);
      window.proofRedaction.observe(document.body, {childList:true,subtree:true,characterData:true});
      redact();
      return 'Local paths and machine label redacted; state and result text unchanged.';
    })()'''.replace('PREFIXES',json.dumps(prefixes))
    print(evaluate(js))
elif operation=='terminal':
    pressed=evaluate('document.querySelector(\'[aria-label="Toggle terminal drawer"]\')?.getAttribute("aria-pressed")')
    if 'true' not in pressed: ab('find','role','button','click','--name','Toggle terminal drawer','--exact')
    evaluate("document.querySelector('[data-thread-terminal-drawer] textarea').focus()")
    time.sleep(1)
    command="unset PROMPT_COMMAND; export PS1='$ '; clear; printf 'CHECKOUT CONTENTS\\n'; ls -A; printf '\\nBRANCH\\n'; GIT_CEILING_DIRECTORIES=\"$(dirname \"$PWD\")\" git branch --show-current; printf '\\nINSPECTION COMPLETE\\n\\033[?25l'; touch .inspection-complete"
    evaluate("(() => { const input=document.querySelector('[data-thread-terminal-drawer] textarea'); input.focus(); input.value=''; input.dispatchEvent(new InputEvent('input', {inputType:'insertFromPaste',data:"+json.dumps(command+"\r")+",bubbles:true})); return 'Command pasted into the real terminal'; })()")
    marker=pathlib.Path(meta['folder'])/'.inspection-complete'
    for _ in range(100):
      if marker.exists(): break
      time.sleep(.05)
    else: raise RuntimeError('Terminal command did not finish')
    print('Real terminal inspection complete.')
elif operation=='capture':
    label=sys.argv[5]
    a=out/(label+'-stability.png'); b=out/(label+'.png')
    ab('screenshot',str(a))
    for _ in range(8):
      time.sleep(.5)
      ab('screenshot',str(b))
      if a.read_bytes()==b.read_bytes():
        print('Stable screenshot: '+b.name);break
      a.write_bytes(b.read_bytes())
    else: raise RuntimeError('Screenshots did not stabilize')
elif operation=='cleanup-action':
    ab('record','start',str(out/'cleanup-action.mp4'))
    ab('find','role','button','click','--name','Delete now','--exact')
    ab('wait','--fn',"/NotFound: FileSystem.stat|Not a linked worktree/.test(document.querySelector('#storage-cleanup-last-run')?.textContent || '')")
    time.sleep(2)
    print(ab('record','stop'))
elif operation=='cleanup-finish':
    # Settings is already open; this is the same navigation and cleanup action on both commits.
    ab('find','role','button','click','--name','Maintenance','--exact')
    ab('wait','--text','Delete now')
    ab('find','role','button','click','--name','Delete now','--exact')
    ab('wait','--fn',"!!document.querySelector('#storage-cleanup-last-run button[aria-expanded]')")
    evaluate("(() => { const button=document.querySelector('#storage-cleanup-last-run button[aria-expanded]'); if(button.getAttribute('aria-expanded')==='false') button.click(); return 'Opened cleanup results'; })()")
    ab('wait','--fn',"/NotFound: FileSystem.stat|Not a linked worktree/.test(document.querySelector('#storage-cleanup-last-run')?.textContent || '')")
    evaluate("document.querySelector('#storage-cleanup').scrollIntoView({block:'start',behavior:'instant'})")
    time.sleep(.5)
    result=ab('get','text','#storage-cleanup-last-run')
    (out/'cleanup-report.txt').write_text(result)
    print(result,flush=True)
    marker=pathlib.Path(meta['cleanupFolder'])/'node_modules'/'.proof-retained'
    print('Dependency marker preserved:',marker.read_text().strip(),flush=True)
    p=subprocess.run([sys.executable,str(pathlib.Path(__file__)),'capture',session,app_log_arg,output_arg,'cleanup-leftover-folder'],capture_output=True,text=True,check=True)
    print(p.stdout.strip(),flush=True)
    print(ab('record','stop'))
elif operation=='cleanup-start':
    ab('record','start',str(out/'cleanup.mp4'))
    for op in ['cleanup-damage','cleanup-config']:
      print(subprocess.run([sys.executable,str(root/'manual-driver.py'),op,meta['checkout'],str(out)],capture_output=True,text=True,check=True).stdout.strip(),flush=True)
    evaluate("(() => { const row=Array.from(document.querySelectorAll('[data-testid=sidebar-row-card]')).find(el=>el.textContent.includes('Cleanup leftover checkout')); row.querySelector('[aria-label=\"Settle thread\"]').click(); return 'Clicked Settle thread'; })()")
    print('Clicked Settle thread through the UI.')
elif operation=='run-recovery':
    checkout=meta['checkout']
    def driver(op, *extra):
      p=subprocess.run([sys.executable,str(pathlib.Path(__file__)),op,session,app_log_arg,output_arg,*extra],capture_output=True,text=True,check=True)
      print(p.stdout.strip(),flush=True)
    driver('reset-ui')
    driver('record-start','resume')
    print(subprocess.run([sys.executable,str(root/'manual-driver.py'),'damage',checkout,str(out)],capture_output=True,text=True,check=True).stdout.strip(),flush=True)
    ab('find','role','textbox','fill','--name','Message Restore leftover checkout','--exact','Inspect your working directory and branch after resuming.')
    ab('press','Enter')
    ab('wait','--text','second fixture turn complete')
    inspection=subprocess.run([sys.executable,str(root/'manual-driver.py'),'inspect',checkout,str(out)],capture_output=True,text=True,check=True).stdout
    (out/'inspection.json').write_text(inspection)
    print(inspection.strip(),flush=True)
    driver('terminal')
    driver('capture','resumed-checkout')
    driver('record-stop')
elif operation=='reset-ui':
    ab('find','role','textbox','fill','--name','Message Restore leftover checkout','--exact','')
    pressed=evaluate('document.querySelector(\'[aria-label="Toggle terminal drawer"]\')?.getAttribute("aria-pressed")')
    if 'true' in pressed: ab('find','role','button','click','--name','Toggle terminal drawer','--exact')
    print('Composer empty; terminal closed.')
elif operation=='record-start': print(ab('record','start',str(out/(sys.argv[5]+'.mp4'))))
elif operation=='record-stop': print(ab('record','stop'))
elif operation=='ab': print(ab(*sys.argv[5:]))
else: raise SystemExit('Unknown operation')
