"""무인 샷에 나간 CARRIED 문안 중 **사람의 몸을 말하는 것**이 몇인가.

감사 1-B 의 크기를 확정한다. 앞 도구(`audit_person_clauses_in_bgonly.py`)
가 무인 샷 882개 중 212개(24%)에 CARRIED 가 함께 나갔음을 셌다. 그런데
**동반한다고 다 문제는 아니다** — 사물만 말하는 문안이 섞여 있다.

    문제 없음   "The rusty iron door remains half open."
    문제 있음   "Jihu still wears her antler-bone pendant."

이 둘을 글자로 못 가른다(사진 **속** 인물은 사물의 일부다). 그래서
모델에게 묻는다 — 이 저장소의 방식이다.

★같이 재는 것: **가르는 일이 애초에 가능한가.** 이 판이 세울 계약은
 저작이 두 칸으로 나눠 내는 것이고, 그 전제가 「모델이 이 구분을 할 수
 있다」다. 나눈 결과(사물만 남긴 문안)를 함께 받아 눈으로 확인한다.

    $ .venv/bin/python tools/prompt_measure/audit_carried_person_share.py [--limit N]
"""
from __future__ import annotations

import argparse
import json
import sys
from collections import Counter
from pathlib import Path

sys.path.insert(0, "/Users/manta/Documents/Projects/TheRoad-I1/scratchpad")
import _opik_env  # noqa: E402,F401  ★cwd 를 backend 로 고정

IN = Path(__file__).resolve().parent / "_carried_unmanned.json"
OUT = Path(__file__).resolve().parent / "_carried_person_share.json"

SYS = """You are given a CARRIED STATE line from a storyboard prompt. It
states what must stay consistent with the neighbouring shots of the scene.

This particular shot renders **no people at all** — it is a plate of the
place. So the line must not describe a living person's body, clothing,
posture or action; if it does, the two instructions contradict each other
and the image model has to pick one.

Split the line into two, keeping the original wording wherever you can:

  objects_en  Things, installations, marks, traces, weather, light, state
              of doors and rooms. A person depicted INSIDE a photograph,
              painting or screen is part of that object — keep it here.
  people_en   A living person's body, what they wear or hold, where they
              stand, what they are doing.

If nothing belongs in one of them, return an empty string for it. Do not
add anything that is not in the given line."""

SCHEMA = {
    "type": "object",
    "additionalProperties": False,
    "properties": {
        "objects_en": {"type": "string"},
        "people_en": {"type": "string"},
        "reason_ko": {"type": "string"},
    },
    "required": ["objects_en", "people_en", "reason_ko"],
}


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--limit", type=int, default=0,
                    help="0 이면 전부")
    ap.add_argument("--model", default="gemini-flash")
    a = ap.parse_args()

    rows = json.loads(IN.read_text(encoding="utf-8"))
    if a.limit:
        rows = rows[:a.limit]
    print(f"■ 고유 CARRIED 문안 {len(rows)}개를 {a.model} 로 가른다\n")

    from app.modules.llm.llm_client import call_structured

    tag = "carried_person_split_probe"
    pc = {tag: {"model": a.model}}
    out, c = [], Counter()
    for i, r in enumerate(rows):
        text = r["text"]
        try:
            got = call_structured(
                tag, SYS, [{"type": "text", "text": text}], SCHEMA,
                project_config=pc, schema_name=tag)
        except Exception as exc:  # noqa: BLE001 — 측정은 계속한다
            c["error"] += 1
            out.append({**r, "error": f"{type(exc).__name__}: {exc}"[:200]})
            continue
        ppl = str(got.get("people_en") or "").strip()
        obj = str(got.get("objects_en") or "").strip()
        kind = ("사람만" if ppl and not obj
                else "섞임" if ppl and obj
                else "사물만" if obj else "빈값")
        c[kind] += 1
        out.append({**r, "objects_en": obj, "people_en": ppl,
                    "reason_ko": got.get("reason_ko"), "kind": kind})
        if (i + 1) % 20 == 0:
            print(f"  … {i+1}/{len(rows)}")

    n = sum(c[k] for k in ("사람만", "섞임", "사물만", "빈값")) or 1
    print(f"\n■ 결과 ({n}개)")
    for k in ("사람만", "섞임", "사물만", "빈값"):
        print(f"    {k:<5} {c[k]:>4}개  ({100.0*c[k]/n:.0f}%)")
    if c["error"]:
        print(f"    ★실패 {c['error']}")
    bad = c["사람만"] + c["섞임"]
    print(f"\n  사람을 말하는 문안 {bad}개 / {n} ({100.0*bad/n:.0f}%)"
          f" — 무인 샷과 모순되는 쪽")

    print("\n■ 가른 결과 표본 — **계약이 가능한가**를 눈으로 본다")
    shown = 0
    for r in out:
        if r.get("kind") != "섞임" or shown >= 8:
            continue
        shown += 1
        print(f"\n  원문 : {r['text'][:150]}")
        print(f"  사물 : {r['objects_en'][:150]}")
        print(f"  사람 : {r['people_en'][:150]}")

    OUT.write_text(json.dumps(out, ensure_ascii=False, indent=1),
                   encoding="utf-8")
    print(f"\n→ {OUT.name}")
    return 0


if __name__ == "__main__":
    sys.exit(main())
