"""W20F follow-up — step_manifest schema_version ↔ step module SCHEMA_VERSION 동기 가드.

배경:
    W20F wave (F5/F6/F7-A/F9) 가 각 step module 의 ``SCHEMA_VERSION`` 상수를
    올렸지만 ``step_manifest.py`` 의 같은 entry ``schema_version`` 동기를
    누락해 ``step_runner._check_cp_mismatch`` 가 cp_schema ≠ manifest_schema
    BLOCK 으로 dispatch 를 fatal 처리 (운영 force 강요) 했다.

본 정적 회귀 가드는 ``SCHEMA_VERSION`` 상수와 manifest entry 의
``schema_version`` 이 일치하는지 module 단위 숫자 비교만 한다 (broad
fixture 회피). 검증 대상은 W20F wave 가 bump 한 4 step 으로 한정 — 다른
step 도 동일 의무를 갖지만 이번 wave 의 사고 표면이다.

각 step pair 는 별도 test 로 분리해서 어느 쪽이 깨졌는지 진단 메시지가
명확하게 노출되도록 한다.
"""
from __future__ import annotations


def _assert_manifest_matches(step_id: str, module_path: str) -> None:
    """주어진 step entry 의 manifest schema_version 이 module SCHEMA_VERSION 과 같은지."""
    import importlib

    from app.core.step_manifest import STEP_MANIFEST

    module = importlib.import_module(module_path)
    module_version = getattr(module, "SCHEMA_VERSION")
    entry = STEP_MANIFEST[step_id]
    assert entry["schema_version"] == module_version, (
        f"step_manifest[{step_id!r}].schema_version ({entry['schema_version']}) "
        f"와 {module_path}.SCHEMA_VERSION ({module_version}) 가 어긋났다. "
        "W20F 회귀 — 둘은 같은 줄을 함께 갱신해야 한다."
    )


def test_background_master_plan_schema_version_matches_manifest():
    _assert_manifest_matches(
        "background_master_plan",
        "app.core.steps.background_master_plan_step",
    )


def test_base_location_dossier_schema_version_matches_manifest():
    _assert_manifest_matches(
        "base_location_dossier",
        "app.core.steps.base_location_dossier_step",
    )


def test_floor_plan_geometry_readback_schema_version_matches_manifest():
    _assert_manifest_matches(
        "floor_plan_geometry_readback",
        "app.core.steps.floor_plan_geometry_readback_step",
    )


def test_shot_aware_bg_render_plan_schema_version_matches_manifest():
    _assert_manifest_matches(
        "shot_aware_bg_render_plan",
        "app.core.steps.shot_aware_bg_render_plan_step",
    )


def test_bg_space_partition_schema_version_matches_manifest():
    # W21B-w5 (2026-05-31): new bg_space_partition step, SCHEMA 1.
    _assert_manifest_matches(
        "bg_space_partition",
        "app.core.steps.bg_space_partition_step",
    )


def test_floor_plan_light_sidecar_schema_version_matches_manifest():
    # W21B-w5 STEP5-B (2026-05-31): new floor_plan_light_sidecar step, SCHEMA 1.
    _assert_manifest_matches(
        "floor_plan_light_sidecar",
        "app.core.steps.floor_plan_light_sidecar_step",
    )


def test_outdoor_structure_form_reference_schema_version_matches_manifest():
    # 2026-07-30: 대상 = seed exact parity(판정 제거) + 이중 판정 선택으로
    # CP shape 이 두 번 바뀌어 SCHEMA 1→3. manifest 핀을 함께 올리지 않으면
    # 신규 CP 를 resume 이 구 스키마로 읽는다 (Codex 리뷰 #1).
    _assert_manifest_matches(
        "outdoor_structure_form_reference",
        "app.core.steps.outdoor_structure_form_reference_step",
    )
