#!/usr/bin/env python3
import os, subprocess, sys, threading, time

LAB = "/Users/oscar/.worktree/hot-reload/labs/hot-reload"
CLI = "/Users/oscar/.worktree/hot-reload/target/debug/kan-hot-dev"
API = os.path.join(LAB, "api-common/src/lib.rs")
SHARED = os.path.join(LAB, "shared/src/lib.rs")

api_original = open(API).read()
shared_original = open(SHARED).read()
env = dict(os.environ, RUST_LOG="info", KAN_HOT_TIMING="1",
           KAN_HOT_EXAMPLE_INTERVAL_MS="50", KAN_HOT_EXAMPLE_SLOW="1")
p = subprocess.Popen([CLI, "run", "--bin", "server", "--lib", "api"], cwd=LAB,
                     stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                     env=env, text=True, bufsize=1)
lines = []
lock = threading.Lock()
def reader():
    for line in p.stdout:
        with lock: lines.append(line.rstrip())
threading.Thread(target=reader, daemon=True).start()

def wait(pred, timeout=40):
    end = time.monotonic() + timeout
    while time.monotonic() < end:
        with lock:
            for l in reversed(lines):
                if pred(l): return l
        time.sleep(0.005)
    raise TimeoutError

def dump_since(prefix):
    with lock:
        for l in lines:
            if any(k in l for k in ("slow stream", "shared/runtime", "change detected", "loaded generation", "host exited", "generation:")):
                print("   ", l[:160])

try:
    wait(lambda l: "slow stream started at 1" in l)
    print("slow stream running on gen 1; editing nested hot crate...")
    tmp = API + ".swap"
    open(tmp, "w").write(api_original.replace("nested-v1", "verify-nested"))
    os.replace(tmp, API)
    wait(lambda l: "gen 2 hello" in l and "verify-nested" in l)
    print("nested hot reload OK (gen 2 serving)")
    finished = wait(lambda l: "slow stream started at 1 finished at" in l, timeout=10)
    print("in-flight stream:", finished[:160])
    assert "with 10 ticks" in finished, finished

    print("editing runtime crate (shared)...")
    tmp = SHARED + ".swap"
    open(tmp, "w").write(shared_original + "\n// touched\n")
    os.replace(tmp, SHARED)
    wait(lambda l: "shared/runtime code changed" in l)
    # A restarted host prints generation: 1 again and calls reset.
    time.sleep(1.5)
    with lock:
        restarts = sum(1 for l in lines if l.startswith("generation: 1"))
    print("host restart detected:", restarts >= 2)
    assert restarts >= 2
    print("ALL SCENARIOS OK")
finally:
    open(API, "w").write(api_original)
    open(SHARED, "w").write(shared_original)
    time.sleep(0.4)
    try:
        p.stdin.write("q\n"); p.stdin.flush()
    except BrokenPipeError:
        pass
    try:
        p.wait(timeout=10)
    except subprocess.TimeoutExpired:
        p.kill()
    dump_since("")
