"""Cheap pre-evaluation validation shared by both Meta-Harness proposers.

The upstream task examples first import-check every generated module and Terminal-Bench additionally
runs a one-task smoke test.  A task-agnostic scaffold cannot know those interfaces, so Meta-Harness
always applies the safe structural fallback (Python ``compile``) and then, when the bound task or
evaluator exposes it, calls this optional contract::

    validate_candidate(genome: Genome) -> None | bool | (bool, str) | EvalResult

``None`` means that no additional task-specific gate applies.  This keeps generic tasks runnable
while letting interface-heavy tasks reproduce the reference's import/smoke gate before the expensive
full evaluation.
"""
from __future__ import annotations

from collections.abc import Callable

from ...records import EvalResult, Genome


def validate_candidate_source(source: str | None, *, name: str, axis: str = "",
                              hypothesis: str = "", language: str = "python",
                              validator: Callable | None = None) -> str:
    """Return ``""`` when a candidate passes, otherwise a human-readable rejection reason."""
    if source is None:
        return "no candidate program was produced"

    if language.lower() in {"python", "py"}:
        try:
            compile(source, f"<candidate:{name}>", "exec")
        except Exception as exc:  # noqa: BLE001 - syntax/encoding/value failures are all gate failures
            return f"{type(exc).__name__}: {exc}"

    if not callable(validator):
        return ""

    genome = Genome(content=source, metadata={
        "candidate_name": name,
        "axis": axis,
        "hypothesis": hypothesis,
    })
    try:
        verdict = validator(genome)
    except Exception as exc:  # noqa: BLE001 - a broken smoke check rejects rather than crashing search
        return f"task validator raised {type(exc).__name__}: {exc}"

    if verdict is None or verdict is True:
        return ""
    if verdict is False:
        return "task-specific candidate validation failed"
    if isinstance(verdict, EvalResult):
        if verdict.valid:
            return ""
        return str(verdict.text_feedback
                   or verdict.artifacts.get("text_feedback")
                   or verdict.artifacts.get("error")
                   or "task-specific candidate validation failed")
    if isinstance(verdict, tuple) and len(verdict) == 2:
        ok, detail = verdict
        return "" if bool(ok) else str(detail or "task-specific candidate validation failed")
    raise TypeError(
        "validate_candidate() must return None, bool, (bool, detail), or EvalResult; "
        f"got {type(verdict).__name__}")
