"""C4 — Background / floor-plan semantic classifier prompt hygiene regression tests.

fix-critical-1 Tier β #7. W2 (background_chain_planning v5) / W3 (background_classify v4) /
W4 (location_floor_plan v4) prompt-pack 의 닫힌 enumeration 추상화 + version sync 를 검증한다.

파일 read + 문자열 검사 + JSON structural diff only — no live LLM, NO VLM.
닫힌-form residue needle 은 split-string 상수로 구성 (test 파일 self-hit 방지).

Gate map (spec §7):
- W2 G4-G6 — background_chain_planning v5 (본 wave)
- W3 G7-G9 — background_classify v4 (W3 append)
- W4 G10-G12 — location_floor_plan v4 (W4 append)
"""
from __future__ import annotations

import copy
import json
from pathlib import Path

from app.modules.prompt_loader import _list_module_versions

_PROMPTS = Path(__file__).resolve().parents[3] / "prompts" / "_base"

# C4 W2/W3/W4 직전 active prompt-pack — historical 비교 기준 (frozen dir).
_BCP_V4_DIR = "4.202604291315"


def _latest_dir(module: str) -> Path:
    """module 의 numeric-latest prompt-pack 디렉토리."""
    return _PROMPTS / module / _list_module_versions(module)[0]


# ─────────────────────── W2 — background_chain_planning v5 ───────────────────────


def test_w2_g4_background_chain_planning_closed_enumeration_removed():
    """G4: v5 prompt-pack 에 A001/A002/A003/A004 닫힌-form enumeration marker 0."""
    d = _latest_dir("background_chain_planning")
    system = (d / "system.md").read_text(encoding="utf-8")
    schema = (d / "schema.json").read_text(encoding="utf-8")
    tmpl = (d / "user_template.md").read_text(encoding="utf-8")
    assert ("store interior, " + "office, kitchen") not in system, "A003 indoor 닫힌 예시 잔존"
    assert ("a road far from " + "any building") not in system, "A003 outdoor 닫힌 예시 잔존"
    assert ("clean / lived-in" + " / disturbed") not in system, "A004 room state 닫힌 목록 잔존"
    assert ("street, road, " + "coast, sea, forest") not in schema, "A001 skip_chain OUTDOOR 목록 잔존"
    assert ("(outdoor " + "/ open-air)") not in tmpl, "A002 outdoor 분기 label 잔존"
    assert ("(indoor / enclosed " + "/ fixed-set)") not in tmpl, "A002 indoor 분기 label 잔존"


def test_w2_g5_background_chain_planning_intent_and_schema_structure_preserved():
    """G5: SKIP DECISION 개념 보존 + schema 구조 diff = skip_chain.description 한 곳만."""
    d = _latest_dir("background_chain_planning")
    system = (d / "system.md").read_text(encoding="utf-8")
    assert "SHARED BUILDING SET" in system, "SKIP DECISION 개념 (SHARED BUILDING SET) 손실"
    assert "DETACHED OPEN AREA" in system, "SKIP DECISION 개념 (DETACHED OPEN AREA) 손실"
    assert "split into separate nodes" in system, "node-split principle 손실"
    # schema structural diff allowlist — properties.skip_chain.description 한 곳만 허용.
    v5 = json.loads((d / "schema.json").read_text(encoding="utf-8"))
    v4 = json.loads(
        (_PROMPTS / "background_chain_planning" / _BCP_V4_DIR / "schema.json")
        .read_text(encoding="utf-8")
    )
    v5_norm = copy.deepcopy(v5)
    v4_norm = copy.deepcopy(v4)
    del v5_norm["properties"]["skip_chain"]["description"]
    del v4_norm["properties"]["skip_chain"]["description"]
    assert v5_norm == v4_norm, "schema.json 구조 diff 가 skip_chain.description 외에 존재"
    assert (
        v5["properties"]["skip_chain"]["description"]
        != v4["properties"]["skip_chain"]["description"]
    ), "skip_chain.description 미변경 (A001 추상화 누락)"


def test_w2_g6_background_chain_planning_prompt_version_sync():
    """G6: PROMPT_VERSION 5 + SCHEMA_VERSION 2 불변 + loader latest-pick = v5."""
    from app.core.steps.background_chain_planning_step import (
        PROMPT_VERSION,
        SCHEMA_VERSION,
    )
    assert PROMPT_VERSION == "5", f"PROMPT_VERSION != 5: {PROMPT_VERSION}"
    assert SCHEMA_VERSION == 2, f"SCHEMA_VERSION bump 발생: {SCHEMA_VERSION}"
    latest = _list_module_versions("background_chain_planning")[0]
    assert latest.startswith("5."), f"loader latest-pick 가 v5 아님: {latest}"
    assert _latest_dir("background_chain_planning").is_dir(), "v5 디렉토리 부재"


# ─────────────────────── W3 — background_classify v4 ───────────────────────

_BCLASSIFY_V3_DIR = "3.202604300520"  # C4 W3 직전 active — historical sibling 비교 기준


def test_w3_g7_background_classify_korean_keyword_list_removed():
    """G7: v4 system.md 에 indoor/outdoor 판단용 닫힌 한국어 keyword 목록 0."""
    system = (_latest_dir("background_classify") / "system.md").read_text(encoding="utf-8")
    assert "Korean clues" not in system, "A005 'Korean clues' 문구 잔존"
    assert ("내부 / 안 " + "/ 방 / 실 / 층") not in system, "A005 indoor 한국어 keyword 목록 잔존"
    assert ("외부 / 옥상 " + "/ 거리 / 도로") not in system, "A005 outdoor 한국어 keyword 목록 잔존"


def test_w3_g8_background_classify_concept_preserved_and_schema_sibling():
    """G8: indoor/outdoor 개념 보존 + schema.json·user_template.md byte-identical sibling."""
    import hashlib
    d = _latest_dir("background_classify")
    system = (d / "system.md").read_text(encoding="utf-8")
    assert "enclosed structure with walls and a ceiling" in system, "indoor 개념 손실"
    # 'outside' 는 system.md 에서 markdown bold(**outside**) — 안정 substring 으로 검사.
    assert "with sky overhead" in system, "outdoor 개념 손실"
    v3 = _PROMPTS / "background_classify" / _BCLASSIFY_V3_DIR
    for stem in ("schema.json", "user_template.md"):
        new_h = hashlib.sha256((d / stem).read_bytes()).hexdigest()
        old_h = hashlib.sha256((v3 / stem).read_bytes()).hexdigest()
        assert new_h == old_h, f"{stem} sibling byte-identical 위반"


def test_w3_g9_background_classify_prompt_version_sync():
    """G9: PROMPT_VERSION 4 + SCHEMA_VERSION 1 불변 + loader latest-pick = v4."""
    from app.core.steps.background_classify_step import PROMPT_VERSION, SCHEMA_VERSION
    assert PROMPT_VERSION == "4", f"PROMPT_VERSION != 4: {PROMPT_VERSION}"
    assert SCHEMA_VERSION == 1, f"SCHEMA_VERSION bump 발생: {SCHEMA_VERSION}"
    latest = _list_module_versions("background_classify")[0]
    assert latest.startswith("4."), f"loader latest-pick 가 v4 아님: {latest}"
    assert _latest_dir("background_classify").is_dir(), "v4 디렉토리 부재"


# ─────────────────────── W4 — location_floor_plan v4 ───────────────────────

_LFP_V3_DIR = "3.202604291130"  # C4 W4 직전 active — historical sibling 비교 기준


def test_w4_g10_location_floor_plan_korean_token_examples_removed():
    """G10: v4 system.md 에 A033 한국어 방 이름/이동 동사 cue + A034 slash-zone 한국어 예시 0."""
    system = (_latest_dir("location_floor_plan") / "system.md").read_text(encoding="utf-8")
    assert ("거실/안방, " + "부엌/거실") not in system, "A033 한국어 방 이름 cue 잔존"
    assert ("문을 열고 " + "들어간다") not in system, "A033 한국어 이동 동사 잔존"
    assert ("옥탑방 안 " + "/ 실내") not in system, "A034 slash-zone 예시 잔존"
    assert ("한옥 / 안방 " + "/ 마루") not in system, "A034 slash-zone 예시 잔존"


def test_w4_g11_location_floor_plan_principle_preserved_and_sibling():
    """G11: room-count/zone generic principle 보존 + :14 Korean-label-OK 규칙 보존 + user_template sibling."""
    import hashlib
    d = _latest_dir("location_floor_plan")
    system = (d / "system.md").read_text(encoding="utf-8")
    assert "Multiple distinct named spaces" in system, "A033 room-count generic principle 손실"
    assert "slash-separated zone labels" in system, "A034 zone-marker generic principle 손실"
    assert "main bedroom (안방)" in system, "boundary — Korean-label-OK 규칙(:14) 손실"
    new_h = hashlib.sha256((d / "user_template.md").read_bytes()).hexdigest()
    old_h = hashlib.sha256(
        (_PROMPTS / "location_floor_plan" / _LFP_V3_DIR / "user_template.md").read_bytes()
    ).hexdigest()
    assert new_h == old_h, "user_template.md sibling byte-identical 위반"


def test_w4_g12_location_floor_plan_prompt_version_sync():
    """G12: PROMPT_VERSION 4 + SCHEMA_VERSION 3 불변 + loader latest-pick = v4."""
    from app.core.steps.location_floor_plan_step import PROMPT_VERSION, SCHEMA_VERSION
    assert PROMPT_VERSION == "4", f"PROMPT_VERSION != 4: {PROMPT_VERSION}"
    assert SCHEMA_VERSION == 3, f"SCHEMA_VERSION bump 발생: {SCHEMA_VERSION}"
    latest = _list_module_versions("location_floor_plan")[0]
    assert latest.startswith("4."), f"loader latest-pick 가 v4 아님: {latest}"
    assert _latest_dir("location_floor_plan").is_dir(), "v4 디렉토리 부재"
