#!/usr/bin/env python3
"""PreToolUse guard for Bash: consult the hakodesh Bash wards (קוסטודעס).

Policy (0.5.0):
  ward matched + locked        -> exit 2  (block; the ward's purpose on stderr)
  unknown/malformed/empty ward -> exit 2  (ward.check is fail-closed; the kernel's cause on stderr)
  ward.check raised            -> exit 2  (fail closed; error on stderr)
  payload not a JSON object    -> exit 2  (fail closed: a crash used to exit 1, which Claude Code
                                           treats as non-blocking, so a malformed payload was a bypass)
  hakodesh not importable      -> exit 1  (allow, but loud: wards are NOT enforced)
The interpreter is pinned to the venv that has hakodesh installed so PATH differences between
the hook environment and an interactive shell cannot silently disable enforcement.
The wards are named by their dialect tokens; the kernel resolves either spelling.
"""
# Interpreter: the installer (devra קונסטיטוי-האמי) rewrites this shebang to the absolute
# path of the interpreter it is itself running under, which is the one that has hakodesh
# importable. That pin is the point -- a PATH difference between the hook environment and
# an interactive shell must not be able to disable enforcement silently. The portable form
# is what lives in the repository, because an absolute home path may never ship.
import json
import os
import sys
import time
from pathlib import Path

WARDS = ["קוסטוס-באש-גרעף", "קוסטוס-באש-קאט", "קוסטוס-באש-פינד"]

if sys.stdin.isatty():
    sys.exit(0)

job_dir = os.environ.get("CLAUDE_JOB_DIR", "/tmp")
payload_log = Path(job_dir) / "tmp" / "hook_payload.jsonl"


def log(record):
    try:
        payload_log.parent.mkdir(parents=True, exist_ok=True)
        with open(payload_log, "a", encoding="utf-8") as fh:
            fh.write(json.dumps(record, ensure_ascii=False) + "\n")
    except Exception:
        pass


try:
    hook_input = json.load(sys.stdin)
except Exception as exc:
    log({"ward_enforcement": "MALFORMED", "reason": "stdin is not JSON: %s" % exc})
    print("הקודש-קוסטודעס: מטען ההוק אינו JSON; דוחה (סגור-בכשל)", file=sys.stderr)
    sys.exit(2)
if not isinstance(hook_input, dict) or not isinstance(hook_input.get("tool_input", {}), dict):
    log({"ward_enforcement": "MALFORMED", "reason": "payload is not an object"})
    print("הקודש-קוסטודעס: מטען ההוק אינו אובייקט; דוחה (סגור-בכשל)", file=sys.stderr)
    sys.exit(2)

log({"bash": hook_input})

try:
    from hakodesh import ward, home as paths, dialectus
except Exception as exc:  # infrastructure absent: allow, but never silently
    log({"ward_enforcement": "DISABLED", "reason": "import failed: %s" % exc,
         "python": sys.executable, "ts": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())})
    print("הקודש-קוסטודעס: לא נאכף (אי אפשר לייבא hakodesh מ-%s: %s)" % (sys.executable, exc), file=sys.stderr)
    sys.exit(1)

try:
    ward_dir = paths.book_dir("ward")  # honours HAKODESH_HOME
except Exception:
    ward_dir = Path.home() / "Library/Application Support/hakodesh/book/wards"

cmd = (hook_input.get("tool_input") or {}).get("command", "")
cmd = "" if cmd is None else str(cmd)
for ward_name in WARDS:
    try:
        result = ward.check(ward_name, {"command": cmd})
    except Exception as exc:
        log({"ward_enforcement": "ERROR", "ward": ward_name, "error": str(exc)})
        print("הקודש-קוסטודעס: הבדיקה של %s נכשלה (%s); דוחה (סגור-בכשל)" % (ward_name, exc), file=sys.stderr)
        sys.exit(2)
    if not isinstance(result, dict) or result.get("denied"):
        msg = result.get("error") if isinstance(result, dict) else None
        # the purpose text is the redirect for a real matcher hit; any other denial (unknown,
        # malformed, empty matcher) keeps the kernel's message, which names the cause.
        # canon_enum_v2 (0.7.0): the dialect spelling only. err() has emitted the dialect form
        # since 0.6.1, and hooks now ship with the kernel, so there is no English code to read.
        # The whole lookup is guarded: it decides only WHICH message to print, and a failure
        # here must never become a traceback -- that exits 1, which Claude Code treats as
        # non-blocking, turning a deny into a bypass (RT3-08).
        try:
            if isinstance(result, dict) and dialectus.canon_enum_v2(result.get("error_code"), dialectus.ERROR_CODES) in (None, "ward.denied"):
                purpose = json.loads((ward_dir / ("%s.json" % ward_name)).read_text(encoding="utf-8-sig")).get("purpose")
                if purpose:
                    msg = purpose
        except Exception:
            pass
        print(msg or "נדחה על ידי קוסטוס %s" % ward_name, file=sys.stderr)
        sys.exit(2)
sys.exit(0)
