#!/usr/bin/env python3
"""프롬프트를 **하나도 안 바꾸고** scene_detail 을 N 번 돌려 흔들림 폭을 잰다.

같은 프롬프트로도 LLM 답은 매번 다르다. 그 폭을 모르면 「고쳤더니 좋아졌다/
나빠졌다」를 말할 수 없다 — 이번에 R1 이 0 → 2 로 늘었는데 그것이 수정 탓인지
원래 흔들리는 폭인지 못 가렸다.

usage: repeat_scene_detail.py [횟수]
"""
import json
import re
import sys
import time

import requests

sys.path.insert(0, "/Users/manta/Documents/Projects/TheRoad-I1/scratchpad")
from _opik_env import opik_target  # ★cwd 를 backend 로 고정 + 미로드 시 중단
from tools.opik_prompt_audit.audit.fetch import fetch_spans
from app.core.config import settings

B = "http://localhost:8000"
ST = json.loads(open("/Users/manta/Documents/Projects/TheRoad-I1/"
                     "scratchpad/minimal_e2e_state.json").read())
P, E = ST["project_id"], ST["episode_id"]
N = int(sys.argv[1]) if len(sys.argv) > 1 else 3

R1 = re.compile(r"\b(close-?up|wide shot|medium shot|long shot|wide view|"
                r"establishing shot|full shot)\b", re.I)
R3 = re.compile(r"\b\d+\s?(cm|mm|m|meters?|inch\w*|feet|ft)\b", re.I)
R4 = re.compile(r"(lights?\s+(?:are\s+)?(?:off|out)|goes?\s+out|went\s+out|"
                r"gone\s+dark|black-?out|power\s+cut|power\s+fail\w*|"
                r"switched?\s+off|turned?\s+off|cuts?\s+out|unlit|"
                r"darkness|pitch\s+dark|extinguish\w*|momentary\s+dark\w*)", re.I)

BASE, WS, PROJ = opik_target()



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


def run_once(i):
    """force 재실행 → 완료 대기. 시작 시각(UTC)을 돌려준다."""
    import datetime as dt
    t0 = dt.datetime.now(dt.timezone.utc) - dt.timedelta(seconds=30)
    while True:
        r = s.post(f"{B}/api/v1/projects/{P}/episodes/{E}/steps/scene_detail",
                   params={"mode": "force"}, timeout=120)
        if r.status_code == 200:
            break
        # 앞 작업이 아직 안 끝나 중복으로 거부되면 기다렸다 다시
        print(f"    (시작 대기 {r.status_code})", flush=True)
        time.sleep(15)
    for _ in range(120):
        time.sleep(10)
        st = {x["step_id"]: x for x in s.get(
            f"{B}/api/v1/projects/{P}/episodes/{E}/steps",
            timeout=60).json()["steps"]}["scene_detail"]
        if st["status"] in ("completed", "failed", "skipped"):
            return t0.strftime("%Y-%m-%dT%H:%M:%S"), st["status"]
    return t0.strftime("%Y-%m-%dT%H:%M:%S"), "timeout"


def measure(since):
    spans = fetch_spans(BASE, WS, PROJ, since, "2026-08-26T00:00:00")
    sd = [x for x in spans if "op:scene_detail" in (x.get("tags") or [])]
    rows = []
    for x in sd:
        try:
            c = json.loads(x["output"]["choices"][0]["message"]["content"])
        except Exception:
            continue
        for v in c.get("t2i_variations") or []:
            p = v.get("t2i_prompt") or ""
            rows.append((len(p), bool(R1.search(p)), bool(R3.search(p)),
                         bool(R4.search(p))))
    return rows


print(f"프롬프트 고정 · {N}회 반복\n")
hist = []
for i in range(1, N + 1):
    since, status = run_once(i)
    rows = measure(since)
    if not rows:
        print(f"  {i}회차: 출력 못 읽음 ({status})", flush=True)
        continue
    n = len(rows)
    r1 = sum(1 for r in rows if r[1])
    r3 = sum(1 for r in rows if r[2])
    r4 = sum(1 for r in rows if r[3])
    avg = sum(r[0] for r in rows) // n
    hist.append((r1, r3, r4, avg))
    print(f"  {i}회차 ({status}): R1 {r1}/{n} · R3 {r3}/{n} · R4 {r4}/{n} "
          f"· 평균 {avg:,}자", flush=True)

if hist:
    print("\n── 흔들림 폭 (같은 프롬프트) ──")
    for idx, name in ((0, "R1 샷타입"), (1, "R3 실수치"), (2, "R4 조명")):
        vals = [h[idx] for h in hist]
        print(f"  {name}: {vals}  → 최소 {min(vals)} 최대 {max(vals)}")
    lens = [h[3] for h in hist]
    print(f"  평균 길이: {lens}  → {min(lens):,}~{max(lens):,}자")
