"""Safety module - deny/ask/allow rules for bash commands.

Rules evaluated in order: deny > allow > ask (default).
"""

from __future__ import annotations

import re
from enum import Enum
from functools import lru_cache
from rich.console import Console

console = Console()


class Action(Enum):
    ALLOW = "allow"
    DENY = "deny"
    ASK = "ask"


@lru_cache(maxsize=128)
def _compile_deny_pattern(pattern: str) -> re.Pattern:
    """Compile a deny pattern to regex with word boundary support.

    Patterns starting with special chars (like '>') use substring match.
    Others use word boundary matching to avoid false positives.
    """
    pattern_lower = pattern.lower()
    # Special shell characters should match as substring (e.g., "> /dev/sda")
    if pattern_lower[0] in '>|&;$`':
        return re.compile(re.escape(pattern_lower))
    # For command-like patterns, use word boundary at start
    # This prevents "dd if=" from matching "add if="
    return re.compile(r'(?:^|[;&|`$\s])' + re.escape(pattern_lower))


def check_command(
    command: str,
    deny_patterns: list[str],
    allow_commands: list[str],
    ask_by_default: bool = True,
) -> Action:
    """Evaluate a bash command against safety rules.

    Order: deny > allow > ask_by_default.

    Deny patterns use word-boundary matching to reduce false positives.
    For example, "dd if=" won't match "add if=something".
    """
    cmd_lower = command.strip().lower()

    if not cmd_lower:
        return Action.ASK if ask_by_default else Action.ALLOW

    # 1. deny patterns - block dangerous commands (word boundary aware)
    for pattern in deny_patterns:
        regex = _compile_deny_pattern(pattern)
        if regex.search(cmd_lower):
            return Action.DENY

    # 2. allow list - auto-approve safe commands (word boundary check)
    for allowed in allow_commands:
        al = allowed.lower()
        if cmd_lower == al or cmd_lower.startswith(al + " ") or cmd_lower.startswith(al + "\t"):
            return Action.ALLOW

    # 3. default
    return Action.ASK if ask_by_default else Action.ALLOW


def prompt_user(command: str) -> bool:
    """Ask the user for approval to run a command.

    In non-interactive mode (stdin not a tty), defaults to allow.
    Deny patterns are still enforced by check_command() before this is called.
    """
    import sys
    if not sys.stdin.isatty():
        return True
    console.print(f"\n[yellow]bash:[/] [bold]{command}[/]")
    try:
        answer = console.input("[yellow]allow? [Y/n] [/]").strip().lower()
        return answer in ("", "y", "yes")  # Enter defaults to yes
    except (EOFError, KeyboardInterrupt):
        console.print()
        return False
