#!/usr/bin/env python3
"""Future capture fixture: run in the target thread's T3 terminal, not a shell tool."""
import argparse
import json
from pathlib import Path
import signal
import socket
import subprocess
import sys

parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--count", type=int, default=10)
parser.add_argument("--base-port", type=int, default=43100)
parser.add_argument("--bind", default="127.0.0.1")
parser.add_argument("--output", required=True)
args = parser.parse_args()
root = Path(args.output)
root.mkdir(parents=True, exist_ok=True)
web_root = root / "http-root"
web_root.mkdir(exist_ok=True)
(web_root / "index.html").write_text("<!doctype html><title>Thread servers fixture</title><h1>Thread servers fixture</h1>\n")
children = {}
logs = []

def stop(port):
    child = children.pop(port)
    if child.poll() is None:
        child.terminate()
        try:
            child.wait(timeout=5)
        except subprocess.TimeoutExpired:
            child.kill()
            child.wait()

def status():
    snapshot = [{"port": port, "pid": child.pid, "running": child.poll() is None}
                for port, child in sorted(children.items())]
    (root / "seed-pids.json").write_text(json.dumps(snapshot, indent=2) + "\n")
    print(json.dumps(snapshot), flush=True)

def set_count(count):
    if count < 0 or count > 100 or args.base_port + count > 65536:
        raise ValueError("count must be 0..100 and ports must be below 65536")
    wanted = set(range(args.base_port, args.base_port + count))
    for port in list(children):
        if port not in wanted or children[port].poll() is not None:
            stop(port)
    for port in sorted(wanted - children.keys()):
        with socket.socket() as probe:
            probe.bind((args.bind, port))
        log = (root / f"http-{port}.log").open("ab")
        logs.append(log)
        children[port] = subprocess.Popen(
            [sys.executable, "-m", "http.server", str(port), "--bind", args.bind,
             "--directory", str(web_root)],
            stdin=subprocess.DEVNULL, stdout=log, stderr=subprocess.STDOUT,
            start_new_session=True,
        )
    status()

def terminate(_signal, _frame):
    raise KeyboardInterrupt

signal.signal(signal.SIGTERM, terminate)
try:
    set_count(args.count)
    print("Commands: set N | status | quit. Keep this launcher running for terminal ancestry.", flush=True)
    for line in sys.stdin:
        parts = line.strip().split()
        if parts == ["quit"]:
            break
        if parts == ["status"]:
            status()
        elif len(parts) == 2 and parts[0] == "set":
            try:
                set_count(int(parts[1]))
            except (ValueError, OSError) as error:
                print(f"Fixture error: {error}", file=sys.stderr, flush=True)
except KeyboardInterrupt:
    pass
finally:
    for port in list(children):
        stop(port)
    for log in logs:
        log.close()
