from __future__ import annotations

"""Core helpers for loading and executing dataset recipes.

Recipes are configured via small JSON or YAML files on disk. Each config
must specify a ``kind`` field (e.g. ``"rag_chunks"``) which determines the
implementation that should be used.
"""

from pathlib import Path
from typing import Any, Dict, Tuple
import json

from ..core.models import DatasetRecipe


class RecipeConfigError(RuntimeError):
    """Raised when a recipe configuration is invalid or unsupported."""


def _load_raw_config(path: Path) -> Dict[str, Any]:
    text = path.read_text(encoding="utf-8")
    suffix = path.suffix.lower()

    if suffix == ".json":
        return json.loads(text)

    if suffix in {".yml", ".yaml"}:
        try:
            import yaml  # type: ignore[import-not-found]
        except Exception as exc:  # pragma: no cover - optional dependency
            raise RecipeConfigError(
                "YAML recipe provided but PyYAML is not installed. "
                "Install 'pyyaml' or use JSON instead."
            ) from exc

        return yaml.safe_load(text)  # type: ignore[no-any-return]

    raise RecipeConfigError(f"Unsupported recipe file extension: {suffix}")


def load_recipe_from_file(path: str | Path) -> Tuple[DatasetRecipe, str]:
    """Load a :class:`DatasetRecipe` and its ``kind`` from a config file.

    The config must at minimum provide:

    - ``kind``: name of the recipe implementation (e.g. ``"rag_chunks"``).
    - ``name``: unique recipe name.

    Other :class:`DatasetRecipe` fields are optional and default to sensible
    empty values.
    """

    p = Path(path)
    data = _load_raw_config(p)

    kind = data.get("kind")
    if not isinstance(kind, str):
        raise RecipeConfigError("Recipe config is missing a string 'kind' field")

    name = data.get("name")
    if not isinstance(name, str):
        raise RecipeConfigError("Recipe config is missing a string 'name' field")

    recipe = DatasetRecipe(
        name=name,
        description=str(data.get("description", "")),
        input_query=str(data.get("input_query", "")),
        prompt_template=str(data.get("prompt_template", "")),
        output_schema=data.get("output_schema", {}) or {},
        params=data.get("params", {}) or {},
    )

    return recipe, kind

