#!/usr/bin/env python3
"""vnkr AI 해설보기(단어) — gpt-4.1-mini vs gpt-5.6-luna 100회 비교 테스트.

앱 요청 재현: ChatRequestModel.getStepOnePrompt(TYPE.WORD) → buildGptRequest 와 동일한 본문.
- 메시지 순서: [user 질문, developer(system) 프롬프트] (클라이언트가 user 를 먼저 넣음)
- 4.1-mini: 실서비스 그대로 (temperature/top_p/2500, X-App-Id vnkr)
- luna: 구클라이언트 형태 그대로 보내 워커 정규화 경로 검증 (X-App-Id 는 openai_5_key 등록된 jpkr 로 우회)
"""
import json, time, threading, queue, re, sys
import requests

SP = "/private/tmp/claude-501/-Users-ymjung-Project-wordbit2/4ac326f2-ee60-44f4-ab5c-4b7aff5d25e6/scratchpad"
URL = "https://all-ai-proxy.brainup.workers.dev/v1/responses"
HEADERS_BASE = {
    "Content-Type": "application/json",
    "X-Proxy-Secret": "8e0851268439bdeb5338c17c2152fa0d38a559aceb85c0485e64481b95de653d",
    "X-Client-Key": "575f3f6bc071de763a61d755e828aefe10ca28bd07be474adbc9827c7a9fe46b",
}
APP_ID = {"gpt-4.1-mini": "net.wordbit.vnkr", "gpt-5.6-luna": "net.wordbit.jpkr"}

PROMPT = open(f"{SP}/step_word_prompt.txt").read()
ITEMS = json.load(open(f"{SP}/vnkr_items_100.json"))

def build_body(model, question):
    # GptApiRemoteSource.buildGptRequest (main 브랜치) 재현 — user 먼저, developer 나중
    return {
        "model": model,
        "input": [
            {"role": "user", "content": [{"type": "input_text", "text": question}]},
            {"role": "developer", "content": [{"type": "input_text", "text": PROMPT}]},
        ],
        "stream": True,
        "temperature": 1.0,
        "max_output_tokens": 2500,
        "top_p": 1.0,
    }

def one_request(model, item, attempt=1):
    body = build_body(model, item["question"])
    rec = {"model": model, "id": item["id"], "content": item["content"],
           "question": item["question"], "attempt": attempt}
    t0 = time.time()
    ttfb = None
    text_parts = []
    status = None
    usage = None
    final_status = None
    try:
        with requests.post(URL, headers={**HEADERS_BASE, "X-App-Id": APP_ID[model]},
                           json=body, stream=True, timeout=(15, 180)) as r:
            status = r.status_code
            if r.status_code != 200:
                rec.update(ok=False, http=status, error=r.text[:300], elapsed=time.time()-t0)
                return rec
            event = ""
            for raw in r.iter_lines(decode_unicode=True):
                if raw is None:
                    continue
                if raw.startswith("event:"):
                    event = raw[6:].strip()
                elif raw.startswith("data:"):
                    data = raw[5:].strip()
                    if not data:
                        continue
                    if event == "response.output_text.delta":
                        if ttfb is None:
                            ttfb = time.time() - t0
                        try:
                            text_parts.append(json.loads(data).get("delta", ""))
                        except Exception:
                            pass
                    elif event in ("response.completed", "response.incomplete"):
                        final_status = event
                        try:
                            resp = json.loads(data).get("response", {})
                            usage = resp.get("usage")
                        except Exception:
                            pass
    except Exception as e:
        rec.update(ok=False, http=status, error=f"{type(e).__name__}: {e}"[:300],
                   elapsed=time.time()-t0)
        return rec
    text = "".join(text_parts)
    rec.update(ok=bool(text) and final_status == "response.completed",
               http=status, final=final_status, ttfb=ttfb, elapsed=time.time()-t0,
               text=text, usage=usage)
    return rec

def worker(q, out, lock):
    while True:
        try:
            model, item = q.get_nowait()
        except queue.Empty:
            return
        rec = one_request(model, item)
        if not rec.get("ok") and rec.get("attempt", 1) == 1:
            time.sleep(2)
            rec = one_request(model, item, attempt=2)
        with lock:
            out.write(json.dumps(rec, ensure_ascii=False) + "\n")
            out.flush()
            done = sum(1 for _ in open(f"{SP}/results.jsonl"))
            if done % 20 == 0:
                print(f"progress: {done}/{2*len(ITEMS)}", flush=True)
        q.task_done()

def main():
    q = queue.Queue()
    for item in ITEMS:
        q.put(("gpt-4.1-mini", item))
        q.put(("gpt-5.6-luna", item))
    lock = threading.Lock()
    out = open(f"{SP}/results.jsonl", "w")
    threads = [threading.Thread(target=worker, args=(q, out, lock), daemon=True) for _ in range(8)]
    t0 = time.time()
    for t in threads: t.start()
    for t in threads: t.join()
    out.close()
    recs = [json.loads(l) for l in open(f"{SP}/results.jsonl")]
    ok = sum(1 for r in recs if r.get("ok"))
    print(f"done: {len(recs)} requests, {ok} ok, {time.time()-t0:.0f}s total")

if __name__ == "__main__":
    main()
