#!/usr/bin/env python3
"""Codex PreToolUse 정책: 위험 명령과 테스트 없는 구현 파일 수정을 차단한다."""

import json
import re
import sys
from pathlib import Path
from typing import Any

SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(SCRIPTS_DIR) not in sys.path:
    sys.path.insert(0, str(SCRIPTS_DIR))

from msvc_harness.config import ConfigError, load_config
from msvc_harness.tdd_policy import CPP_SUFFIXES, evaluate_paths


DANGEROUS_PATTERNS = (
    re.compile(r"\bgit\s+reset\s+--hard\b", re.IGNORECASE),
    re.compile(r"\bgit\s+push\b[^\n]*--force(?:-with-lease)?\b", re.IGNORECASE),
    re.compile(r"\brm\s+-rf\b", re.IGNORECASE),
    re.compile(
        r"\bRemove-Item\b(?=[^\n]*-Recurse\b)(?=[^\n]*-Force\b)",
        re.IGNORECASE,
    ),
    re.compile(
        r"\b(?:rmdir|rd)\b(?=[^\n]*/s\b)(?=[^\n]*/q\b)",
        re.IGNORECASE,
    ),
    re.compile(r"\bDROP\s+TABLE\b", re.IGNORECASE),
)
PATCH_PATH = re.compile(
    r"^\*\*\* (?:(?:Add|Update|Delete) File:|Move to:) (?P<path>.+?)\s*$",
    re.MULTILINE,
)


def _tool_input(payload: dict[str, Any]) -> dict[str, Any]:
    value = payload.get("tool_input", {})
    return value if isinstance(value, dict) else {}


def _candidate_paths(payload: dict[str, Any], root: Path) -> list[Path]:
    tool_input = _tool_input(payload)
    raw_paths: list[str] = []
    for key in ("path", "file_path"):
        value = tool_input.get(key)
        if isinstance(value, str) and value.strip():
            raw_paths.append(value.strip())

    edits = tool_input.get("edits")
    if isinstance(edits, list):
        for edit in edits:
            if not isinstance(edit, dict):
                continue
            value = edit.get("path")
            if isinstance(value, str) and value.strip():
                raw_paths.append(value.strip())

    for key in ("patch", "input"):
        value = tool_input.get(key)
        if isinstance(value, str):
            raw_paths.extend(match.group("path") for match in PATCH_PATH.finditer(value))

    paths: list[Path] = []
    for raw_path in raw_paths:
        path = Path(raw_path)
        paths.append(path.resolve() if path.is_absolute() else (root / path).resolve())
    return list(dict.fromkeys(paths))


def evaluate(payload: dict[str, Any], root: Path) -> str | None:
    """Return a blocking reason, or None when the tool call is allowed."""
    tool_name = str(payload.get("tool_name", ""))
    tool_input = _tool_input(payload)

    if tool_name in {"Bash", "shell_command", "PowerShell"}:
        command = tool_input.get("command", "")
        if isinstance(command, str) and any(
            pattern.search(command) for pattern in DANGEROUS_PATTERNS
        ):
            return "위험한 명령어가 감지되어 실행을 차단했습니다."

    if tool_name not in {"apply_patch", "Edit", "MultiEdit", "Write"}:
        return None

    paths = _candidate_paths(payload, root)
    try:
        config = load_config(root)
    except ConfigError as exc:
        if any(path.suffix.lower() in CPP_SUFFIXES for path in paths):
            return f"TDD GUARD: .harness/config.json must be repaired: {exc}"
        return None
    return evaluate_paths(paths, root, config.tdd)


def main() -> int:
    try:
        payload = json.load(sys.stdin)
        if not isinstance(payload, dict):
            raise TypeError("hook input must be a JSON object")
        cwd = payload.get("cwd")
        if cwd is not None and not isinstance(cwd, str):
            raise TypeError("hook cwd must be a string")
    except (json.JSONDecodeError, TypeError) as exc:
        print(f"TDD GUARD: hook 입력을 해석하지 못해 검사를 건너뜁니다: {exc}", file=sys.stderr)
        return 0

    root = Path(payload.get("cwd") or Path.cwd()).resolve()
    reason = evaluate(payload, root)
    if reason:
        print(reason, file=sys.stderr)
        return 2
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
