"""Base check class and utilities."""

import time
from abc import ABC, abstractmethod
from typing import Any

from ai_qa.core.context import Context
from ai_qa.core.result import CheckResult


class BaseCheck(ABC):
    """Abstract base class that every check extends.

    Attributes:
        name: Check identifier (set by @register).
        requires_llm: Whether this check needs an LLM provider.
        version: Semantic version of the check.
        description: What this check validates.
        threshold: Confidence threshold for passing (0.0-1.0).
        config: Additional configuration dict.
    """

    name: str = "unnamed"
    requires_llm: bool = True
    version: str = "1.0.0"
    description: str = ""

    def __init__(self, threshold: float = 0.5, **kwargs: Any) -> None:
        """Initialize check with threshold and config.

        Args:
            threshold: Confidence threshold for passing (0.0-1.0).
            **kwargs: Additional configuration stored in self.config.

        Raises:
            ValueError: If threshold is outside 0.0-1.0.
        """
        if not (0.0 <= threshold <= 1.0):
            raise ValueError(
                f"threshold must be between 0.0 and 1.0, got {threshold}"
            )
        self.threshold = threshold
        self.config = kwargs

    @abstractmethod
    def evaluate(self, ctx: Context) -> CheckResult:
        """Evaluate the check. Must be implemented by subclasses.

        Args:
            ctx: The context to evaluate.

        Returns:
            Fully populated CheckResult.
        """
        pass

    def validate_context(self, ctx: Context) -> None:
        """Validate that required Context fields are present.

        Override in subclasses to check for required fields.
        Raises ContextValidationError if validation fails.

        Args:
            ctx: The context to validate.

        Raises:
            ContextValidationError: If required fields are missing.
        """
        # Base implementation is a no-op
        pass

    def run(self, ctx: Context) -> CheckResult:
        """Run the check with timing and error handling.

        Wraps evaluate() with:
        - Timing (latency_ms)
        - Error handling (returns failed CheckResult on exception)
        - Sets check_version on result
        - Validates that evaluate() returned a CheckResult

        Args:
            ctx: The context to evaluate.

        Returns:
            CheckResult with latency and version set.
        """
        start = time.time()
        try:
            result = self.evaluate(ctx)

            # Validate return type
            if not isinstance(result, CheckResult):
                return CheckResult(
                    name=self.name,
                    passed=False,
                    score=0.0,
                    threshold=self.threshold,
                    evidence=(
                        f"Internal error: evaluate() returned {type(result)}, "
                        "not CheckResult"
                    ),
                    check_version=self.version,
                )

            # Set timing and version
            result.latency_ms = (time.time() - start) * 1000
            result.check_version = self.version
            return result

        except Exception as e:
            # Return failed result instead of raising
            return CheckResult(
                name=self.name,
                passed=False,
                score=0.0,
                threshold=self.threshold,
                evidence=f"Check failed with exception: {type(e).__name__}: {str(e)}",
                latency_ms=(time.time() - start) * 1000,
                check_version=self.version,
            )
