#!/usr/bin/env python3
"""Drive a fresh T3 web app with the adjacent synthetic Codex protocol fixture.
Usage: drive.py CHECKOUT SERVER_LOG AGENT_BROWSER OUTPUT_ROOT before|after
The app must be running with seed disabled and Codex binaryPath set to ./codex.
All generated state is synthetic. Pairing credentials stay in memory.
"""
import datetime
import hashlib
import json
import pathlib
import re
import subprocess
import sys
import time

checkout, server_log, browser, output_root, side = sys.argv[1:]
checkout = pathlib.Path(checkout)
root = pathlib.Path(output_root)
out = root / (side + '-' + datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ'))
out.mkdir(parents=True)
session = 'question-id-proof-' + side

def run(args, cwd=None):
    return subprocess.check_output([str(a) for a in args], cwd=cwd, text=True, stderr=subprocess.STDOUT)

def cli(*args):
    return run(['node', 'apps/server/src/bin.ts', *args, '--base-dir', '.t3'], checkout)

def ab(*args):
    return run([browser, '--session', session, *args])

workspace = root / 'demo'
if not workspace.exists():
    workspace.mkdir()
    run(['git', 'init', '--quiet', '--initial-branch=main', workspace])
    run(['git', '-c', 'user.name=Scenario', '-c', 'user.email=scenario@example.invalid', 'commit', '--allow-empty', '--quiet', '-m', 'Initial'], workspace)
cli('project', 'add', str(workspace), '--title', 'Question ID proof')
started = json.loads(cli('thread', 'start', str(workspace), 'Ask me to choose A.', '--title', 'Codex whitespace question', '--provider', 'codex', '--model', 'gpt-5.6-luna', '--json'))
pairing = re.findall(r'pairingUrl:\s*(\S+)', pathlib.Path(server_log).read_text())[-1]
ab('open', pairing)
ab('set', 'viewport', '1280', '800')
ab('wait', '--text', 'Codex whitespace question')
ab('find', 'role', 'button', 'click', '--name', 'Codex whitespace question, Input, Question ID proof')
ab('wait', '--text', 'Send answer A to Codex')
if 'region "Thread details"' in ab('snapshot', '-i'):
    ab('find', 'role', 'button', 'click', '--name', 'Toggle thread details panel')
ab('record', 'start', out / 'interaction.mp4')
time.sleep(2)
ab('find', 'role', 'button', 'click', '--name', 'A Send answer A to Codex 1')
ab('wait', '--text', 'Codex protocol fixture received:')
time.sleep(2)
ab('press', 'Escape')
ab('eval', 'document.activeElement.blur()')
# Captures are accepted only after two renders 500 ms apart match pixel for pixel.
for attempt in range(10):
    first, second = out / f'stability-{attempt}.png', out / 'result.png'
    ab('screenshot', first)
    time.sleep(0.5)
    ab('screenshot', second)
    comparison = subprocess.run(['compare', '-metric', 'AE', str(first), str(second), 'null:'], capture_output=True, text=True)
    if comparison.returncode == 0:
        break
else:
    raise RuntimeError('The final view did not stabilize')
(out / 'page.txt').write_text(ab('snapshot'))
ab('record', 'stop')
print(json.dumps({'side': side, 'output': str(out), 'threadId': started['threadId'], 'stablePixels': True}))
