"""Predicate-based termination condition."""

from typing import Callable, Optional, Sequence

from agentbyte.messages import Message
from agentbyte.types import StopMessage

from .base import BaseTermination


class PredicateTermination(BaseTermination):
    """
    Terminates when a caller-supplied predicate returns True.

    The predicate receives the delta messages (new messages since the last
    check) and returns True to trigger termination.  This is the catch-all
    escape hatch: any stopping criterion that isn't covered by a named
    condition can be expressed as a callable.

    Args:
        predicate: A callable ``(Sequence[Message]) -> bool``.
        reason:    Human-readable reason included in the StopMessage.
                   Defaults to ``"Predicate condition met"``.

    Example:
        # Stop when the last message contains "error"
        termination = PredicateTermination(
            lambda msgs: bool(msgs) and "error" in str(msgs[-1].content).lower(),
            reason="Error detected in agent output",
        )

        # Combine with a safety cap
        termination = (
            PredicateTermination(my_quality_check) | MaxMessageTermination(20)
        )
    """

    def __init__(
        self,
        predicate: Callable[[Sequence[Message]], bool],
        reason: str = "Predicate condition met",
    ) -> None:
        super().__init__()
        if not callable(predicate):
            raise ValueError("predicate must be callable")
        if not reason:
            raise ValueError("reason cannot be empty")
        self.predicate = predicate
        self.stop_reason = reason

    def check(self, new_messages: Sequence[Message]) -> Optional[StopMessage]:
        """Invoke the predicate; terminate if it returns True."""
        if self.predicate(new_messages):
            return self._set_termination(
                self.stop_reason,
                {"predicate": repr(self.predicate)},
            )
        return None
