#!/usr/bin/env python3
"""PreToolUse hook: блокирует запрещённые git/fs/docker команды, включая shell-обёртки.

Deny-префиксы в settings.json позиционны и обходятся обёртками
(`powershell -c "git push --force"` не матчится ни одним deny-паттерном).
Этот hook regex-сканирует ВСЮ строку команды Bash-тула, поэтому ловит и пейлоады
внутри powershell/pwsh/bash -c.

Ограничение (как у check-secret-read.py): видит только текст top-level команды —
опасная операция внутри вызываемого скрипта не детектится. Known-bypass (осознанно):
long-form `rm --recursive --force`, PowerShell-алиасы ri/rd/del, сокращения -Rec.
Fail-open по дизайну: это tripwire-слой поверх deny-правил и review, а не
единственная защита.

Протокол: exit 0 = allow, exit 2 + stderr = block (сообщение уходит Claude).
"""

import json
import re
import sys

# (pattern, объяснение). Все паттерны применяются к полной строке команды.
PATTERNS: list[tuple[str, str]] = [
    (r"git\s+(?:-C\s+\S+\s+)?push\b[^\n]*?\s(-f|--force(?:-with-lease|-if-includes)?)\b",
     "git push --force/-f/--force-with-lease запрещён без явного approval юзера"),
    (r"git\s+(?:-C\s+\S+\s+)?push\b[^\n]*?--no-verify",
     "git push --no-verify запрещён (pre-push hooks обязательны)"),
    (r"git\s+(?:-C\s+\S+\s+)?push\b[^\n]*\s\S+\s+(\S+:)?main(?![\w/-])",
     "прямой push в main запрещён — только ветка + PR (git-pr.md)"),
    (r"git\s+(?:-C\s+\S+\s+)?commit\b[^\n]*?--amend\b",
     "git commit --amend запрещён без явного approval юзера"),
    (r"git\s+(?:-C\s+\S+\s+)?commit\b[^\n]*?(--no-verify\b|\s-n\b)",
     "git commit --no-verify/-n запрещён (pre-commit hooks обязательны)"),
    (r"git\s+(?:-C\s+\S+\s+)?rebase\b[^\n]*?(\s-i\b|--interactive\b)",
     "git rebase -i/--interactive запрещён"),
    (r"git\s+(?:-C\s+\S+\s+)?reset\b[^\n]*?--hard\b",
     "git reset --hard запрещён (уничтожает локальные изменения)"),
    (r"git\s+(?:-C\s+\S+\s+)?clean\b[^\n]*?\s-[a-zA-Z]*f",
     "git clean -f* запрещён (удаляет untracked-файлы)"),
    (r"git\s+(?:-C\s+\S+\s+)?checkout\b[^\n]*?\s-f\b",
     "git checkout -f запрещён (теряет незакоммиченные изменения)"),
    (r"\brm\b(?=[^\n|;&]*\s-[a-zA-Z]*r)(?=[^\n|;&]*\s-[a-zA-Z]*f)",
     "rm -rf (и вариации -fr / -r -f) запрещён"),
    (r"(?i)remove-item\b(?=[^\n]*-recurse)(?=[^\n]*-force)",
     "Remove-Item -Recurse -Force запрещён"),
    (r"(?i)docker(?:-|\s+)compose\s+down\b[^\n]*(\s-v\b|--volumes\b)",
     "docker compose down -v удаляет volumes"),
    (r"docker\s+volume\s+rm\b",
     "docker volume rm запрещён"),
]


def strip_commit_messages(cmd: str) -> str:
    """Вырезает содержимое -m/--message аргументов git commit.

    Иначе false-positive: `git commit -m "запретили rm -rf"` блокируется из-за
    текста сообщения. Пейлоады обёрток (powershell -c "git push --force") не
    страдают — они не привязаны к -m.
    """
    return re.sub(
        r"(\s(?:-m|--message))\s+(\"(?:[^\"\\]|\\.)*\"|'[^']*')",
        r"\1 MSG",
        cmd,
    )


def main() -> int:
    try:
        data = json.load(sys.stdin)
        if data.get("tool_name") != "Bash":
            return 0
        cmd = (data.get("tool_input") or {}).get("command") or ""
        cmd = strip_commit_messages(cmd)
        for pattern, reason in PATTERNS:
            if re.search(pattern, cmd):
                print(
                    f"BLOCKED (check-dangerous-commands): {reason}. "
                    "Правило CLAUDE.md #5 / git-pr.md § Запреты. Если операция реально нужна — "
                    "попроси юзера выполнить её самому или дать явное разрешение; "
                    "не пытайся обойти через обёртки/скрипты.",
                    file=sys.stderr,
                )
                return 2
        return 0
    except Exception:
        return 0  # fail-open: tripwire-слой, не единственная защита


if __name__ == "__main__":
    sys.exit(main())
