"""Manual real-app driver. Use separate base/head worktrees and fresh T3 homes.
Start each with T3CODE_DEV_SEED=0 vp run dev, noting its server port.
Set PROOF_MODEL to an available inexpensive Codex model; it is never printed.
Run setup, open the returned thread in the web client, start a browser recording,
then run crash-resume. Stop the dev runner and the returned restart PID afterwards.
"""
import argparse, datetime, json, os, pathlib, signal, subprocess, time, urllib.request

p = argparse.ArgumentParser()
p.add_argument('phase', choices=['setup', 'crash-resume'])
p.add_argument('--checkout', required=True)
p.add_argument('--project', required=True)
p.add_argument('--output', required=True)
p.add_argument('--port', type=int)
p.add_argument('--web-port', type=int)
p.add_argument('--thread')
a = p.parse_args()
run = pathlib.Path(a.output) / datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%dT%H%M%SZ')
run.mkdir(parents=True)

def cli(*args):
    r = subprocess.run(['node', 'apps/server/src/bin.ts', *args], cwd=a.checkout,
                       capture_output=True, text=True, check=True)
    return r.stdout

def sample(label):
    x = json.loads(cli('thread', 'show', '--base-dir', '.t3', a.thread, '--messages', '100', '--json'))
    files = sorted(pathlib.Path(a.project).glob('*.txt'))
    # Publish only synthetic conversation text, status, file basenames and timestamps.
    def clean(s):
        return s.replace(str(pathlib.Path(a.project).resolve()), '<project>')
    data = {'at': datetime.datetime.now(datetime.timezone.utc).isoformat(), 'label': label,
            'status': x['thread']['status'], 'itemCount': x['thread']['itemCount'],
            'messages': [{'role': m['role'], 'text': clean(m['text'])} for m in x['messages']],
            'files': [{'name': f.name, 'mtime': f.stat().st_mtime} for f in files]}
    (run / (label + '.json')).write_text(json.dumps(data, indent=2))
    print(label, data['status'], data['itemCount'], len(files), flush=True)

if a.phase == 'setup':
    pathlib.Path(a.project).mkdir(parents=True, exist_ok=True)
    subprocess.run(['git', 'init', '-q', a.project], check=True)
    cli('project', 'add', '--base-dir', '.t3', '--title', 'Goal restart proof', a.project)
    print(cli('thread', 'start', '--base-dir', '.t3', '--provider', 'codex', '--model',
              os.environ['PROOF_MODEL'], '--title', 'Goal restart proof', a.project,
              '/goal Create thirty numbered text files one per turn. After each file end your turn and leave the goal active until all thirty exist.', '--json'))
else:
    sample('before-crash')
    sockets = subprocess.check_output(['ss', '-H', '-ltnp', 'sport = :' + str(a.port)], text=True)
    import re
    pid = int(re.search(r'pid=(\d+)', sockets).group(1))
    cwd = pathlib.Path('/proc/' + str(pid) + '/cwd').resolve()
    if not cwd.is_relative_to(pathlib.Path(a.checkout).resolve()):
        raise RuntimeError('Port owner does not belong to this checkout')
    os.kill(pid, signal.SIGKILL)
    log = open(run / 'restart-private.log', 'w')
    child = subprocess.Popen(['node', 'apps/server/src/bin.ts', '--port', str(a.port),
                              '--base-dir', '.t3', '--dev-url', 'http://localhost:' + str(a.web_port),
                              '--no-browser'], cwd=a.checkout,
                             env=dict(os.environ, T3CODE_DEV_SEED='0'), stdout=log, stderr=log)
    print('restart PID:', child.pid, flush=True)
    for _ in range(60):
        try:
            urllib.request.urlopen('http://127.0.0.1:' + str(a.port) + '/.well-known/t3/environment', timeout=1)
            break
        except Exception:
            time.sleep(1)
    for n in range(12):
        sample('after-restart-' + str(n))
        time.sleep(5)
    cli('thread', 'send', '--base-dir', '.t3', a.thread, 'continue', '--json')
    for n in range(6):
        sample('after-continue-' + str(n))
        time.sleep(5)
    print('Keep restart-private.log private; publish only a sanitized excerpt.')
