"""Local evaluator and threshold bridge for eval checks."""

from __future__ import annotations

import asyncio

from agentbyte.cancellation_token import CancellationToken
from agentbyte.eval.base import BaseEvalJudge
from agentbyte.eval.report import EvalItemReport, EvalReport
from agentbyte.eval.types import EvalTrajectory

from .types import CheckResult, EvalCheck


class LocalEvaluator:
    """Run one or more local checks across a trajectory batch."""

    def __init__(self, *checks: EvalCheck, name: str = "Local") -> None:
        self.name = name
        self._checks = checks

    async def evaluate(
        self,
        trajectories: list[EvalTrajectory],
        *,
        eval_name: str = "Local Eval",
        cancellation_token: CancellationToken | None = None,
    ) -> EvalReport:
        items: list[EvalItemReport] = []
        for trajectory in trajectories:
            if cancellation_token and cancellation_token.is_cancelled():
                break
            try:
                check_results = await asyncio.gather(
                    *[self._run_check(check, trajectory) for check in self._checks]
                )
                passed = all(result.passed for result in check_results)
                score = self._choose_score(check_results)
                items.append(
                    EvalItemReport(
                        task_name=trajectory.task.name,
                        score=score,
                        passed=passed,
                        check_results=check_results,
                        error=None,
                    )
                )
            except Exception as exc:
                items.append(
                    EvalItemReport(
                        task_name=trajectory.task.name,
                        score=None,
                        passed=False,
                        check_results=[],
                        error=str(exc),
                    )
                )
        return EvalReport(items=items)

    async def _run_check(
        self,
        check: EvalCheck,
        trajectory: EvalTrajectory,
    ) -> CheckResult:
        result = check(trajectory)
        if asyncio.iscoroutine(result):
            result = await result
        return result

    @staticmethod
    def _choose_score(check_results: list[CheckResult]):
        for result in check_results:
            score = getattr(result, "score", None)
            if score is not None:
                from agentbyte.eval.types import EvalScore

                numeric_score = max(min(score, 10.0), 0.0)
                return EvalScore(
                    overall=numeric_score,
                    dimensions={"overall": numeric_score},
                    reasoning={"overall": result.reason},
                    trajectory=None,
                    metadata={"source_check": result.check_name},
                )
        return None


def threshold_gate(
    judge: BaseEvalJudge,
    threshold: float = 7.0,
) -> EvalCheck:
    """Bridge a numeric eval judge into a pass/fail local check."""

    async def _check(trajectory: EvalTrajectory) -> CheckResult:
        score = await judge.score(trajectory)
        passed = score.overall >= threshold
        return CheckResult(
            passed=passed,
            reason=f"overall={score.overall:.3f}, threshold={threshold:.3f}",
            check_name=f"threshold_gate[{judge.name}]",
            score=score.overall,
        )

    return _check


__all__ = ["LocalEvaluator", "threshold_gate"]
