"""BLEU score metric — n-gram precision between answer and expected answer."""

from __future__ import annotations

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


class BleuMetric(BaseMetric):
    """BLEU score between the answer and expected answer.

    Uses sentence-level BLEU with smoothing to avoid zero scores on short
    texts.  Measures n-gram precision with a brevity penalty.

    Requires ``nltk``: ``pip install evalkit-llm[bleu]``.
    Deterministic — no judge call.
    """

    name: str = "bleu"

    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_answer is None:
            return MetricScore(
                metric_name=self.name,
                score=0.0,
                reason="BLEU requires expected_answer",
                passed=False,
            )
        try:
            from nltk.translate.bleu_score import SmoothingFunction, sentence_bleu
        except ImportError as exc:
            raise ImportError("Install nltk: pip install evalkit-llm[bleu]") from exc

        reference = test_case.expected_answer.split()
        hypothesis = (test_case.answer or "").split()
        if not reference or not hypothesis:
            score = 0.0
        else:
            smoothing = SmoothingFunction().method1
            score = sentence_bleu([reference], hypothesis, smoothing_function=smoothing)
        passed = score >= self.threshold
        return MetricScore(
            metric_name=self.name,
            score=round(score, 4),
            reason=f"BLEU score: {score:.2%}",
            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)
