from __future__ import annotations

"""Recipe dispatch for neuraparse.

The :func:`execute_recipe` function takes a config file, a document graph
and a target directory, and materialises a dataset artefact on disk.
"""

from pathlib import Path

from ..core.models import DocumentGraph
from .base import RecipeConfigError, load_recipe_from_file
from .basic_qa import run_basic_qa
from .cross_document_ranking import run_cross_document_ranking
from .entity_context_ranking import run_entity_context_ranking
from .entity_knowledge import run_entity_knowledge
from .graded_relevance import run_graded_relevance
from .graph_neighborhood import run_graph_neighborhood
from .multi_context_ranking import run_multi_context_ranking
from .outline_summary import run_outline_summary
from .profiles import load_profiles_from_file
from .rag_chunks import run_rag_chunks
from .section_relevance import run_section_relevance

# Built-in default profile definitions. These can be overridden or
# extended at runtime by loading an external profiles config file using
# :func:`load_profiles_from_file`.
PROFILE_DEFINITIONS: dict[str, list[str]] = {
    "graphrag": [
        "rag_chunks",
        "basic_qa",
        "outline_summary",
        "entity_knowledge",
        "graph_neighborhood",
        "section_relevance",
    ],
    # Evaluation / ranking-focused profile. Kept separate from the default
    # graphrag profile to make it easy to run evaluation tasks in isolation.
    "eval_ranking": [
        "section_relevance",
        "multi_context_ranking",
    ],
    # Advanced evaluation profile with graded relevance and cross-document ranking.
    "eval_advanced": [
        "graded_relevance",
        "cross_document_ranking",
        "entity_context_ranking",
    ],
}


def execute_recipe(
    config_path: str | Path,
    graph: DocumentGraph,
    base_dir: str | Path,
    document_id: str,
) -> Path:
    """Execute a recipe described by ``config_path`` against ``graph``.

    Currently supported ``kind`` values:

    - ``"rag_chunks"``: one chunk per paragraph node.
    - ``"basic_qa"``: one QA pair per paragraph node.
    - ``"outline_summary"``: one record per section-level summary node.
    - ``"entity_knowledge"``: one record per entity keyword aggregating mentions.
    - ``"graph_neighborhood"``: paragraph + neighborhood context (siblings, section summary).
    - ``"section_relevance"``: query/context relevance pairs based on section membership.
    - ``"multi_context_ranking"``: multi-context ranking with positive/negative labels.
    - ``"graded_relevance"``: graded relevance pairs (0-3 labels).
    - ``"cross_document_ranking"``: cross-document ranking with explicit metadata.
    - ``"entity_context_ranking"``: entity + summary context ranking.
    """

    recipe, kind = load_recipe_from_file(config_path)

    if kind == "rag_chunks":
        return run_rag_chunks(recipe, graph, base_dir, document_id)
    if kind == "basic_qa":
        return run_basic_qa(recipe, graph, base_dir, document_id)
    if kind == "outline_summary":
        return run_outline_summary(recipe, graph, base_dir, document_id)
    if kind == "entity_knowledge":
        return run_entity_knowledge(recipe, graph, base_dir, document_id)
    if kind == "graph_neighborhood":
        return run_graph_neighborhood(recipe, graph, base_dir, document_id)
    if kind == "section_relevance":
        return run_section_relevance(recipe, graph, base_dir, document_id)
    if kind == "multi_context_ranking":
        return run_multi_context_ranking(recipe, graph, base_dir, document_id)
    if kind == "graded_relevance":
        return run_graded_relevance(recipe, graph, base_dir, document_id)
    if kind == "cross_document_ranking":
        return run_cross_document_ranking(recipe, graph, base_dir, document_id)
    if kind == "entity_context_ranking":
        return run_entity_context_ranking(recipe, graph, base_dir, document_id)

    raise RecipeConfigError(f"Unknown or unsupported recipe kind: {kind}")


def execute_profile(
    profile_name: str,
    graph: DocumentGraph,
    base_dir: str | Path,
    document_id: str,
    profiles_config: str | Path | None = None,
) -> list[Path]:
    """Execute a named profile consisting of multiple recipes.

    Resolution order for profile definitions:

    1. If ``profiles_config`` is provided, load profile definitions from that
       file via :func:`load_profiles_from_file`.
    2. Fall back to the built-in :data:`PROFILE_DEFINITIONS` mapping.

    This allows projects to override or extend profiles without modifying the
    library code.
    """

    if profiles_config is not None:
        profiles = load_profiles_from_file(profiles_config)
    else:
        profiles = PROFILE_DEFINITIONS

    kinds = profiles.get(profile_name)
    if kinds is None:
        raise RecipeConfigError(f"Unknown or unsupported profile: {profile_name}")

    outputs: list[Path] = []
    for kind in kinds:
        if kind == "rag_chunks":
            outputs.append(run_rag_chunks(None, graph, base_dir, document_id))
        elif kind == "basic_qa":
            outputs.append(run_basic_qa(None, graph, base_dir, document_id))
        elif kind == "outline_summary":
            outputs.append(run_outline_summary(None, graph, base_dir, document_id))
        elif kind == "entity_knowledge":
            outputs.append(run_entity_knowledge(None, graph, base_dir, document_id))
        elif kind == "graph_neighborhood":
            outputs.append(run_graph_neighborhood(None, graph, base_dir, document_id))
        elif kind == "section_relevance":
            outputs.append(run_section_relevance(None, graph, base_dir, document_id))
        elif kind == "multi_context_ranking":
            outputs.append(run_multi_context_ranking(None, graph, base_dir, document_id))
        elif kind == "graded_relevance":
            outputs.append(run_graded_relevance(None, graph, base_dir, document_id))
        elif kind == "cross_document_ranking":
            outputs.append(run_cross_document_ranking(None, graph, base_dir, document_id))
        elif kind == "entity_context_ranking":
            outputs.append(run_entity_context_ranking(None, graph, base_dir, document_id))
        else:
            raise RecipeConfigError(
                f"Unknown or unsupported recipe kind in profile {profile_name!r}: {kind}"
            )

    return outputs
