"""CP-9 Phase 3 — EvalNode Protocol.

Validates runtime_checkable Protocol semantics: duck-typed classes with
judge_id, model_used, and evaluate() pass isinstance; classes missing
any pass fail. Per audit § 12f item 3, the call shape is
``evaluate(context: EvalContext) -> EvalResult`` — Phase 7's
LegacyFlashCriticEvalNode adapter implements exactly that.
"""

import sys
import pathlib
from pathlib import Path

sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent.parent.parent))
from recoil.core.paths import ensure_pipeline_importable  # noqa: E402

ensure_pipeline_importable()

from recoil.pipeline.core.eval import EvalNode, EvalContext, EvalResult  # noqa: E402


class _DuckJudge:
    """Minimal duck class — has all three required attrs."""
    judge_id = "duck"
    model_used = "duck-model"

    def evaluate(self, context: EvalContext) -> EvalResult:
        return EvalResult(
            score=0.5, reasoning="duck", judge_id=self.judge_id,
            model_used=self.model_used,
        )


class _MissingEvaluate:
    judge_id = "x"
    model_used = "x"


class _MissingJudgeId:
    model_used = "x"

    def evaluate(self, context):  # type: ignore[no-untyped-def]
        return None


class _MissingModelUsed:
    judge_id = "x"

    def evaluate(self, context):  # type: ignore[no-untyped-def]
        return None


def test_eval_node_protocol_isinstance_duck_typed() -> None:
    assert isinstance(_DuckJudge(), EvalNode)


def test_eval_node_protocol_isinstance_missing_evaluate_fails() -> None:
    assert not isinstance(_MissingEvaluate(), EvalNode)


def test_eval_node_protocol_isinstance_missing_judge_id_fails() -> None:
    assert not isinstance(_MissingJudgeId(), EvalNode)


def test_eval_node_protocol_isinstance_missing_model_used_fails() -> None:
    assert not isinstance(_MissingModelUsed(), EvalNode)


def test_eval_node_protocol_evaluate_returns_eval_result(tmp_path: Path) -> None:
    img = tmp_path / "a.png"
    img.write_bytes(b"\x89PNG\r\n")
    judge = _DuckJudge()
    ctx = EvalContext(
        target_artifact_path=img,
        target_take=None,
        prompt="p", rubric="r", judge_id="ja",
    )
    res = judge.evaluate(ctx)
    assert isinstance(res, EvalResult)
    assert res.score == 0.5
    assert res.judge_id == "duck"
