#!/usr/bin/env python3
"""task-3078 작업 A-2 — '결선 무방비 지도' 실측.

목적: "어디가 무방비인지" 표를 **추측이 아니라 실측**으로 만든다.

방법: 프롬프트 조립·권한 결선 라인을 하나씩 끊고(변이) **전체 서버 스위트**를
      오라클로 돌린다. 살아남으면(SURVIVED) 그 결선은 아무도 지키지 않는다는 뜻이다.

★ no-op 선증명 · 파이프 없이 반환코드 직접 확인 · worktree 제자리 실행.
"""
from __future__ import annotations

import json
import re
import shutil
import subprocess
import sys
import time
from pathlib import Path

SERVER = Path(__file__).resolve().parent
MAIN = SERVER / "main.py"

# (id, 무엇이 죽는가, 원문, 치환문)
CANDIDATES = [
    ("seo_mode→build_content_prompt", "SEO 노하우 모드 전체(t3077 A4)",
     "        seo_mode=seo_mode,\n", ""),
    ("seo_mode→build_benchmark_prompt", "벤치마킹 키워드 횟수 규칙(t3077 A5)",
     "build_benchmark_prompt(req.benchmark, seo_mode)", "build_benchmark_prompt(req.benchmark)"),
    ("benchmark_prompt→조립", "환각금지·분량·이미지 규칙 전체(t3072)",
     "        benchmark_prompt=benchmark_prompt,\n", ""),
    ("benchmark 객체→조립", "이미지 구획 지시(t3070 B-1/t3074)",
     "        benchmark=req.benchmark if benchmark_prompt else None,\n", ""),
    ("compliance_prompt→조립", "금소법 프롬프트",
     "        compliance_prompt=compliance_prompt,\n", ""),
    ("channel_prompt→조립", "채널별 작성 규칙(③)",
     "        channel_prompt=req.channelPrompt,\n", ""),
    ("filtered_skills→조립", "플랜 스킬 필터(권한 우회)",
     "        skills=filtered_skills,", "        skills=requested_skills,"),
    ("profile_prompt→조립", "설계사 프로필·등록번호 하단문구",
     "        profile_prompt=profile_prompt,\n", ""),
    ("personal_reg_prompt→조립", "설계사 개인 추가 규정",
     "        personal_reg_prompt=personal_reg_prompt,\n", ""),
    ("allowed_skills 결선", "플랜별 허용 스킬 원천",
     "    allowed_skills = plan_tier.skills", "    allowed_skills = plan_catalog.HIDDEN_TIER.skills"),
    ("allowed_channels 결선", "플랜별 채널 게이트",
     "    allowed_channels = plan_tier.channels", "    allowed_channels = plan_catalog.HIDDEN_TIER.channels"),
    ("model_cap 결선", "모델 상한(t3014)",
     "    model_cap = plan_tier.model_cap", "    model_cap = plan_catalog.HIDDEN_TIER.model_cap"),
    ("cli_model 결선", "사용자 선택 모델이 실제 생성에 실림(t3014)",
     "    cli_model = resolved_model", '    cli_model = "haiku"'),
]

SUITE = ["tests/"]


def run_full() -> tuple[int, str]:
    proc = subprocess.run(
        [sys.executable, "-m", "pytest", "-q", "-p", "no:randomly", *SUITE],
        cwd=SERVER, capture_output=True, text=True,
    )
    lines = (proc.stdout or "").strip().splitlines()
    summary = next((l for l in reversed(lines) if "passed" in l or "failed" in l or "error" in l), "(none)")
    return proc.returncode, summary


def main() -> int:
    print("대조군 (무변이) 전체 스위트 …", flush=True)
    rc, s = run_full()
    print(f"  rc={rc}  {s}\n", flush=True)
    if rc != 0:
        print("✖ 대조군이 빨갛다 — 중단"); return 1
    base_summary = s

    backup = MAIN.with_suffix(".py.auditbak")
    shutil.copy2(MAIN, backup)
    rows = []
    try:
        for cid, dies, old, new in CANDIDATES:
            src = MAIN.read_text(encoding="utf-8")
            if old not in src:
                rows.append({"id": cid, "dies": dies, "verdict": "NO-OP(원문부재)",
                             "summary": "", "failed": 0})
                print(f"[{cid}] ✖ 원문 부재 — 이 변이는 무효", flush=True)
                continue
            mutated = src.replace(old, new, 1)
            assert mutated != src, f"{cid}: 치환 후 동일 — no-op"
            MAIN.write_text(mutated, encoding="utf-8")
            t0 = time.time()
            try:
                rc_m, s_m = run_full()
            finally:
                shutil.copy2(backup, MAIN)
            m = re.search(r"(\d+) failed", s_m)
            failed = int(m.group(1)) if m else 0
            verdict = "KILL" if rc_m != 0 else "SURVIVED"
            rows.append({"id": cid, "dies": dies, "verdict": verdict,
                         "summary": s_m, "failed": failed})
            print(f"[{cid:34s}] {verdict:9s} failed={failed:<4d} ({time.time()-t0:.0f}s)  {s_m}", flush=True)
    finally:
        shutil.copy2(backup, MAIN)
        backup.unlink(missing_ok=True)

    out = SERVER / "wiring_audit_3078_result.json"
    out.write_text(json.dumps({"base": base_summary, "rows": rows},
                              ensure_ascii=False, indent=2), encoding="utf-8")
    print(f"\n결과 저장: {out}")
    surv = [r for r in rows if r["verdict"] == "SURVIVED"]
    print(f"무방비(SURVIVED) {len(surv)}/{len(rows)}: {[r['id'] for r in surv]}")
    rc2, s2 = run_full()
    print(f"복원 확인: rc={rc2} {s2}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
