"""LengthCheck: Validate output length is within bounds."""

from __future__ import annotations

from typing import Any

from ai_qa.checks.registry import register
from ai_qa.core.check import BaseCheck
from ai_qa.core.context import Context
from ai_qa.core.result import CheckResult
from ai_qa.exceptions import ContextValidationError


@register("length")
class LengthCheck(BaseCheck):
    """Validate output length is within specified bounds.

    Requires:
        - ctx.output: string or dict/list to measure
        - min_length: minimum length (optional, default -1 for no min)
        - max_length: maximum length (optional, default -1 for no max)

    For strings: length is character count
    For dict/list: length is number of items

    Example:
        check = LengthCheck(min_length=10, max_length=100)
    """

    requires_llm = False
    version = "1.0.0"
    description = "Validate output length is within bounds"

    def __init__(
        self,
        threshold: float = 0.5,
        min_length: int = -1,
        max_length: int = -1,
        **kwargs: Any,
    ) -> None:
        """Initialize LengthCheck.

        Args:
            threshold: Score threshold (0.0-1.0).
            min_length: Minimum length (-1 for no minimum).
            max_length: Maximum length (-1 for no maximum).
            **kwargs: Additional config kwargs.

        Raises:
            ValueError: If both bounds are -1 or invalid.
        """
        super().__init__(threshold=threshold, **kwargs)
        if min_length == -1 and max_length == -1:
            raise ValueError(
                "LengthCheck requires at least min_length or max_length.\n"
                "Example: LengthCheck(min_length=10, max_length=100)"
            )
        if min_length >= 0 and max_length >= 0 and min_length > max_length:
            raise ValueError(
                f"min_length ({min_length}) cannot be greater than max_length ({max_length})"
            )
        self.min_length = min_length
        self.max_length = max_length

    def validate_context(self, ctx: Context) -> None:
        """Validate that output exists.

        Args:
            ctx: Context to validate.

        Raises:
            ContextValidationError: If output is missing.
        """
        if ctx.output is None:
            raise ContextValidationError(
                "LengthCheck requires ctx.output to be set.\n"
                "Example: Context(output='hello world')"
            )

    def _get_length(self, obj: Any) -> int:
        """Get the length of an object.

        Args:
            obj: Object to measure.

        Returns:
            Length of the object.

        Raises:
            TypeError: If object type is not supported.
        """
        if isinstance(obj, (str, dict, list)):
            return len(obj)
        raise TypeError(
            f"Cannot measure length of {type(obj).__name__}. " "Supported types: str, dict, list"
        )

    def evaluate(self, ctx: Context) -> CheckResult:
        """Evaluate if output length is within bounds.

        Args:
            ctx: Context with output to validate.

        Returns:
            CheckResult with pass/fail and evidence.
        """
        self.validate_context(ctx)

        try:
            length = self._get_length(ctx.output)
        except TypeError as e:
            return CheckResult(
                name=self.name,
                passed=False,
                score=0.0,
                threshold=self.threshold,
                evidence=str(e),
                check_version=self.version,
            )

        # Check minimum length
        if self.min_length >= 0 and length < self.min_length:
            return CheckResult(
                name=self.name,
                passed=False,
                score=0.0,
                threshold=self.threshold,
                evidence=f"Length {length} is below minimum {self.min_length}",
                check_version=self.version,
            )

        # Check maximum length
        if self.max_length >= 0 and length > self.max_length:
            return CheckResult(
                name=self.name,
                passed=False,
                score=0.0,
                threshold=self.threshold,
                evidence=f"Length {length} exceeds maximum {self.max_length}",
                check_version=self.version,
            )

        # Within bounds
        return CheckResult(
            name=self.name,
            passed=True,
            score=1.0,
            threshold=self.threshold,
            evidence=f"Length {length} is within bounds",
            check_version=self.version,
        )
