"""ToolParameterMetric — deterministic parameter matching for agent tool calls."""

from __future__ import annotations

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


class ToolParameterMetric(BaseMetric):
    """Measures how well actual tool call parameters match expected parameters.

    Compares tool calls at the same position where names match. Per-tool score
    is the fraction of key-value pairs that match, divided by the max of expected
    and actual parameter counts. Final score is the average across matched pairs.
    """

    name: str = "tool_parameter"

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

    def _build_prompt(self, test_case: TestCase) -> str:
        return ""

    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="Missing expected or actual tool calls.",
                passed=False,
            )

        expected = test_case.expected_tool_calls
        actual = test_case.actual_tool_calls

        pair_scores: list[float] = []
        for exp_call, act_call in zip(expected, actual):
            if exp_call.name != act_call.name:
                continue

            exp_params = exp_call.parameters
            act_params = act_call.parameters

            max_params = max(len(exp_params), len(act_params))
            if max_params == 0:
                pair_scores.append(1.0)
                continue

            matching = sum(1 for k, v in exp_params.items() if act_params.get(k) == v)
            pair_scores.append(matching / max_params)

        if not pair_scores:
            score = 0.0
        else:
            score = sum(pair_scores) / len(pair_scores)

        passed = score >= self.threshold
        return MetricScore(
            metric_name=self.name,
            score=score,
            reason=f"Parameter match score: {score:.3f} across {len(pair_scores)} tool pair(s).",
            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)
