"""ToolSelectionMetric — deterministic position-wise tool name matching."""

from __future__ import annotations

from evalkit.judges.base import BaseJudge
from evalkit.metrics.base import BaseMetric
from evalkit.models import MetricScore, TestCase


class ToolSelectionMetric(BaseMetric):
    """Measures whether the agent called the right tools in the right order.

    Compares expected and actual tool call sequences position by position.
    Score = matches / max(len(expected), len(actual)).
    An empty expected and empty actual list yields a perfect score of 1.0.
    No judge call is made; the metric is fully deterministic.
    """

    name: str = "tool_selection"

    def __init__(self, threshold: float = 0.5) -> None:
        self.threshold = threshold

    def _build_prompt(self, test_case: TestCase) -> str:
        return ""  # not used — deterministic metric

    def _compute(self, test_case: TestCase) -> MetricScore:
        if test_case.expected_tool_calls is None or test_case.actual_tool_calls is None:
            return MetricScore(
                metric_name=self.name,
                score=0.0,
                reason="ToolSelectionMetric requires both expected_tool_calls and actual_tool_calls",
                passed=False,
            )

        expected = test_case.expected_tool_calls
        actual = test_case.actual_tool_calls
        total = max(len(expected), len(actual))

        if total == 0:
            score = 1.0
        else:
            matches = sum(1 for i in range(min(len(expected), len(actual))) if expected[i].name == actual[i].name)
            score = matches / total

        passed = score >= self.threshold
        return MetricScore(
            metric_name=self.name,
            score=score,
            reason=f"Matched {int(score * total) if total else 0} of {total} tool calls in order",
            passed=passed,
        )

    def score(self, test_case: TestCase, judge: BaseJudge | None) -> MetricScore:
        return self._compute(test_case)

    async def score_async(self, test_case: TestCase, judge: BaseJudge | None) -> MetricScore:
        return self._compute(test_case)
