"""domain_policy.yaml 스키마 검증 + cross-reference 체크."""

from __future__ import annotations

import pytest
import yaml
from pydantic import ValidationError

from policy import loader as policy_loader
from policy.schema import DomainPolicy


def test_default_yaml_loads(policy):
    """기본 yaml 이 pydantic 검증 통과."""
    assert isinstance(policy, DomainPolicy)
    assert policy.version == 1
    assert "safety_health" in policy.scope
    assert "news" in policy.scope
    assert policy.self_declare_semantics == "additive_trigger_only"


def test_subject_domains_count(policy):
    """plan 에서 정의한 9개 subject_domain 전부 존재."""
    expected = {
        "safety_reference",
        "safety_operational",
        "msds",
        "hazard_specific",
        "incident_report",
        "health_record",
        "safety_video",
        "news_item",
        "news_digest_request",
    }
    assert set(policy.subject_domains.keys()) == expected


def test_all_subject_domains_have_suggested_ui_category(policy):
    """storage_category → suggested_ui_category 리네임 확인.
    모든 도메인이 실측 enum 에서만 값을 선택.
    """
    valid = {"document", "library", "news", "memo", "audio", "video", "law"}
    for name, dom in policy.subject_domains.items():
        assert dom.suggested_ui_category in valid, (
            f"{name}.suggested_ui_category={dom.suggested_ui_category} not in enum"
        )


def test_fallback_domain_required(policy):
    """fallback_domain 필수 (INV-6)."""
    assert policy.fallback_domain.name == "generic"
    assert policy.fallback_domain.suggested_ui_category in {
        "document",
        "library",
        "news",
        "memo",
        "audio",
        "video",
        "law",
    }


def test_risk_flags_cross_reference_ok(policy):
    """default_risk_flags 에 미정의 flag 참조 없음."""
    known = set(policy.risk_flags.keys())
    for name, dom in policy.subject_domains.items():
        for flag in dom.default_risk_flags:
            assert flag in known, f"{name} references undefined flag {flag}"


def test_forbidden_rules_reference_existing_domains(policy):
    """forbidden_for_4b.applies_when_subject_in 의 도메인이 subject_domains 에 존재."""
    known = set(policy.subject_domains.keys())
    for rule in policy.forbidden_for_4b:
        for dom in rule.applies_when_subject_in:
            assert dom in known, f"{rule.id} references undefined domain {dom}"


def test_reject_unknown_flag_in_yaml(tmp_path, policy_yaml_path):
    """yaml 에 정의되지 않은 flag 를 subject_domain 이 참조하면 ValidationError."""
    with open(policy_yaml_path, encoding="utf-8") as f:
        raw = yaml.safe_load(f)
    # 가짜 flag 주입
    raw["subject_domains"]["safety_reference"]["default_risk_flags"] = [
        "does_not_exist_flag"
    ]
    bad_yaml = tmp_path / "bad.yaml"
    bad_yaml.write_text(yaml.safe_dump(raw, allow_unicode=True))

    policy_loader.clear_cache()
    with pytest.raises(ValidationError):
        policy_loader.load_policy(str(bad_yaml))


def test_reject_invalid_ui_category(tmp_path, policy_yaml_path):
    """suggested_ui_category 에 enum 외 값 들어가면 ValidationError."""
    with open(policy_yaml_path, encoding="utf-8") as f:
        raw = yaml.safe_load(f)
    raw["subject_domains"]["safety_reference"]["suggested_ui_category"] = "nonexistent"
    bad_yaml = tmp_path / "bad_cat.yaml"
    bad_yaml.write_text(yaml.safe_dump(raw, allow_unicode=True))

    policy_loader.clear_cache()
    with pytest.raises(ValidationError):
        policy_loader.load_policy(str(bad_yaml))


def test_reject_too_long_synthesis_directive(tmp_path, policy_yaml_path):
    """500 chars 초과 synthesis_directive 는 reject."""
    with open(policy_yaml_path, encoding="utf-8") as f:
        raw = yaml.safe_load(f)
    raw["risk_flags"]["safety_legal_interpretation"]["synthesis_directive"] = "x" * 600
    bad_yaml = tmp_path / "bad_dir.yaml"
    bad_yaml.write_text(yaml.safe_dump(raw, allow_unicode=True))

    policy_loader.clear_cache()
    with pytest.raises(ValidationError):
        policy_loader.load_policy(str(bad_yaml))
