#!/usr/bin/env python
"""PRD Plugin pre-commit gate (PreToolUse hook) for Claude Code.

Wired to PreToolUse[Bash] in .claude/settings.json. When a `git commit` is about
to run, it runs `prd_gate` against the repo and **blocks the commit** (exit 2,
findings on stderr) if the gate reports any error-severity finding (duplicate
IDs, version-marker drift, implemented-without-graduated_to, stranded outbox,
state inconsistency). This is the hard lever: it doesn't force a skill to be
called, it walls off the commit until the workflow's outputs are clean.

Opt-in and fail-open by design:
- Only active when `automation.precommit_gate` is true in config (default false,
  so installing it changes nothing until a repo opts in).
- Only inspects `git commit` commands; everything else passes untouched.
- Any error, a missing gate, or warnings-only resolves to ALLOW — it must never
  trap the user out of committing because of a hook bug.
"""

import json
import re
import sys
from pathlib import Path

# A git-commit invocation within one command segment: `git ... commit`, but not
# `git config commit.gpgsign ...` (commit.) and not across a |/&/; separator.
GIT_COMMIT_RE = re.compile(r"\bgit\b[^|&;\n]*\bcommit\b(?!\.)")


def is_git_commit(command):
    return bool(command) and bool(GIT_COMMIT_RE.search(command))


def precommit_enabled(root):
    p = Path(root) / ".prd_plugin" / "config.json"
    if not p.is_file():
        return False
    try:
        cfg = json.loads(p.read_text(encoding="utf-8-sig"))
        return bool(cfg.get("automation", {}).get("precommit_gate", False))
    except Exception:
        return False


def _find_gate_dir(root):
    for cand in (Path(root) / ".prd_plugin" / "scripts", Path(root) / "scripts"):
        if (cand / "prd_gate.py").is_file():
            return cand
    return None


def blocking_findings(report):
    """Error-severity findings only — warnings (e.g. timescales) don't block."""
    return [f for f in report.get("findings", []) if f.get("severity") == "error"]


def main():
    try:
        raw = sys.stdin.read()
        payload = json.loads(raw) if raw.strip() else {}
    except Exception:
        payload = {}
    if not isinstance(payload, dict):
        payload = {}

    root = payload.get("cwd") or str(Path.cwd())
    tool_input = payload.get("tool_input")
    command = tool_input.get("command", "") if isinstance(tool_input, dict) else ""

    if not is_git_commit(command) or not precommit_enabled(root):
        return 0

    gate_dir = _find_gate_dir(root)
    if gate_dir is None:
        return 0
    try:
        sys.path.insert(0, str(gate_dir))
        import prd_gate
        report = prd_gate.run_checks(root)
    except Exception:
        return 0  # fail open — never block on a hook bug

    findings = blocking_findings(report)
    if not findings:
        return 0

    lines = ["PRD Plugin commit gate blocked this commit — fix these first:"]
    for f in findings:
        lines.append(f"- [{f.get('check')}] {f.get('summary')}"
                     + (f" -> {f.get('required_action')}" if f.get('required_action') else ""))
    lines.append("Fix these with the MCP state tools where possible (prd_next_id for IDs, "
                 "prd_open_goal/prd_file_request/prd_record_evidence for records) instead of "
                 "hand-editing JSON, then re-run the gate: python scripts/prd_gate.py check "
                 "(downstream: python .prd_plugin/scripts/prd_gate.py check).")
    sys.stderr.write("\n".join(lines) + "\n")
    return 2


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