#!/usr/bin/env python3
"""sidecar-prewrite-guard — PreToolUse deny on hand-edits to GENERATED artifacts.

The framework's generated files (a2ui sidecars, the trait catalog, the
component reference) are build outputs of the yaml/source SoT. Hand-editing
them creates drift the verify gates then hard-fail on. This guard converts
the former AGENTS.md prose rule ("never hand-edit generated registries")
into enforcement: it denies Write/Edit on the generated paths with a repair
message naming the real source + rebuild command.

Deliberately narrow: only the three generated classes below. Bash-driven
regeneration (npm run components / build:traits) is unaffected — this guard
watches the Write/Edit tools only.

Usage:
  sidecar-prewrite-guard --hook       # PreToolUse mode: event JSON on stdin
  sidecar-prewrite-guard selftest     # prove the matcher on embedded fixtures
"""
import json
import sys

GENERATED = (
    # (predicate description, matcher fn, source-of-truth + rebuild command)
    (
        "trait catalog",
        lambda p: p.endswith("traits/_catalog.json"),
        "edit the trait source files and run `npm run build:traits`",
    ),
    (
        "a2ui sidecar",
        lambda p: p.endswith(".a2ui.json"),
        "edit the component's `<name>.yaml` SoT and run `npm run components`",
    ),
    (
        "generated component reference",
        lambda p: "/.claude/docs/reference/components/" in p
        or p.startswith(".claude/docs/reference/components/"),
        "edit the component yaml/source and run `npm run docs:reference`",
    ),
    (
        "patterns & templates index",
        lambda p: p.endswith("site/patterns-index.json")
        or p.endswith("site/pages/patterns/index.html")
        or p.endswith("skills/pattern-catalog/references/pattern-index.md"),
        "edit annotations.yaml (or site/sitemap.json) and run `npm run build:patterns-index`",
    ),
)


def classify(path):
    for label, match, fix in GENERATED:
        if match(path):
            return label, fix
    return None, None


def hook_mode():
    try:
        event = json.load(sys.stdin)
    except Exception:
        return 0  # malformed event: stay quiet
    tool_input = event.get("tool_input") or {}
    path = tool_input.get("file_path") or ""
    label, fix = classify(path)
    if not label:
        return 0
    reason = (
        f"sidecar-prewrite-guard · {path} is a GENERATED {label} — "
        f"hand-edits are overwritten and fail the drift gates. Instead: {fix}. "
        "If this block is wrong for this file, report it against "
        ".claude/docs/specs/plugin-estate-v2.md — do not hand-edit the artifact."
    )
    print(
        json.dumps(
            {
                "hookSpecificOutput": {
                    "hookEventName": "PreToolUse",
                    "permissionDecision": "deny",
                    "permissionDecisionReason": reason,
                }
            }
        )
    )
    return 0


def selftest():
    cases = [
        ("packages/web-components/traits/_catalog.json", True),
        ("packages/web-components/components/badge/badge.a2ui.json", True),
        (".claude/docs/reference/components/table.md", True),
        ("site/patterns-index.json", True),
        ("site/pages/patterns/index.html", True),
        (
            "packages/plugins/adia-ui-factory/skills/pattern-catalog/references/pattern-index.md",
            True,
        ),
        ("packages/web-components/components/badge/badge.yaml", False),
        ("packages/web-components/components/badge/badge.css", False),
        (
            "packages/plugins/adia-ui-factory/skills/pattern-catalog/references/annotations.yaml",
            False,
        ),
        ("apps/tasks/app/index.html", False),
    ]
    for path, should_block in cases:
        blocked = classify(path)[0] is not None
        if blocked != should_block:
            print(f"selftest: FAIL {path} blocked={blocked} expected={should_block}")
            return 1
    print("selftest: PASS")
    return 0


if __name__ == "__main__":
    if "selftest" in sys.argv:
        sys.exit(selftest())
    if "--hook" in sys.argv:
        sys.exit(hook_mode())
    print(__doc__)
    sys.exit(0)
