"""Command policy validation: denylist-based for v0, to be replaced with capability-based in v1."""

import re
from dataclasses import dataclass


class PolicyError(Exception):
    """Command violates policy."""
    pass


# Regex patterns for destructive commands
# These are matched case-insensitively against the command string.
#
# LIMITATIONS (documented in CLAUDE.md):
# - False positives: "grep chmod" blocked because "chmod" matches
# - False negatives: "python -c 'shutil.rmtree(...)'" not blocked
# - No protection against new tools, obfuscation, language interpreters
#
# This is a v0 expedient. v1 will replace with capability-based allowlist.

_DENY_PATTERNS = [
    # Deletion: rm, rmdir, del, rm -rf, etc.
    r"\brm\b",
    r"\brmdir\b",
    r"\bdel\b",

    # Disk: dd, mkfs, fdisk, parted, etc. (potential data loss)
    r"\bdd\b",
    r"\bmkfs\b",
    r"\bfdisk\b",
    r"\bparted\b",

    # System: reboot, shutdown, halt, poweroff, etc.
    r"\breboot\b",
    r"\bshutdown\b",
    r"\bhalt\b",
    r"\bpoweroff\b",

    # Permissions: chmod, chown, chgrp (change ownership/perms)
    r"\bchmod\b",
    r"\bchown\b",
    r"\bchgrp\b",

    # Users: useradd, userdel, passwd, etc.
    r"\buseradd\b",
    r"\buserdel\b",
    r"\busermod\b",
    r"\bpasswd\b",
    r"\bsudo\b",

    # Fork bomb pattern (a=(); a=(a "${a[@]}" a); a "${a[@]}")
    r":\(\)\s*\{\s*:\|\:&\s*\};\s*:",
]

_DENY_REGEX = re.compile("|".join(f"({p})" for p in _DENY_PATTERNS), re.IGNORECASE)


@dataclass
class Policy:
    """
    Command execution policy.

    Current implementation: Token-based denylist.
    Target: Capability-based allowlist (v1/v2).
    """

    read_only: bool = True  # If True, block write operations


def validate_command(cmd: str, policy: Policy) -> None:
    """
    Validate that command is allowed by policy.

    Args:
        cmd: Command string to execute
        policy: Policy object

    Raises:
        PolicyError: If command violates policy

    Guarantees:
    - If this function returns, command is allowed by current policy
    - If it raises, command is blocked

    Contingencies:
    - Blocking is based on regex pattern matching (not AST analysis)
    - Token-based approach has high false positive/negative rates (see CLAUDE.md)
    - Not a replacement for OS-level sandboxing or capability-based access control
    """
    if _DENY_REGEX.search(cmd):
        raise PolicyError(f"command blocked by denylist: {cmd}")

    # Read-only mode: block write-like operators
    if policy.read_only:
        # Block: >, >>, tee, sed -i, etc.
        if re.search(r"(>|>>|tee\b|sed\s+-i)", cmd, re.IGNORECASE):
            raise PolicyError(f"write operation blocked in read-only mode: {cmd}")
