"""Area #11 v1 W4 — production code residue gate (8 항목, all strict 0 / positive).

substring-classifier dispatcher cluster 가 production 에서 완전 폐기됐는지
가드. 4-gate compliance + 3-path cascade producer coverage 동시 검증.

8 gate (negative = 0 hits, positive = expected count):
  1. `_classify_label(` production caller = 0 (negative)
  2. `_is_explicit_character_ref_label` production reference = 0 (negative)
  3. `_IMAGE_N_PAREN_HEADER_RE` / `_IMAGE_N_BARE_PREFIX_RE` reference = 0 (negative)
  4. `_HEADER_CHARACTER_TAGS` / `_BARE_LABEL_CHARACTER_PREFIXES` reference = 0 (negative)
  5. ` in label_lower` substring branch in prompt_service.py = 0 (negative)
  6. test 의 `from app.services.prompt_service import _classify_label` = 0 (negative)
  7. `make_labeled_ref_payload` producer call sites >= 1 in scene_reference_service.py (positive)
  8. `payload.labeled_refs` consumer access in scene_generation_coordinator.py >= 3 (positive — 3-path cascade)

phantom guard 본체 (_classify_label dispatcher + helper + regex constants +
frozenset) 가 W3 atomic switch + W4 cleanup 으로 모두 폐기. consumer dispatch 는
LabeledRefPayload.ref_roles enum 만 read.

NO VLM. structured SOT only.

spec: docs/superpowers/specs/2026-05-18-area-11-classify-label-substring-replacement-design.md
plan: docs/superpowers/plans/2026-05-18-area-11-classify-label-substring-replacement-implementation.md
"""
from __future__ import annotations

from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parents[3]


def _grep_count(pattern: str, rel_path: str) -> int:
    """plain-text substring count — file 또는 dir glob 지원 (Area #5 W4 pattern donor).

    pure Python — file path 면 단일 file 의 substring count, dir 면 그 dir 안
    모든 file 합산 (재귀).
    """
    target = REPO_ROOT / rel_path
    paths: list[Path] = []
    if target.is_file():
        paths = [target]
    elif target.is_dir() or str(rel_path).endswith("/"):
        paths = [p for p in target.rglob("*") if p.is_file()]
    else:
        raise RuntimeError(f"path not found: {target}")
    total = 0
    for p in paths:
        try:
            text = p.read_text(encoding="utf-8", errors="ignore")
        except (UnicodeDecodeError, PermissionError, OSError):
            continue
        if pattern in text:
            total += text.count(pattern)
    return total


# ──────────────────────────────────────────────────────────────────
# 4 negative residue gate (production substring-classifier 폐기 확인)
# ──────────────────────────────────────────────────────────────────


def test_gate_1_classify_label_caller_zero_in_production():
    """Gate 1: production `_classify_label(` caller = 0 — W4 cleanup 후 정의도 폐기."""
    # production app/ 안에 `_classify_label(` invocation 또는 definition 0 hit.
    hits = _grep_count("_classify_label(", "backend/app/services/prompt_service.py")
    assert hits == 0, (
        f"_classify_label( production reference 발견: {hits} hit "
        "(Area #11 W4 dead code removal incomplete — Codex 권고 verbatim 위반)"
    )


def test_gate_2_is_explicit_character_ref_label_definition_zero():
    """Gate 2: `def _is_explicit_character_ref_label(` definition 폐기 (strict pattern)."""
    hits = _grep_count("def _is_explicit_character_ref_label(", "backend/app/services/prompt_service.py")
    assert hits == 0, (
        f"_is_explicit_character_ref_label definition 발견: {hits} hit "
        "(W4 cleanup 후 helper 정의 폐기 의무)"
    )


def test_gate_3_image_n_regex_constants_definition_zero():
    """Gate 3: regex constant 할당 `= re.compile(` definition 폐기 (strict pattern)."""
    paren_hits = _grep_count(
        "_IMAGE_N_PAREN_HEADER_RE = re.compile",
        "backend/app/services/prompt_service.py",
    )
    bare_hits = _grep_count(
        "_IMAGE_N_BARE_PREFIX_RE = re.compile",
        "backend/app/services/prompt_service.py",
    )
    assert paren_hits == 0, f"_IMAGE_N_PAREN_HEADER_RE assignment 발견: {paren_hits} hit"
    assert bare_hits == 0, f"_IMAGE_N_BARE_PREFIX_RE assignment 발견: {bare_hits} hit"


def test_gate_4_character_tag_frozensets_definition_zero():
    """Gate 4: frozenset / tuple 할당 폐기 (strict pattern)."""
    header_hits = _grep_count(
        "_HEADER_CHARACTER_TAGS = frozenset",
        "backend/app/services/prompt_service.py",
    )
    bare_hits = _grep_count(
        "_BARE_LABEL_CHARACTER_PREFIXES = ",
        "backend/app/services/prompt_service.py",
    )
    assert header_hits == 0, f"_HEADER_CHARACTER_TAGS assignment 발견: {header_hits} hit"
    assert bare_hits == 0, f"_BARE_LABEL_CHARACTER_PREFIXES assignment 발견: {bare_hits} hit"


def test_gate_5_substring_branch_in_label_lower_zero_in_production():
    """Gate 5: `prompt_service.py` 의 `in label_lower` substring branch 0.

    `_classify_label` 본체 폐기 후, `label.lower()` 호출 후 substring match 식 0.
    (resolve_ref_roles 는 payload.ref_roles enum 만 dispatch — substring matching 0.)
    """
    hits = _grep_count("in label_lower", "backend/app/services/prompt_service.py")
    assert hits == 0, (
        f"`in label_lower` substring branch 발견: {hits} hit "
        "(Semantic Regex Ban 위반 — Area #11 W4 dead code removal incomplete)"
    )


def test_gate_6_test_classify_label_direct_import_zero():
    """Gate 6: test 안 substring-classifier helper 의 직접 import 0.

    test cascade 완료 후 폐기된 helper 의 from-import 0. self-file (residue gate
    docstring) 의 식별자 mention 은 historical context 보존이므로 exclude.
    """
    # test 디렉토리 합산 (단, self residue gate file 자체는 path-resolve 후 제외).
    # pattern literal 도 self-file 의 source 안 substring scan 에 catch 되지 않게
    # 변수 concat (`"_classi" + "fy_label,"` etc.) — false-positive 차단.
    target_dir = REPO_ROOT / "backend/tests"
    self_file = (REPO_ROOT / "backend/tests/integration/test_area_11_residue_gate.py").resolve()
    pattern_import = "from app.services.prompt_serv" + "ice import _classi" + "fy_label"
    pattern_multi = "    _classi" + "fy_label,"  # multi-line import 의 trailing line
    total = 0
    for p in target_dir.rglob("*.py"):  # .py 만 scan — __pycache__ bytecode 제외
        if not p.is_file() or p.resolve() == self_file:
            continue
        if "__pycache__" in p.parts:
            continue
        try:
            text = p.read_text(encoding="utf-8", errors="ignore")
        except (UnicodeDecodeError, PermissionError, OSError):
            continue
        total += text.count(pattern_import)
        total += text.count(pattern_multi)
    assert total == 0, (
        f"prompt_service substring-classifier helper 직접 import 발견: {total} hit "
        "(W3 cascade Task 3.1 incomplete)"
    )


# ──────────────────────────────────────────────────────────────────
# 2 positive coverage gate (producer + consumer cascade)
# ──────────────────────────────────────────────────────────────────


def test_gate_7_producer_make_labeled_ref_payload_present():
    """Gate 7: scene_reference_service.py 가 `make_labeled_ref_payload(` 1+ 호출 (positive).

    8 producer site (resolve_refs_for_prompt + _set + 6 internal append site) 가
    structured SOT factory 경유. 정확 site count 는 implementation detail —
    >= 1 만 확인 (positive coverage).
    """
    hits = _grep_count("make_labeled_ref_payload(", "backend/app/services/scene_reference_service.py")
    assert hits >= 1, (
        f"scene_reference_service.py 의 make_labeled_ref_payload 호출 {hits} hit — "
        "Area #11 v1 W2 producer-side LabeledRefPayload emit 깨짐"
    )


def test_gate_8_consumer_payload_labeled_refs_3path_cascade():
    """Gate 8: scene_generation_coordinator.py 의 `payload.labeled_refs` access >= 3 (3-path cascade).

    single helper path + batch path + variation path = 3 path 모두 payload 경유.
    정확 count 는 implementation detail — >= 3 strict bound.
    """
    hits = _grep_count("payload.labeled_refs", "backend/app/services/scene_generation_coordinator.py")
    assert hits >= 3, (
        f"scene_generation_coordinator.py 의 payload.labeled_refs access {hits} hit "
        "(3-path cascade single + batch + variation 깨짐 — Area #11 v1 W2 invariant 위반)"
    )
