"""DangerousPatternDetector — domain service for shell danger detection."""

from __future__ import annotations

from cleveragents.tui.shell_safety.danger_level import ShellDangerLevel
from cleveragents.tui.shell_safety.dangerous_pattern import DangerousPattern
from cleveragents.tui.shell_safety.pattern_registry import DEFAULT_PATTERNS
from cleveragents.tui.shell_safety.warning import DangerousCommandWarning


class DangerousPatternDetector:
    """Checks shell commands against a configurable registry of dangerous patterns.

    The detector ships with a set of built-in
    :data:`~cleveragents.tui.shell_safety.pattern_registry.DEFAULT_PATTERNS`
    but callers can supply a custom registry, add patterns at runtime, or
    replace the registry entirely.

    Usage::

        detector = DangerousPatternDetector()
        warnings = detector.check("rm -rf /")
        if warnings:
            print(warnings[0].message)

    Attributes:
        _patterns: Ordered list of :class:`DangerousPattern` objects that
            are checked against each command.
    """

    def __init__(
        self,
        patterns: list[DangerousPattern] | None = None,
    ) -> None:
        """Initialise the detector.

        Args:
            patterns: Custom pattern list.  When ``None`` the built-in
                :data:`DEFAULT_PATTERNS` are used.
        """
        self._patterns: list[DangerousPattern] = (
            list(patterns) if patterns is not None else list(DEFAULT_PATTERNS)
        )

    # ------------------------------------------------------------------
    # Registry management
    # ------------------------------------------------------------------

    def add_pattern(self, pattern: DangerousPattern) -> None:
        """Append *pattern* to the registry.

        Patterns are checked in insertion order; the first match wins when
        :meth:`check_first` is used.
        """
        self._patterns.append(pattern)

    def remove_pattern(self, name: str) -> bool:
        """Remove the pattern with the given *name* from the registry.

        Returns:
            ``True`` if a pattern was removed, ``False`` if no pattern with
            that name existed.
        """
        before = len(self._patterns)
        self._patterns = [p for p in self._patterns if p.name != name]
        return len(self._patterns) < before

    def replace_patterns(self, patterns: list[DangerousPattern]) -> None:
        """Replace the entire pattern registry with *patterns*."""
        self._patterns = list(patterns)

    @property
    def patterns(self) -> tuple[DangerousPattern, ...]:
        """Read-only view of the current pattern registry."""
        return tuple(self._patterns)

    # ------------------------------------------------------------------
    # Detection
    # ------------------------------------------------------------------

    def check(self, command: str) -> list[DangerousCommandWarning]:
        """Return all warnings for *command* (may be empty).

        All matching patterns are returned, ordered by insertion order.
        """
        return [
            DangerousCommandWarning.from_pattern(command, pattern)
            for pattern in self._patterns
            if pattern.matches(command)
        ]

    def check_first(self, command: str) -> DangerousCommandWarning | None:
        """Return the first (highest-priority) warning for *command*, or ``None``."""
        for pattern in self._patterns:
            if pattern.matches(command):
                return DangerousCommandWarning.from_pattern(command, pattern)
        return None

    def is_dangerous(self, command: str) -> bool:
        """Return ``True`` if *command* matches any registered pattern."""
        return any(p.matches(command) for p in self._patterns)

    def max_danger_level(self, command: str) -> ShellDangerLevel | None:
        """Return the highest :class:`ShellDangerLevel` matched, or ``None``."""
        levels = [p.level for p in self._patterns if p.matches(command)]
        return max(levels) if levels else None
