"""String containment metric — does the answer contain the expected answer?"""

from __future__ import annotations

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


class StringContainmentMetric(BaseMetric):
    """Binary check: does the answer contain the expected answer as a substring?

    Useful when expected answers are short (e.g., "Paris") and actual answers
    are verbose (e.g., "The capital of France is Paris.").

    Both strings are normalized before comparison.  Score is 1.0 (contained)
    or 0.0 (not contained).  Deterministic — no judge call.
    """

    name: str = "string_containment"

    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="String containment requires expected_answer",
                passed=False,
            )
        answer = normalize_text(test_case.answer or "")
        expected = normalize_text(test_case.expected_answer)
        contained = expected in answer
        return MetricScore(
            metric_name=self.name,
            score=1.0 if contained else 0.0,
            reason="Expected answer found in response" if contained else "Expected answer not found in response",
            passed=contained,
        )

    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)
