"""G3.2 background_prompt v5 schema + normalize tests."""
from __future__ import annotations

import pytest

from app.modules.pipeline.background_prompt import (
    BackgroundPromptError,
    validate_bg_prompt_output,
)


def _base_output(**overrides):
    o = {
        "bg_id": "bg1",
        "t2i_prompt": "x" * 60,
        "ref_guide": "guide",
        "shot_guides": [{"shot_id": "S1_Shot1", "guide_text": "g"}],
        "objects_owned_by_background": ["door", "window"],
    }
    o.update(overrides)
    return o


class TestValidateBgPromptOwnedField:
    def test_valid_passes(self) -> None:
        out = _base_output()
        validate_bg_prompt_output(out, "bg1", {"S1_Shot1"})
        # 성공 = exception 없음. 또한 normalize 가 in-place 적용:
        assert out["objects_owned_by_background"] == ["door", "window"]

    def test_normalizes_in_place_strip_dedupe_sort(self) -> None:
        out = _base_output(objects_owned_by_background=["  window ", "door", "door", "window"])
        validate_bg_prompt_output(out, "bg1", {"S1_Shot1"})
        assert out["objects_owned_by_background"] == ["door", "window"]

    def test_missing_field_fails(self) -> None:
        out = _base_output()
        del out["objects_owned_by_background"]
        with pytest.raises(ValueError, match="objects_owned_by_background"):
            validate_bg_prompt_output(out, "bg1", {"S1_Shot1"})

    def test_empty_list_fails(self) -> None:
        out = _base_output(objects_owned_by_background=[])
        with pytest.raises(ValueError, match="objects_owned_by_background"):
            validate_bg_prompt_output(out, "bg1", {"S1_Shot1"})

    def test_normalize_drops_all_blank_then_fails(self) -> None:
        # Wave 6 cascade: normalize_owned_list 가 비-string (None) 을 silent drop
        # 대신 AppError raise. 의도(=blank/null silent 통과 차단) 보존하되 raise
        # 클래스 변경 — AppError 가 ValueError 의 부모(Exception) sub 가 아니므로
        # pytest.raises(Exception) 으로 양쪽 모두 cover.
        from app.core.errors import AppError
        out = _base_output(objects_owned_by_background=["", "  ", None])
        with pytest.raises((ValueError, AppError)):
            validate_bg_prompt_output(out, "bg1", {"S1_Shot1"})

    def test_normalize_all_whitespace_fails_after_normalize(self) -> None:
        # Wave 6: 진정한 whitespace-only 만 silent drop → 결과 빈 list →
        # validate_bg_prompt_output 의 후속 ValueError.
        out = _base_output(objects_owned_by_background=["", "  ", "\t"])
        with pytest.raises(ValueError, match="objects_owned_by_background"):
            validate_bg_prompt_output(out, "bg1", {"S1_Shot1"})

    def test_non_list_fails(self) -> None:
        out = _base_output(objects_owned_by_background="not a list")
        with pytest.raises(ValueError, match="objects_owned_by_background"):
            validate_bg_prompt_output(out, "bg1", {"S1_Shot1"})

    # round 5 BLOCKING 1 + round 6 BLOCKING 3: 한국어 시나리오에서도 owned 영어
    # canonical 강제. raw list 에 non-ASCII entry 가 하나라도 있으면 raise.
    def test_korean_mixed_owned_raises_not_silent_drop(self) -> None:
        # round 6 BLOCKING 3: 한국어 entry 가 섞이면 silent drop 금지 → raise.
        out = _base_output(objects_owned_by_background=["문", "창문", "TV"])
        with pytest.raises(ValueError, match="non-ASCII"):
            validate_bg_prompt_output(out, "bg1", {"S1_Shot1"})

    def test_all_korean_owned_fails(self) -> None:
        out = _base_output(objects_owned_by_background=["문", "창문"])
        with pytest.raises(ValueError, match="non-ASCII"):
            validate_bg_prompt_output(out, "bg1", {"S1_Shot1"})

    def test_japanese_kana_owned_fails(self) -> None:
        out = _base_output(objects_owned_by_background=["ドア", "テーブル"])
        with pytest.raises(ValueError, match="non-ASCII"):
            validate_bg_prompt_output(out, "bg1", {"S1_Shot1"})

    def test_cjk_ideograph_owned_fails(self) -> None:
        out = _base_output(objects_owned_by_background=["门", "桌子"])
        with pytest.raises(ValueError, match="non-ASCII"):
            validate_bg_prompt_output(out, "bg1", {"S1_Shot1"})

    def test_pure_ascii_passes(self) -> None:
        # 정상 path: 모두 영어.
        out = _base_output(objects_owned_by_background=["TV", "door", "window"])
        validate_bg_prompt_output(out, "bg1", {"S1_Shot1"})
        assert out["objects_owned_by_background"] == ["TV", "door", "window"]
