# recoil/pipeline/tests/test_coverage_mode_profiles.py
"""Schema + accessor tests for coverage_mode_preferences on video model profiles.

Guards against silent reintroduction of the bug where coverage_planner.py
hardcoded `t2v` regardless of model. Every video model referenced in
pipeline_config.json `coverage_strategy.model_routing` MUST declare a
`coverage_mode_preferences` block with all three keys.
"""

import json
from pathlib import Path

import pytest

CONFIG_DIR = Path(__file__).resolve().parent.parent.parent / "config"
PROFILES_PATH = CONFIG_DIR / "model_profiles.json"
PIPELINE_CONFIG_PATH = CONFIG_DIR / "pipeline_config.json"

REQUIRED_KEYS = {"character_pass", "env_with_frame", "env_without_frame"}
ALLOWED_MODES = {"i2v", "r2v", "t2v"}


def _load_profiles() -> dict:
    return json.loads(PROFILES_PATH.read_text(encoding="utf-8"))


def _routed_models() -> set[str]:
    """Models referenced from coverage_strategy.model_routing."""
    cfg = json.loads(PIPELINE_CONFIG_PATH.read_text(encoding="utf-8"))
    routing = cfg.get("coverage_strategy", {}).get("model_routing", {})
    return {
        v for k, v in routing.items() if not k.startswith("_") and isinstance(v, str)
    }


def test_every_routed_model_declares_coverage_mode_preferences():
    profiles = _load_profiles()
    failures = []
    for model_id in _routed_models():
        profile = profiles.get(model_id)
        if profile is None:
            failures.append(f"{model_id}: routed but not in model_profiles.json")
            continue
        prefs = profile.get("coverage_mode_preferences")
        if not prefs:
            failures.append(
                f"{model_id}: missing `coverage_mode_preferences` block "
                f"(required for any model in coverage_strategy.model_routing)"
            )
            continue
        missing = REQUIRED_KEYS - {k for k in prefs if not k.startswith("_")}
        if missing:
            failures.append(f"{model_id}: missing keys {sorted(missing)}")
    assert not failures, "\n".join(failures)


def test_coverage_mode_values_are_valid():
    """Mode values must be one of the modes the engine actually understands."""
    profiles = _load_profiles()
    failures = []
    for model_id, profile in profiles.items():
        if not isinstance(profile, dict):
            continue
        prefs = profile.get("coverage_mode_preferences")
        if not prefs:
            continue
        for key, val in prefs.items():
            if key.startswith("_"):
                continue
            if val not in ALLOWED_MODES:
                failures.append(
                    f"{model_id}.{key} = '{val}' (allowed: {sorted(ALLOWED_MODES)})"
                )
    assert not failures, "\n".join(failures)


def test_get_coverage_mode_returns_seeddance_preferences():
    from recoil.core.model_profiles import get_coverage_mode

    assert get_coverage_mode("seeddance-2.0", "character", False) == "r2v"
    assert get_coverage_mode("seeddance-2.0", "character", True) == "r2v"
    assert get_coverage_mode("seeddance-2.0", "env", True) == "i2v"
    assert get_coverage_mode("seeddance-2.0", "env", False) == "r2v"


def test_get_coverage_mode_returns_kling_preferences():
    from recoil.core.model_profiles import get_coverage_mode

    assert get_coverage_mode("kling-v3", "character", False) == "i2v"
    assert get_coverage_mode("kling-v3", "env", True) == "i2v"
    assert get_coverage_mode("kling-v3", "env", False) == "t2v"


def test_get_coverage_mode_returns_veo_preferences():
    from recoil.core.model_profiles import get_coverage_mode

    assert get_coverage_mode("veo-3.1", "env", True) == "i2v"
    assert get_coverage_mode("veo-3.1", "env", False) == "t2v"


def test_get_coverage_mode_raises_loud_for_model_without_block():
    from recoil.core.model_profiles import get_coverage_mode

    # Image model has no coverage_mode_preferences — caller-friendly KeyError.
    with pytest.raises(KeyError) as excinfo:
        get_coverage_mode("gemini-3-pro-image-preview", "character", False)
    msg = str(excinfo.value)
    assert "coverage_mode_preferences" in msg
    assert "model_profiles.json" in msg


def test_get_coverage_mode_raises_for_unknown_model():
    from recoil.core.model_profiles import get_coverage_mode

    with pytest.raises(KeyError):
        get_coverage_mode("does-not-exist", "character", False)
