"""ShellSafetyService — application service for shell command safety checks."""

from __future__ import annotations

from collections.abc import Callable

from cleveragents.tui.shell_safety.danger_level import ShellDangerLevel
from cleveragents.tui.shell_safety.dangerous_pattern import DangerousPattern
from cleveragents.tui.shell_safety.pattern_detector import DangerousPatternDetector
from cleveragents.tui.shell_safety.warning import DangerousCommandWarning


class ShellSafetyService:
    """Application service that checks shell commands before execution.

    Wraps
    :class:`~cleveragents.tui.shell_safety.pattern_detector.DangerousPatternDetector`
    with a higher-level API suitable for use by the TUI and other callers.

    The service supports an optional *warn_callback* that is invoked whenever
    a dangerous command is detected.  The callback receives the
    :class:`~cleveragents.tui.shell_safety.warning.DangerousCommandWarning`
    and should return ``True`` to allow execution or ``False`` to block it.

    When no callback is provided, commands at or above *block_level* are
    blocked automatically.

    Usage::

        service = ShellSafetyService()
        result = service.check_command("rm -rf /")
        if not result.allowed:
            print(result.warning.message)

    Attributes:
        _detector: The underlying :class:`DangerousPatternDetector`.
        _block_level: Commands at or above this level are blocked when no
            callback is provided.
        _warn_callback: Optional callable invoked for dangerous commands.
    """

    def __init__(
        self,
        *,
        detector: DangerousPatternDetector | None = None,
        block_level: ShellDangerLevel = ShellDangerLevel.MEDIUM,
        warn_callback: Callable[[DangerousCommandWarning], bool] | None = None,
        extra_patterns: list[DangerousPattern] | None = None,
    ) -> None:
        """Initialise the service.

        Args:
            detector: Custom detector.  When ``None`` a default
                :class:`DangerousPatternDetector` is created.
            block_level: Minimum :class:`ShellDangerLevel` that triggers
                automatic blocking when no *warn_callback* is provided.
                Defaults to :attr:`~ShellDangerLevel.MEDIUM`.
            warn_callback: Optional callable ``(warning) -> bool``.  Return
                ``True`` to allow execution, ``False`` to block.
            extra_patterns: Additional patterns to register on top of the
                defaults.
        """
        self._detector = (
            detector if detector is not None else DangerousPatternDetector()
        )
        self._block_level = block_level
        self._warn_callback = warn_callback

        if extra_patterns:
            for pattern in extra_patterns:
                self._detector.add_pattern(pattern)

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    def check_command(self, command: str) -> SafetyCheckResult:
        """Check *command* and return a :class:`SafetyCheckResult`.

        The result indicates whether the command is allowed to proceed and
        includes any warning that was generated.
        """
        warning = self._detector.check_first(command)

        if warning is None:
            return SafetyCheckResult(command=command, warning=None, allowed=True)

        if self._warn_callback is not None:
            allowed = self._warn_callback(warning)
        else:
            allowed = warning.danger_level < self._block_level

        return SafetyCheckResult(command=command, warning=warning, allowed=allowed)

    def is_safe(self, command: str) -> bool:
        """Return ``True`` if *command* passes the safety check."""
        return self.check_command(command).allowed

    @property
    def detector(self) -> DangerousPatternDetector:
        """The underlying :class:`DangerousPatternDetector`."""
        return self._detector

    @property
    def block_level(self) -> ShellDangerLevel:
        """The minimum danger level that triggers automatic blocking."""
        return self._block_level


class SafetyCheckResult:
    """Result of a :meth:`ShellSafetyService.check_command` call.

    Attributes:
        command: The command that was checked.
        warning: The
            :class:`~cleveragents.tui.shell_safety.warning.DangerousCommandWarning`
            if a dangerous pattern was detected, otherwise ``None``.
        allowed: ``True`` if the command is allowed to proceed.
    """

    __slots__ = ("allowed", "command", "warning")

    def __init__(
        self,
        command: str,
        warning: DangerousCommandWarning | None,
        allowed: bool,
    ) -> None:
        self.command = command
        self.warning = warning
        self.allowed = allowed

    def __repr__(self) -> str:
        return (
            f"SafetyCheckResult(command={self.command!r}, "
            f"allowed={self.allowed}, "
            f"warning={self.warning!r})"
        )
