"""Cross-config validator.

Validates that every model id named in provider_strategy.json,
pipeline_config.json::coverage_strategy.model_routing, and model_roles.json
exists in model_profiles.json. Raises CrossConfigDriftError on drift.

Per Tenet 1 (SSOT) — cross-references between config files must be
validated at load time, not at use time.
"""

from __future__ import annotations

from typing import Any

from recoil.core.exceptions import CrossConfigDriftError


def validate_cross_config(
    *,
    model_profiles: dict[str, Any],
    provider_strategy: dict[str, Any],
    pipeline_config: dict[str, Any],
    model_roles: dict[str, Any],
) -> None:
    """Raise CrossConfigDriftError if any cross-reference fails to resolve."""
    from recoil.core.model_profiles import iter_model_ids, iter_strategy_model_ids

    profile_ids = set(iter_model_ids(model_profiles))

    drift_messages: list[str] = []

    # 1. provider_strategy.json keys must be in model_profiles.json
    strategy_ids = set(iter_strategy_model_ids(provider_strategy))
    orphan_strategy = strategy_ids - profile_ids
    if orphan_strategy:
        drift_messages.append(
            f"provider_strategy.json names {sorted(orphan_strategy)} which "
            f"are not in model_profiles.json"
        )

    # 2. pipeline_config.coverage_strategy.model_routing values must be in
    #    model_profiles.json AND have a coverage_mode_preferences block.
    coverage_strategy = pipeline_config.get("coverage_strategy") or {}
    model_routing = coverage_strategy.get("model_routing") or {}
    for routing_key, model_id in model_routing.items():
        if routing_key.startswith("_"):
            continue
        if not isinstance(model_id, str):
            continue
        if model_id not in profile_ids:
            drift_messages.append(
                f"pipeline_config.coverage_strategy.model_routing.{routing_key} = "
                f"{model_id!r} which is not in model_profiles.json"
            )
            continue
        profile = model_profiles[model_id]
        if not isinstance(profile, dict) or "coverage_mode_preferences" not in profile:
            drift_messages.append(
                f"pipeline_config.coverage_strategy.model_routing.{routing_key} = "
                f"{model_id!r} which lacks `coverage_mode_preferences` block "
                f"in model_profiles.json"
            )

    # 3. model_roles.json values (the role-to-model-id leaves) must be in
    #    model_profiles.json
    def _walk_roles(node: Any, path: str = "") -> list[tuple[str, str]]:
        out = []
        if isinstance(node, dict):
            for k, v in node.items():
                if k.startswith("_"):
                    continue
                out.extend(_walk_roles(v, f"{path}.{k}" if path else k))
        elif isinstance(node, str):
            out.append((path, node))
        return out

    for role_path, model_id in _walk_roles(model_roles):
        if role_path == "schema_version":
            continue
        if model_id not in profile_ids:
            drift_messages.append(
                f"model_roles.json[{role_path}] = {model_id!r} which is not "
                f"in model_profiles.json"
            )

    if drift_messages:
        msg = "Cross-config drift detected:\n  " + "\n  ".join(drift_messages)
        raise CrossConfigDriftError(msg)


__all__ = ["validate_cross_config"]
