"""ROUGE score metric — n-gram recall 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 RougeMetric(BaseMetric):
    """ROUGE score between the answer and expected answer.

    Supports ``rougeL`` (longest common subsequence, default), ``rouge1``
    (unigram overlap), and ``rouge2`` (bigram overlap).  Each can report
    ``fmeasure`` (default), ``precision``, or ``recall``.

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

    name: str = "rouge"

    _VALID_MEASURES = frozenset({"fmeasure", "precision", "recall"})

    def __init__(
        self,
        threshold: float = 0.5,
        *,
        rouge_type: str = "rougeL",
        measure: str = "fmeasure",
    ) -> None:
        if measure not in self._VALID_MEASURES:
            raise ValueError(f"measure must be one of {self._VALID_MEASURES}, got {measure!r}")
        self.threshold = threshold
        self.rouge_type = rouge_type
        self.measure = measure

    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="ROUGE requires expected_answer",
                passed=False,
            )
        try:
            from rouge_score import rouge_scorer
        except ImportError as exc:
            raise ImportError("Install rouge-score: pip install evalkit-llm[rouge]") from exc

        scorer = rouge_scorer.RougeScorer([self.rouge_type], use_stemmer=True)
        scores = scorer.score(test_case.expected_answer, test_case.answer or "")
        score = getattr(scores[self.rouge_type], self.measure)
        passed = score >= self.threshold
        return MetricScore(
            metric_name=self.name,
            score=round(score, 4),
            reason=f"{self.rouge_type} {self.measure}: {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)
