"""Area #7a canary — VWR SOT enforcement + scene_director A047 absorption + step_manifest schema_version invalidation.

5 canary (spec §5.4, no live LLM):
- C1: jsonschema.validate() invalid rule_type reject (deterministic).
- C2: VWR system.md ↔ rules_schema.json 10-token 정합 (structural).
- C3: scene_director v9 principle-only — VWR SOT reference token 1+ + A047 3 site signatures 0.
- C4: STEP_MANIFEST visual_world_rules.schema_version = 1 신설 (current None → 1, cp invalidation).
- C5: scene_director step load_prompt path 정합 (9.* dir 가 prompt_loader latest version 으로 자동 활성).

NO VLM 의무 — 모든 verification = schema/text/manifest only.
"""

from __future__ import annotations

import json
import re
from pathlib import Path

import jsonschema
import pytest

REPO_ROOT = Path(__file__).resolve().parents[3]  # backend/tests/integration/<this> → repo root (Codex iter 1 F2 흡수: parents[2] 는 backend/ 까지만 도달)
VWR_DIR = REPO_ROOT / "prompts" / "_base" / "visual_world_rules"
SCENE_DIRECTOR_DIR = REPO_ROOT / "prompts" / "_base" / "scene_director"

CANONICAL_RULE_TYPE_ENUM = [
    "possession",
    "transformation",
    "ghost",
    "projection",
    "superpower",
    "body_deformation",
    "time_period",
    "costume",
    "technology",
    "other",
]


def _latest_active_dir(base: Path, version_prefix: str) -> Path:
    candidates = sorted(
        p for p in base.iterdir() if p.is_dir() and p.name.startswith(version_prefix)
    )
    assert candidates, f"no {version_prefix}* dir under {base}"
    return candidates[-1]


@pytest.fixture(scope="module")
def vwr_v7_schema() -> dict:
    schema_path = _latest_active_dir(VWR_DIR, "7.") / "rules_schema.json"
    return json.loads(schema_path.read_text(encoding="utf-8"))


@pytest.fixture(scope="module")
def vwr_v7_system_md() -> str:
    system_md_path = _latest_active_dir(VWR_DIR, "7.") / "system.md"
    return system_md_path.read_text(encoding="utf-8")


@pytest.fixture(scope="module")
def scene_director_v9_system_md() -> str:
    system_md_path = _latest_active_dir(SCENE_DIRECTOR_DIR, "9.") / "system.md"
    return system_md_path.read_text(encoding="utf-8")


# --- C1: jsonschema.validate() invalid rule_type reject (deterministic) ---


def test_c1_invalid_rule_type_rejected_by_jsonschema(vwr_v7_schema: dict) -> None:
    """Codex iter 3 F-4 흡수: live LLM / router completion call 금지.
    `jsonschema.validate()` 직접 호출 (production `_validate_local_schema` parity) + synthetic invalid payload.
    """
    invalid_payload = {
        "rules": [
            {
                "rule_type": "invalid_value_not_in_enum",  # ← schema enum 위반
                "description": "test",
                "visual_guideline": "test",
            }
        ],
        "director_notes": [],
        "era": "modern",
        "region": "test",
        "source_language": "ko",
        "t2i_context": "test context",
    }
    with pytest.raises(jsonschema.ValidationError):
        jsonschema.validate(invalid_payload, vwr_v7_schema)


def test_c1_valid_rule_type_accepted(vwr_v7_schema: dict) -> None:
    """canonical 10 enum 모두 valid 확인."""
    for rule_type in CANONICAL_RULE_TYPE_ENUM:
        valid_payload = {
            "rules": [
                {
                    "rule_type": rule_type,
                    "description": "test",
                    "visual_guideline": "test",
                }
            ],
            "director_notes": [],
            "era": "modern",
            "region": "test",
            "source_language": "ko",
            "t2i_context": "test context",
        }
        jsonschema.validate(valid_payload, vwr_v7_schema)  # no raise


# --- C2: VWR system.md ↔ rules_schema.json 10-token 정합 (structural) ---


def test_c2_system_md_schema_rule_type_alignment(
    vwr_v7_schema: dict, vwr_v7_system_md: str
) -> None:
    """system.md (line 51 canonical list) ↔ rules_schema.json enum 10-token 정합."""
    schema_enum = vwr_v7_schema["properties"]["rules"]["items"]["properties"]["rule_type"]["enum"]
    assert schema_enum == CANONICAL_RULE_TYPE_ENUM
    for token in CANONICAL_RULE_TYPE_ENUM:
        assert token in vwr_v7_system_md, f"missing canonical token {token} in system.md"


# --- C3: scene_director v9 principle-only + A047 signatures 0 ---


def test_c3_scene_director_v9_principle_only(scene_director_v9_system_md: str) -> None:
    body = scene_director_v9_system_md
    assert re.search(r"visual_world_rules|VWR|rule_type", body), "scene_director v9 must reference VWR SOT"
    # A047 Site 1 / 2 / 3 signature 0 — residue gate 정합 sanity
    forbidden_signatures = [
        r"영상통화,\s*CCTV,\s*방송 화면",
        r"빙의/원격접속 중이면 실제 몸이 안 보이므로",
        r"\(빙의/원격/V\.O\.\s*등\)",
    ]
    for pat in forbidden_signatures:
        assert not re.search(pat, body), f"A047 signature residue: {pat}"


# --- C4: STEP_MANIFEST visual_world_rules.schema_version = 1 신설 + cp invalidation flow ---


def test_c4_step_manifest_schema_version_set() -> None:
    from app.core.step_manifest import STEP_MANIFEST

    vwr_entry = STEP_MANIFEST["visual_world_rules"]
    assert vwr_entry.get("schema_version") == 1, vwr_entry


def test_c4_cp_invalidation_flow_deterministic() -> None:
    """Codex iter 1 F3 흡수: spec §5.4 C4 의 "cp invalidation flow (incompatible
    stamp cp 차단 verify)" — deterministic StepRunner._check_cp_mismatch 호출.
    no live LLM / router completion call.
    """
    from app.core.step_manifest import STEP_MANIFEST
    from app.core.step_runner import StepRunner

    vwr_manifest = STEP_MANIFEST["visual_world_rules"]
    assert vwr_manifest["schema_version"] == 1

    class _ManifestStub:
        """Duck-type stub — _check_cp_mismatch 본문 line 1511-1516 schema_version
        분기 만 trigger. cp_hash 분기는 cp.config_hash 미지정 + manifest entry
        가 cp_hash 도 None 으로 skip.
        """
        manifest = vwr_manifest
        step_id = "visual_world_rules"
        project_config: dict = {}

    stub = _ManifestStub()

    # Incompatible stamp cp (cp_schema=2 vs current=1) → BLOCK (non-None mismatch reason).
    incompatible_result = StepRunner._check_cp_mismatch(stub, {"schema_version": 2})
    assert incompatible_result is not None, "incompatible-stamp cp must BLOCK"
    assert "schema_version mismatch" in incompatible_result, incompatible_result

    # Older unstamped cp (cp_schema absent) → None (legacy 호환 보장, force rerun 운영자 결정).
    legacy_result = StepRunner._check_cp_mismatch(stub, {})
    assert legacy_result is None, legacy_result

    # Matching cp (cp_schema=1) → None (no schema_version mismatch; cp_hash 분기는 absent 로 skip).
    match_result = StepRunner._check_cp_mismatch(stub, {"schema_version": 1})
    assert match_result is None, match_result


# --- C5: scene_director step load_prompt path 정합 (9.* auto-active) ---


def test_c5_scene_director_prompt_loader_latest_v9() -> None:
    """prompt_loader latest version dir 자동 결정 — 9.* dir 가 latest 로 활성."""
    from app.modules.prompt_loader import load_prompt

    system = load_prompt("scene_director", "system")
    # v9 의 principle-only rewrite 안에 들어가는 SOT reference token 1+
    assert re.search(r"visual_world_rules|VWR|rule_type", system), (
        "scene_director latest prompt must reference VWR SOT (v9 expected)"
    )


def test_c5_vwr_prompt_loader_latest_v7() -> None:
    """prompt_loader latest version dir 자동 결정 — 7.* dir 가 latest 로 활성."""
    from app.modules.prompt_loader import load_prompt, load_schema

    system = load_prompt("visual_world_rules", "system")
    # v7 의 canonical 10-token list (line 51) 본문 안 substring 1+
    assert "possession" in system and "transformation" in system

    schema = load_schema("visual_world_rules", "rules_schema")
    rule_type_def = schema["properties"]["rules"]["items"]["properties"]["rule_type"]
    assert rule_type_def["enum"] == CANONICAL_RULE_TYPE_ENUM
