import os,subprocess,time,threading
ROOT="/Users/oscar/.worktree/hot-reload/labs/hot-reload"; CLI="/Users/oscar/.worktree/hot-reload/target/debug/kan-hot-dev"
COMMON=os.path.join(ROOT,"api-common","src","lib.rs"); SHARED=os.path.join(ROOT,"shared","src","lib.rs")
env=dict(os.environ,RUST_LOG="error",KAN_HOT_TIMING="1",KAN_HOT_EXAMPLE_INTERVAL_MS="300",KAN_HOT_EXAMPLE_SLOW="1")
p=subprocess.Popen([CLI,"run","--bin","server","--lib","api"],cwd=ROOT,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,env=env,text=True,bufsize=1)
lines=[]; lock=threading.Lock()
def rd():
    for l in p.stdout:
        with lock: lines.append((time.monotonic(),l))
threading.Thread(target=rd,daemon=True).start()
def wait(pred,to=40):
    d=time.monotonic()+to
    while time.monotonic()<d:
        with lock:
            for t,l in lines:
                if pred(l): return t,l
        time.sleep(.005)
    raise TimeoutError
def all_lines():
    with lock: return [l for _,l in lines]
common=open(COMMON).read(); shared=open(SHARED).read()
fail=[]
try:
    wait(lambda l:"gen 1 hello" in l)
    # in-flight streaming across a reload
    wait(lambda l:"slow stream started at" in l, 10)
    open(COMMON,"w").write(common.replace("nested-v1","stream-marker"))
    wait(lambda l:'"nested":"stream-marker"' in l, 30)
    t,l=wait(lambda l:"slow stream started at" in l and "finished at" in l, 30)
    print("stream line:",l.strip())
    if "with 10 ticks" not in l: fail.append("stream ticks")
    # runtime/shared edit restarts the host and resets state
    with lock: before=len(lines)
    open(SHARED,"w").write(shared.replace('AppState::new("ctx")','AppState::new("ctx2")') if False else shared)
    # AppState::new("ctx") lives in server main; edit the shared comment instead to avoid changing API
    open(SHARED,"w").write(shared.replace("//! State which intentionally","//! State (edited) which intentionally"))
    wait(lambda l:"shared/runtime code changed" in l or "rebuilding host" in l, 60)
    wait(lambda l:"gen 1 hello" in l, 60)
    print("runtime restart observed; generation reset to 1")
finally:
    open(COMMON,"w").write(common); open(SHARED,"w").write(shared); time.sleep(1)
    try: p.stdin.write("q\n");p.stdin.flush();p.wait(timeout=15)
    except Exception: p.kill()
print("FAILURES:", fail or "none")
