"""정적 15,309자를 성격별로 가른다 — 무엇이 재료이고 무엇이 사족인가."""
import json,re,sys
from collections import defaultdict
sys.path.insert(0,"/Users/manta/Documents/Projects/TheRoad-I1/scratchpad")
from _opik_env import opik_target
from tools.opik_prompt_audit.audit.fetch import fetch_spans
B,W,P=opik_target()
cards=[]
for x in sorted((s for s in fetch_spans(B,W,P,"2026-08-24T19:20:00","2026-12-31T00:00:00")
                 if "op:scene_detail" in (s.get("tags") or [])),key=lambda s:s["start_time"]):
    inp=x.get("input"); msgs=inp if isinstance(inp,list) else (inp or {}).get("messages") or []
    u="".join((m.get("content") or "") for m in msgs if m.get("role")=="user" and isinstance(m.get("content"),str))
    m2=re.search(r"\[RenderPromptCard v1\]\n(\{.*?\})\n",u,re.S)
    if m2:
        try: cards.append(json.loads(m2.group(1)))
        except Exception: pass
def walk(node, path=""):
    if isinstance(node, dict):
        for k,v in node.items(): yield from walk(v, f"{path}.{k}")
    elif isinstance(node, list): yield path, json.dumps(node,ensure_ascii=False,sort_keys=True)
    else: yield path, json.dumps(node,ensure_ascii=False)
vals=defaultdict(set); size=defaultdict(int)
for c in cards:
    for p,v in walk(c): vals[p].add(v); size[p]=max(size[p],len(v))
static={p:size[p] for p in vals if len(vals[p])==1}

KIND=[("왜 그런지 (rationale)", r"rationale|_summary$|scope_summary"),
      ("금지 나열 (forbidden)",  r"forbidden"),
      ("자가 점검 (self_check)", r"self_check"),
      ("권장 재료 (recommended·required)", r"recommend|required_phras|required_handling"),
      ("규칙 문장 (constraints)", r"constraints"),
      ("그 밖의 값",             r".*")]
used=set(); tot=sum(static.values())
print(f"정적 {tot:,}자 (카드의 73%) 를 성격별로\n")
print(f"{'성격':<34}{'자수':>9}{'비중':>7}  대표")
for name,pat in KIND:
    ks=[p for p in static if p not in used and re.search(pat,p)]
    used|=set(ks); s=sum(static[k] for k in ks)
    top=sorted(ks,key=lambda k:-static[k])[:1]
    print(f"{name:<34}{s:>8,}자{100*s/tot:>6.1f}%  {top[0] if top else ''}")
