"""Result classes for check execution."""

from __future__ import annotations

import json
from datetime import datetime
from typing import Any

from pydantic import BaseModel, field_validator

from ai_qa.core.context import Context


class CheckResult(BaseModel):
    """Structured result from a single check execution."""

    name: str
    passed: bool
    score: float
    threshold: float
    evidence: str
    latency_ms: float = 0.0
    cost_usd: float = 0.0
    tokens_used: dict[str, int] = {}
    raw: dict[str, Any] = {}
    check_version: str = ""
    cached: bool = False

    @field_validator("score")
    @classmethod
    def score_bounds(cls, v: float) -> float:
        """Validate score is between 0.0 and 1.0."""
        if not (0.0 <= v <= 1.0):
            raise ValueError(f"score must be between 0.0 and 1.0, got {v}")
        return v

    @field_validator("threshold")
    @classmethod
    def threshold_bounds(cls, v: float) -> float:
        """Validate threshold is between 0.0 and 1.0."""
        if not (0.0 <= v <= 1.0):
            raise ValueError(f"threshold must be between 0.0 and 1.0, got {v}")
        return v

    @field_validator("name")
    @classmethod
    def name_not_empty(cls, v: str) -> str:
        """Validate name is non-empty."""
        if not v or not isinstance(v, str):
            raise ValueError("name must be a non-empty string")
        return v

    @field_validator("evidence")
    @classmethod
    def evidence_not_empty(cls, v: str) -> str:
        """Validate evidence is non-empty."""
        if not v or not isinstance(v, str):
            raise ValueError("evidence must be a non-empty string")
        return v

    @field_validator("latency_ms")
    @classmethod
    def latency_non_negative(cls, v: float) -> float:
        """Validate latency_ms is non-negative."""
        if v < 0:
            raise ValueError(f"latency_ms must be >= 0, got {v}")
        return v

    @field_validator("cost_usd")
    @classmethod
    def cost_non_negative(cls, v: float) -> float:
        """Validate cost_usd is non-negative."""
        if v < 0:
            raise ValueError(f"cost_usd must be >= 0, got {v}")
        return v


class ValidationResult(BaseModel):
    """Aggregate result from running multiple checks."""

    checks: list[CheckResult]
    context: Context
    timestamp: datetime | None = None

    def __init__(self, **data: Any) -> None:
        """Initialize with default timestamp."""
        if "timestamp" not in data or data["timestamp"] is None:
            data["timestamp"] = datetime.now()
        super().__init__(**data)

    @property
    def passed(self) -> bool:
        """True only if ALL checks passed."""
        return all(check.passed for check in self.checks)

    @property
    def scores(self) -> dict[str, float]:
        """Return {check_name: score} mapping."""
        return {check.name: check.score for check in self.checks}

    @property
    def failed_checks(self) -> list[CheckResult]:
        """Return only the checks that failed."""
        return [check for check in self.checks if not check.passed]

    @property
    def total_cost(self) -> float:
        """Sum of all check costs."""
        return sum(check.cost_usd for check in self.checks)

    @property
    def total_latency_ms(self) -> float:
        """Sum of all check latencies."""
        return sum(check.latency_ms for check in self.checks)

    @property
    def summary(self) -> str:
        """Human-readable one-line summary."""
        passed_count = sum(1 for check in self.checks if check.passed)
        total_count = len(self.checks)
        if self.passed:
            return f"{total_count}/{total_count} checks passed"
        else:
            failed_names = [
                f"{check.name} ({check.score:.2f} < {check.threshold:.2f})"
                for check in self.failed_checks
            ]
            return f"{passed_count}/{total_count} checks passed ({', '.join(failed_names)} failed)"

    def to_dict(self) -> dict[str, Any]:
        """Return full JSON-serializable representation."""
        return {
            "checks": [check.model_dump() for check in self.checks],
            "context": self.context.model_dump(),
            "timestamp": self.timestamp.isoformat() if self.timestamp else None,
            "passed": self.passed,
            "scores": self.scores,
            "total_cost": self.total_cost,
            "total_latency_ms": self.total_latency_ms,
            "summary": self.summary,
        }

    def to_json(self) -> str:
        """Return JSON string."""
        return json.dumps(self.to_dict(), indent=2)

    def get_check(self, name: str) -> CheckResult | None:
        """Look up check by name. Returns None if not found."""
        for check in self.checks:
            if check.name == name:
                return check
        return None

    def assert_passed(self) -> None:
        """Raise AssertionError with summary if not passed."""
        if not self.passed:
            raise AssertionError(f"Validation failed: {self.summary}")
