#!/usr/bin/env python3
"""Drive the same Android deep links on each commit. No server or account required.
Run after loading that commit's Metro bundle and reaching the empty thread list:
python3 deep-link-proof.py --session SESSION --side base --out OUTPUT_DIRECTORY
Use --side head for the fixed commit. Captures are synthetic, disconnected state.
"""
import argparse, datetime, pathlib, re, subprocess, time
p = argparse.ArgumentParser()
p.add_argument('--session', required=True)
p.add_argument('--side', choices=['base', 'head'], required=True)
p.add_argument('--out', required=True)
a = p.parse_args()
out = pathlib.Path(a.out) / (a.side + '-' + datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ'))
out.mkdir(parents=True)
log = (out / 'driver.txt').open('w')
def run(*args):
    r = subprocess.run(['agent-device', *args, '--session', a.session], text=True, capture_output=True)
    log.write('COMMAND: ' + ' '.join(args) + '\n' + r.stdout + r.stderr + '\n'); log.flush()
    if r.returncode: raise RuntimeError(r.stdout + r.stderr)
    return r.stdout

def snap(): return run('snapshot', '-i')
def press(text, state=None):
    state = state or snap()
    match = re.search(r'^(@\S+) \[(?:button|group)\] "' + re.escape(text) + r'"\s*$', state, re.M)
    if not match: raise RuntimeError('Missing control: ' + text + '\n' + state)
    return run('press', match[1], '--settle')
def capture(label):
    first, second = out / (label + '-a.png'), out / (label + '.png')
    run('screenshot', str(first), '--normalize-status-bar')
    time.sleep(0.5)
    run('screenshot', str(second), '--normalize-status-bar')
    r = subprocess.run(['compare', '-metric', 'AE', str(first), str(second), 'null:'], capture_output=True, text=True)
    log.write('STABILITY ' + label + ': ' + r.stderr + '\n'); log.flush()
    if r.returncode: raise RuntimeError('Screenshots differ: ' + label + ' ' + r.stderr)

cases = [
 ('browser-env', 'threads/%20/thread-1/browser'),
 ('browser-thread', 'threads/env-1/%20/browser'),
 ('app-item', 'threads/env-1/thread-1/apps/%20'),
 ('app-env', 'threads/%20/thread-1/apps/item-1'),
 ('app-thread', 'threads/env-1/%20/apps/item-1'),
]
run('record', 'start', str(out / 'deep-links.mp4'))
try:
    run('open', 't3code-dev://threads/env-1/thread-1/browser', '--platform', 'android')
    state = snap()
    if 'Close browser' not in state: raise RuntimeError('Control did not reach Browser')
    press('Close browser', state)
    for label, path in cases:
        run('open', 't3code-dev://' + path, '--platform', 'android')
        state = snap()
        if re.search(r'\[(?:group|button)\] "Dismiss"', state):
            press('Dismiss', state)
            state = snap()
        if a.side == 'base':
            if 'Schema validation failed' not in state: raise RuntimeError('Base bug absent: ' + label)
        elif 'Schema validation failed' in state or "This screen couldn't be displayed" in state:
            raise RuntimeError('Head still crashes: ' + label)
        elif label.startswith('app') and 'Exit full screen' not in state:
            raise RuntimeError('Missing app screen: ' + label)
        elif label.startswith('browser') and 'Close browser' not in state:
            raise RuntimeError('Missing Browser state: ' + label)
        capture(label)
        press('Go back' if a.side == 'base' else ('Close browser' if label.startswith('browser') else 'Exit full screen'), state)
finally:
    run('record', 'stop')
print(out)
