# recoil/pipeline/tests/test_model_profile_schema.py
"""Property test: every model in the profile has the required Phase 1 fields.

Required fields per spec Appendix C. Empirical TBDs may be null (Phase 0.5
populates them); behavioral fields must be present and non-null.
"""

import json
from pathlib import Path

REQUIRED_BEHAVIORAL_FIELDS = {
    "provider",
    "modality",
    "max_reference_images",
    "supports_negative_prompt",
    "prompt_emphasis_syntax",
    "content_filter_sensitivity",
    "requires_content_filter_soften_retry",
    "requires_style_anchor_for_long_runs",
    "concurrent_limit",
    "allowed_reroll_strategies",
    "forbidden_reroll_strategies",
}

EMPIRICAL_FIELDS_THAT_MAY_BE_NULL = {
    "effective_max_character_refs",
    "position_bias_severity",
    "grid_reference_failure_rate",
    "content_filter_block_rate",
    "rate_limit_rpm",
    "max_subjects_per_generation",
}


def test_every_model_has_required_behavioral_fields():
    # Anchor to __file__ so the test passes regardless of cwd
    # (recoil/, CLAUDE_PROJECTS/, etc.). __file__ → recoil/pipeline/tests/
    # → walk up twice → recoil/ → /config/model_profiles.json.
    profiles_path = (
        Path(__file__).resolve().parent.parent.parent / "config" / "model_profiles.json"
    )
    profiles = json.loads(profiles_path.read_text())
    # Behavioral fields are visual-pipeline (image/video) concerns. CP-8
    # added audio_t2a + lipsync_post profiles which use a parallel adapter
    # shape and don't carry these visual-prompt knobs. CP-9 Phase 3 adds
    # the text_multimodal eval modality (gemini-3.1-pro-preview) which is
    # also non-generation and exempt for the same reason.
    NON_VISUAL_MODALITIES = {"audio_t2a", "lipsync_post", "text_multimodal"}
    failures = []
    for model_id, profile in profiles.items():
        if not isinstance(profile, dict):
            continue
        if profile.get("modality") in NON_VISUAL_MODALITIES:
            continue
        missing = REQUIRED_BEHAVIORAL_FIELDS - profile.keys()
        if missing:
            failures.append(f"{model_id}: missing {sorted(missing)}")
    assert not failures, "Models missing required Phase 1 fields:\n" + "\n".join(
        failures
    )
