#!/usr/bin/env python3
"""MAI-Image-2.5 (OpenRouter) 운영 왕복 실측 파일럿 — 프로덕션 무접촉.

배경: 최종 이미지 생성 모델 교체 후보(#103). 편입 판단 전에 운영 왕복을
실측한다(오프라인 배치 재평가는 지연·불안정을 못 보여준다는 QK 전례).

물음(한 실행=한 물음):
  T1  텍스트만 → 이미지가 오는가 (응답 shape·지연·크기)
  T2  참조 1장 첨부 → 수신은 되는가 (거부/무시/반영 — 반영 여부는 육안)
  T3  다중 참조 첨부 → 스틸 조립 전제(라벨 텍스트+이미지 N장)가 성립하는가

사용:
  python3 mai_image_pilot.py --tag t1 --prompt "..."
  python3 mai_image_pilot.py --tag t3 --prompt-file p.txt \
      --ref "SHOT BACKGROUND" bg.png --ref "LAYOUT SKETCH" sketch.png

출력: --out 디렉토리에 <tag>_<i>.png 저장 + calls.json 에 왕복 기록 append.
실패도 데이터다 — 재시도하지 않고 그대로 기록한다.
"""
from __future__ import annotations

import argparse
import base64
import json
import mimetypes
import time
from datetime import datetime, timezone
from pathlib import Path

import requests

ROOT = Path(__file__).resolve().parent
DEFAULT_MODEL = "microsoft/mai-image-2.5"
ENDPOINT = "https://openrouter.ai/api/v1/chat/completions"
TIMEOUT_S = 420


def load_api_key() -> str:
    for line in (ROOT / ".env").read_text(encoding="utf-8").splitlines():
        if line.startswith("OPENROUTER_API_KEY="):
            return line.split("=", 1)[1].strip()
    raise SystemExit("OPENROUTER_API_KEY 가 backend/.env 에 없다")


def image_part(path: Path) -> dict:
    mime = mimetypes.guess_type(str(path))[0] or "image/png"
    b64 = base64.b64encode(path.read_bytes()).decode()
    return {"type": "image_url", "image_url": {"url": f"data:{mime};base64,{b64}"}}


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("--tag", required=True, help="기록 식별자 (t1/t2/t3 …)")
    ap.add_argument("--model", default=DEFAULT_MODEL)
    ap.add_argument("--prompt")
    ap.add_argument("--prompt-file")
    ap.add_argument("--ref", nargs=2, action="append", default=[],
                    metavar=("LABEL", "PATH"),
                    help="라벨 텍스트 + 이미지 경로 (반복 가능)")
    ap.add_argument("--out", default=str(ROOT.parent / "artifact" / "20260813_MAI이미지25_실측"))
    ap.add_argument("--modalities", default="image,text",
                    help='쉼표 목록. "none"=파라미터 자체를 뺀다')
    args = ap.parse_args()

    prompt = args.prompt or Path(args.prompt_file).read_text(encoding="utf-8")
    out_dir = Path(args.out)
    out_dir.mkdir(parents=True, exist_ok=True)

    content: list[dict] = [{"type": "text", "text": prompt}]
    for label, ref_path in args.ref:
        p = Path(ref_path)
        if not p.is_file():
            raise SystemExit(f"참조 파일 없음: {p}")
        content.append({"type": "text", "text": label})
        content.append(image_part(p))

    body = {
        "model": args.model,
        "messages": [{"role": "user", "content": content}],
    }
    if args.modalities != "none":
        body["modalities"] = [m.strip() for m in args.modalities.split(",") if m.strip()]

    rec: dict = {
        "ts": datetime.now(timezone.utc).isoformat(),
        "tag": args.tag,
        "model": args.model,
        "prompt_chars": len(prompt),
        "n_refs": len(args.ref),
        "ref_labels": [l for l, _ in args.ref],
    }

    t0 = time.monotonic()
    try:
        resp = requests.post(
            ENDPOINT,
            headers={"Authorization": f"Bearer {load_api_key()}",
                     "Content-Type": "application/json"},
            json=body, timeout=TIMEOUT_S)
        rec["latency_s"] = round(time.monotonic() - t0, 1)
        rec["http_status"] = resp.status_code
        try:
            data = resp.json()
        except ValueError:
            rec["error"] = f"비JSON 응답 (앞 300자): {resp.text[:300]!r}"
            data = None
        if data is not None:
            if "error" in data:
                rec["error"] = json.dumps(data["error"], ensure_ascii=False)[:500]
            choices = data.get("choices") or []
            msg = (choices[0].get("message") or {}) if choices else {}
            rec["finish_reason"] = choices[0].get("finish_reason") if choices else None
            rec["usage"] = data.get("usage")
            text = msg.get("content")
            rec["text_reply"] = (text or "")[:300] if isinstance(text, str) else text
            images = msg.get("images") or []
            saved: list[dict] = []
            for i, im in enumerate(images, 1):
                url = ((im.get("image_url") or {}).get("url")
                       if isinstance(im, dict) else "") or ""
                if url.startswith("data:"):
                    header, b64 = url.split(",", 1)
                    raw = base64.b64decode(b64)
                    ext = ".png" if "png" in header else ".jpg"
                    fp = out_dir / f"{args.tag}_{i}{ext}"
                    fp.write_bytes(raw)
                    saved.append({"file": fp.name, "bytes": len(raw)})
                else:
                    saved.append({"url_head": url[:120]})
            rec["n_images"] = len(images)
            rec["saved"] = saved
    except requests.Timeout:
        rec["latency_s"] = round(time.monotonic() - t0, 1)
        rec["error"] = f"timeout {TIMEOUT_S}s"
    except Exception as exc:  # 실측 기록이 목적 — 종류 불문 남긴다
        rec["latency_s"] = round(time.monotonic() - t0, 1)
        rec["error"] = f"{type(exc).__name__}: {exc}"[:500]

    calls = out_dir / "calls.json"
    hist = json.loads(calls.read_text(encoding="utf-8")) if calls.is_file() else []
    hist.append(rec)
    calls.write_text(json.dumps(hist, ensure_ascii=False, indent=1), encoding="utf-8")
    print(json.dumps(rec, ensure_ascii=False, indent=1))


if __name__ == "__main__":
    main()
