"""scene_schema.json — item_id required, scene_index/var_index optional (T5/AC-A6)."""
import json
from pathlib import Path

import app.modules.prompt_loader as prompt_loader


def _latest_schema_path() -> Path:
    base = prompt_loader.PROMPTS_BASE / "t2i_review"
    versions = sorted(
        [d for d in base.iterdir() if d.is_dir()],
        key=lambda d: prompt_loader._version_sort_key(d.name),
        reverse=True,
    )
    assert versions, f"no version dir under {base}"
    return versions[0] / "scene_schema.json"


def test_scene_schema_latest_has_item_id_required():
    p = _latest_schema_path()
    data = json.loads(p.read_text())
    result_props = data["properties"]["results"]["items"]
    assert "item_id" in result_props["properties"], (
        f"item_id property 누락: {p}"
    )
    assert "item_id" in result_props["required"], (
        f"item_id required 누락: {p}"
    )


def test_scene_schema_latest_has_scene_var_index_nullable_required():
    """OpenAI strict structured output: 모든 properties keys 가 required 에 포함되어야
    한다. 도메인 의도상 scene_index / var_index 는 item_id 보조 (LLM 이 누락 가능) 라
    nullable type 으로 표현한다. 즉 required 에는 포함하되 type 은 ["integer", "null"].

    runtime 사고 (2026-05-08): scene_index 가 properties 에는 있지만 required 에 없어
    OpenAI strict mode 가 "Missing 'scene_index'" 로 reject — 모든 scene batch fail.
    fix: scene_index/var_index 모두 required + nullable type.
    """
    p = _latest_schema_path()
    data = json.loads(p.read_text())
    result_props = data["properties"]["results"]["items"]
    required = result_props["required"]

    # 두 키 모두 required 에 포함 (OpenAI strict 호환)
    assert "scene_index" in required, (
        f"scene_index 는 OpenAI strict mode 호환을 위해 required 에 포함되어야 함: {p}"
    )
    assert "var_index" in required, (
        f"var_index 는 OpenAI strict mode 호환을 위해 required 에 포함되어야 함: {p}"
    )

    # 도메인 의도 (optional) 보존: type 이 nullable
    properties = result_props["properties"]
    for key in ("scene_index", "var_index"):
        type_def = properties[key].get("type")
        assert isinstance(type_def, list) and "null" in type_def, (
            f"{key} 는 nullable type (e.g. [\"integer\", \"null\"]) 이어야 함 "
            f"(item_id 가 권위, LLM 누락 시 null 허용): {p} got type={type_def!r}"
        )


def test_scene_system_prompt_has_item_id_protocol():
    """scene_system.md 에 ITEM_ID PROTOCOL instruction 포함."""
    base = prompt_loader.PROMPTS_BASE / "t2i_review"
    versions = sorted(
        [d for d in base.iterdir() if d.is_dir()],
        key=lambda d: prompt_loader._version_sort_key(d.name),
        reverse=True,
    )
    p = versions[0] / "scene_system.md"
    text = p.read_text()
    assert "ITEM_ID PROTOCOL" in text, (
        f"ITEM_ID PROTOCOL 섹션 누락: {p}"
    )
    assert "item_id" in text, f"item_id mention 누락: {p}"


def test_scene_schema_latest_has_5_core_fields():
    """Area #6 v1: issue 안 5 core fields ({id, scope, issue_code, action, hint_diagnostic}) required strict."""
    p = _latest_schema_path()
    data = json.loads(p.read_text())
    issue_props = data["properties"]["results"]["items"]["properties"]["issues"]["items"]
    required = set(issue_props["required"])
    expected_core = {"id", "scope", "issue_code", "action", "hint_diagnostic"}
    assert expected_core.issubset(required), (
        f"5 core fields not all required: missing={expected_core - required}, schema={p}"
    )
    # target/suggestion legacy field 폐기 검증
    properties = set(issue_props["properties"].keys())
    assert "target" not in properties, f"legacy target field 잔존: {p}"
    assert "suggestion" not in properties, f"legacy suggestion field 잔존: {p}"


def test_scene_schema_latest_scope_const_scene():
    p = _latest_schema_path()
    data = json.loads(p.read_text())
    issue_props = data["properties"]["results"]["items"]["properties"]["issues"]["items"]
    assert issue_props["properties"]["scope"]["enum"] == ["scene"], (
        f"scene_schema scope const='scene' 위반: {p}"
    )


def test_scene_schema_latest_action_const_regen_required():
    p = _latest_schema_path()
    data = json.loads(p.read_text())
    issue_props = data["properties"]["results"]["items"]["properties"]["issues"]["items"]
    assert issue_props["properties"]["action"]["enum"] == ["regen_required"], (
        f"action const='regen_required' 위반: {p}"
    )


def test_entity_schema_latest_has_5_core_fields():
    """Area #6 v1: entity_schema 도 issue 안 5 core fields required strict."""
    base = prompt_loader.PROMPTS_BASE / "t2i_review"
    versions = sorted(
        [d for d in base.iterdir() if d.is_dir()],
        key=lambda d: prompt_loader._version_sort_key(d.name),
        reverse=True,
    )
    p = versions[0] / "entity_schema.json"
    data = json.loads(p.read_text())
    issue_props = data["properties"]["results"]["items"]["properties"]["issues"]["items"]
    required = set(issue_props["required"])
    expected_core = {"id", "scope", "issue_code", "action", "hint_diagnostic"}
    assert expected_core.issubset(required), (
        f"entity 5 core fields not all required: missing={expected_core - required}, schema={p}"
    )
    assert issue_props["properties"]["scope"]["enum"] == ["entity"]
    properties = set(issue_props["properties"].keys())
    assert "target" not in properties, f"legacy target field 잔존: {p}"
    assert "suggestion" not in properties, f"legacy suggestion field 잔존: {p}"
