"""Reporting models for local evaluation checks."""

from __future__ import annotations

from pydantic import BaseModel, ConfigDict, Field

from agentbyte.eval.checks.types import CheckResult
from agentbyte.eval.types import EvalScore


class EvalNotPassedError(Exception):
    """Raised when an evaluation report contains failures."""


class EvalItemReport(BaseModel):
    """Per-item report for local evaluation checks."""

    model_config = ConfigDict(frozen=True)

    task_name: str = Field(..., description="Task name")
    score: EvalScore | None = Field(
        default=None,
        description="Optional numeric score associated with the item",
    )
    passed: bool = Field(..., description="Whether the item passed")
    check_results: list[CheckResult] = Field(
        default_factory=list,
        description="Per-check results for the item",
    )
    error: str | None = Field(default=None, description="Error details if the item errored")

    @property
    def is_error(self) -> bool:
        """Whether this item errored."""
        return self.error is not None

    @property
    def is_passed(self) -> bool:
        """Whether this item passed."""
        return self.passed and not self.is_error

    @property
    def is_failed(self) -> bool:
        """Whether this item failed at least one check."""
        return not self.passed and not self.is_error


class EvalReport(BaseModel):
    """Aggregated report from local evaluation checks."""

    model_config = ConfigDict(frozen=True)

    items: list[EvalItemReport] = Field(default_factory=list)
    threshold: float = Field(default=7.0, ge=0.0, le=10.0)

    @property
    def passed(self) -> int:
        """Number of items that passed."""
        return sum(1 for item in self.items if item.is_passed)

    @property
    def failed(self) -> int:
        """Number of items that failed."""
        return sum(1 for item in self.items if item.is_failed or item.is_error)

    @property
    def total(self) -> int:
        """Total number of items."""
        return len(self.items)

    @property
    def all_passed(self) -> bool:
        """Whether all items passed."""
        return self.total > 0 and self.failed == 0

    @property
    def avg_score(self) -> float:
        """Average numeric score across items that carry one."""
        scores = [item.score.overall for item in self.items if item.score is not None]
        if not scores:
            return 0.0
        return sum(scores) / len(scores)

    def raise_for_status(self, msg: str | None = None) -> None:
        """Raise EvalNotPassedError if any item failed or errored."""
        if self.all_passed:
            return
        detail = msg or f"Evaluation report: {self.passed} passed, {self.failed} failed."
        failure_summaries: list[str] = []
        for item in self.items:
            if item.is_passed:
                continue
            if item.error:
                failure_summaries.append(f"{item.task_name}: error={item.error}")
                continue
            reasons = []
            for result in item.check_results:
                reason = getattr(result, "reason", None)
                passed = getattr(result, "passed", None)
                name = getattr(result, "check_name", "check")
                if passed is False:
                    reasons.append(f"{name}: {reason}")
            if reasons:
                failure_summaries.append(f"{item.task_name}: {'; '.join(reasons)}")
        if failure_summaries:
            detail += " " + " | ".join(failure_summaries)
        raise EvalNotPassedError(detail)


__all__ = ["EvalNotPassedError", "EvalItemReport", "EvalReport"]
