#!/usr/bin/env python3
"""fresh E2E v16 — 텍스트 스텝만 순차 실행 (2026-07-08, 커밋 금지).

category=analysis run-all 이 cross-category 가드(floor_plan_render 미완료)로
거부되어, fp-render 에 전이 의존하지 않는 analysis 스텝 39개만 단일 스텝
API 로 순차 실행한다 (목록=scratchpad/e2e_v16_steps.json 의 runnable —
step_manifest 전이 의존 계산 산출). 이미지 스텝 호출 0.

각 스텝: POST /steps/{id} (백그라운드 스레드) → GET /steps 폴링(30s)
→ completed 진행 / failed·중단 상태면 즉시 종료(보고).
usage: backend/.venv/bin/python e2e_v16_seq_runner.py
"""
import json
import sys
import time
from pathlib import Path

import requests

BASE = "http://localhost:8000"
ROOT = Path("/Users/manta/Documents/Projects/TheRoad-I1")
STATE = json.loads((ROOT / "scratchpad" / "e2e_v16_state.json").read_text()) \
    if (ROOT / "scratchpad" / "e2e_v16_state.json").exists() else \
    json.loads((ROOT / "scratchpad" / "e2e_geum_v16_state.json").read_text())
PID, EID = STATE["project_id"], STATE["episode_id"]
STEPS = json.loads((ROOT / "scratchpad" / "e2e_v16_steps.json").read_text())["runnable"]

TERMINAL_OK = {"completed", "skipped", "not_applicable"}
TERMINAL_BAD = {"failed"}

s = requests.Session()


def login():
    r = s.post(f"{BASE}/api/v1/auth/login",
               json={"username": "admin", "password": "admin123"}, timeout=30)
    r.raise_for_status()


def status_map():
    r = s.get(f"{BASE}/api/v1/projects/{PID}/episodes/{EID}/steps", timeout=60)
    r.raise_for_status()
    return {x["step_id"]: x for x in r.json()["steps"]}


def run_step(sid):
    t0 = time.time()
    st = status_map().get(sid) or {}
    if st.get("status") in TERMINAL_OK:
        print(f"[{sid}] already {st.get('status')}", flush=True)
        return True
    r = s.post(f"{BASE}/api/v1/projects/{PID}/episodes/{EID}/steps/{sid}",
               params={"mode": "resume"}, timeout=120)
    if r.status_code != 200:
        print(f"[{sid}] start FAILED http={r.status_code} {r.text[:300]}",
              flush=True)
        return False
    print(f"[{sid}] started", flush=True)
    while True:
        time.sleep(30)
        try:
            st = status_map().get(sid) or {}
        except Exception as exc:
            print(f"[{sid}] poll error {exc}", flush=True)
            continue
        cur = st.get("status")
        if cur in TERMINAL_OK:
            print(f"[{sid}] {cur} ({int(time.time()-t0)}s)", flush=True)
            return True
        if cur in TERMINAL_BAD:
            print(f"[{sid}] FAILED ({int(time.time()-t0)}s) — "
                  f"{json.dumps(st, ensure_ascii=False)[:400]}", flush=True)
            return False
        if time.time() - t0 > 5400:
            print(f"[{sid}] TIMEOUT 90min (status={cur})", flush=True)
            return False


def main():
    login()
    print(f"target {len(STEPS)} steps | pid={PID} eid={EID}", flush=True)
    for sid in STEPS:
        if not run_step(sid):
            print(f"STOP at {sid}", flush=True)
            sys.exit(1)
    print("ALL TEXT STEPS DONE", flush=True)


if __name__ == "__main__":
    main()
