"""G4.6 Phase 4 fix iter 6 — **v21 archival** Rule X-2 mechanical block semantic
alignment test (Codex iter 6 I2).

**ARCHIVAL NOTICE (Area #1 W5, 2026-05-16)**: v21 prompt 는 historical archival.
active prompt 는 v25 (Area #1 W5 후 — id_policy.subject_reference_policy SOT).
본 test 는 v21 file 의 in-place wording 보존 검증 (regression guard) — v25
active prompt 와 별개 archival target. Area #1 E2 active prompt residue gate
는 v25 + v19 대상이며 v21 archival 은 gate 대상 외.

기존 alignment 테스트 (timestamp / line count / section presence) 와 별도로,
Rule X-2 mechanical block 의 의미 항목들이 v21 prompt 안에 실제로 명시되어
있는지 직접 grep — iter 5 보강 후 wording 이 retroactively 약화되는 것을
방지하기 위한 회귀 가드.

검사 항목 (v21 시점 binding spec):
1. "every t2i_variation" / "매 variation" — 매 variation 마다 ID 등장 강제
2. generic noun 대체 금지 — "figure" / "subject" / "person" 명시
3. multi-character distinct ID — "서로 다른 ID" 강조
4. partial 통과 없음 — descriptor only 금지 명시
5. 예외 우선순위 (B1) — partial_focus / body_part_focus_rule /
   reproduction_surface_rule 명시 (v21 시점 wording; v25 active 는
   subject_reference_policy SOT 로 대체)

User binding refs:
- feedback_no_scenario_keywords.md (placeholder only — distinct ID 예시는
  C##/<C_ID_A>/<C_ID_B> 등 시나리오 token 0)
- session_20260506_g4_6_phase4.md (Rule X-2 spec, v21 시점)
"""
from __future__ import annotations

from pathlib import Path

import pytest


_REPO_ROOT = Path(__file__).resolve().parents[3]
_SCENE_DETAIL_BASE = _REPO_ROOT / "prompts" / "_base" / "scene_detail"


def _v21_system_md() -> str:
    candidates = sorted(
        (p for p in _SCENE_DETAIL_BASE.glob("21.*/system.md") if p.is_file()),
        key=lambda p: p.parent.name,
    )
    if not candidates:
        pytest.skip("no v21 system.md found")
    return candidates[-1].read_text(encoding="utf-8")


def test_rule_x2_mechanical_block_present():
    """Rule X-2 mechanical block heading 등장."""
    content = _v21_system_md()
    assert "기계적 규칙" in content, (
        "Rule X-2 mechanical block heading missing — iter 5 보강이 빠졌거나 "
        "선언 위치가 변경됨"
    )


def test_rule_x2_every_variation_clause():
    """매 variation 별 enforcement — iter 5 spec (1)."""
    content = _v21_system_md()
    assert "매 variation" in content or "every variation" in content.lower(), (
        "Rule X-2 mechanical (1) — '매 variation' 강제 wording missing"
    )


def test_rule_x2_generic_noun_substitution_forbidden():
    """generic noun 대체 금지 — iter 5 spec (2)."""
    content = _v21_system_md()
    # 3 examples of generic nouns must be enumerated in the rule
    for noun in ("figure", "subject", "person"):
        assert noun in content, (
            f"Rule X-2 mechanical (2) — generic noun example {noun!r} missing"
        )
    assert "대체하지 못한다" in content or "cannot substitute" in content.lower(), (
        "Rule X-2 mechanical (2) — '대체하지 못한다' wording missing"
    )


def test_rule_x2_multi_character_distinct_id():
    """multi-character shot 의 서로 다른 ID 강조 — iter 6 I1."""
    content = _v21_system_md()
    assert "서로 다른" in content, (
        "Rule X-2 mechanical (3) — multi-character '서로 다른 ID' wording missing"
    )
    # 잘못된 예 (같은 ID 반복) 명시
    assert "같은" in content and "반복" in content, (
        "Rule X-2 mechanical (3) — '같은 ID 반복' bad-example warning missing"
    )


def test_rule_x2_descriptor_only_forbidden():
    """descriptor only 금지 명시 — iter 5 spec (4)."""
    content = _v21_system_md()
    assert "descriptor only" in content.lower(), (
        "Rule X-2 mechanical (4) — 'descriptor only' wording missing"
    )
    assert "금지" in content, (
        "Rule X-2 mechanical (4) — '금지' wording missing"
    )


def test_rule_x2_exemption_partial_focus():
    """예외 우선순위 (B1) — partial_focus mode 명시."""
    content = _v21_system_md()
    assert "partial_focus" in content, (
        "Rule X-2 exemption (B1) — partial_focus mode missing"
    )


def test_rule_x2_exemption_body_part_focus_rule():
    """예외 우선순위 (B1) — body_part_focus_rule.trigger_phrases 명시."""
    content = _v21_system_md()
    assert "body_part_focus_rule" in content, (
        "Rule X-2 exemption (B1) — body_part_focus_rule reference missing"
    )


def test_rule_x2_exemption_reproduction_surface_rule():
    """예외 우선순위 (B1) — reproduction_surface_rule 명시."""
    content = _v21_system_md()
    assert "reproduction_surface_rule" in content, (
        "Rule X-2 exemption (B1) — reproduction_surface_rule reference missing"
    )


def test_rule_x2_exemption_forward_only():
    """면제는 forward 만 적용 — Reverse + Source 2 항상 enforce."""
    content = _v21_system_md()
    assert "Source 1 forward" in content or "forward 한정" in content, (
        "Rule X-2 exemption (B1) — 'Source 1 forward 한정' wording missing"
    )


def test_rule_x2_examples_use_placeholders_only():
    """A-prime — Rule X-2 추가 예시는 placeholder (C##/<C_ID_A>/<C_ID_B>) 만,
    시나리오 derived token 0."""
    content = _v21_system_md()
    # ## Rule X-2 ~ next ## section 추출
    start = content.find("## Rule X-2")
    if start == -1:
        pytest.fail("Rule X-2 section heading not found")
    next_section = content.find("\n## ", start + 1)
    section = content[start:next_section] if next_section != -1 else content[start:]
    forbidden_in_examples = (
        # 시나리오 derived ethnicity / nationality (placeholder block 외)
        "한국인", "일본인", "중국인", "korean", "east asian",
        # 시나리오 derived role / appearance literal
        "여자", "남자", "붉은 눈동자",
    )
    hits = [t for t in forbidden_in_examples if t.lower() in section.lower()]
    assert not hits, (
        f"Rule X-2 section contains scenario-derived tokens in examples: {hits}. "
        f"Use generic placeholder (C##/<C_ID_A>/<C_ID_B>) only."
    )
