"""Run published handlers only. Shell payloads are inert JSON input, never executed."""
import json
import os
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest

EXAMPLES = Path(__file__).resolve().parent


class PublishedPolicies(unittest.TestCase):
    def setUp(self):
        self.temp = tempfile.TemporaryDirectory()
        self.addCleanup(self.temp.cleanup)
        self.root = Path(self.temp.name).resolve()
        (self.root / "src").mkdir()

    def check_path(self, path, expected, **kwargs):
        event = {"hook_event_name": "PreToolUse", "tool_name": "Write",
                 "tool_input": {"file_path": str(path)}}
        event.update(kwargs)
        result = subprocess.run([sys.executable, str(EXAMPLES / "protect-files.py")],
                                input=json.dumps(event), text=True, capture_output=True,
                                env={**os.environ, "AGENT_WORKSPACE": str(self.root)})
        self.assertEqual(result.returncode, expected, (event, result.stderr))

    def test_source_edits_pass_without_creating_files(self):
        for path in ["src/app.py", "src/nested/new.ts", self.root / "src/app.py"]:
            with self.subTest(path=path):
                self.check_path(path, 0)
        self.assertFalse((self.root / "src/app.py").exists())

    def test_sensitive_and_out_of_scope_targets_are_denied(self):
        for path in [".env", "src/.env", "src/.example/.env", "src/.template/.env",
                     ".claude/settings.json", "src/CLAUDE.md", "src/AGENTS.md",
                     "src/wrangler.toml", "src/app.xcconfig", "src/GoogleService-Info.plist",
                     "src/../.env", "src/../../outside", "src-other/app.py", "/tmp/outside.py"]:
            with self.subTest(path=path):
                self.check_path(path, 2)

    def test_resolved_symlink_cannot_escape_source_tree(self):
        (self.root / "outside").mkdir()
        (self.root / "src/link").symlink_to(self.root / "outside", target_is_directory=True)
        self.check_path("src/link/file.py", 2)

    def test_src_symlink_cannot_escape_workspace(self):
        (self.root / "src").rmdir()
        (self.root / "src").symlink_to(self.root.parent, target_is_directory=True)
        self.check_path("src/file.py", 2)

    def test_wrong_event_tool_or_missing_path_is_denied(self):
        self.check_path("src/app.py", 2, hook_event_name="PostToolUse")
        self.check_path("src/app.py", 2, tool_name="Bash")
        self.check_path("", 2)
        self.check_path("src/app.py", 2, tool_input={})

    def test_malformed_json_and_missing_environment_are_denied(self):
        for payload in ["not json", "null", "[]", '{"tool_input":null}']:
            with self.subTest(payload=payload):
                result = subprocess.run([sys.executable, str(EXAMPLES / "protect-files.py")],
                                        input=payload, text=True, capture_output=True,
                                        env={**os.environ, "AGENT_WORKSPACE": str(self.root)})
                self.assertEqual(result.returncode, 2)
        env = dict(os.environ)
        env.pop("AGENT_WORKSPACE", None)
        result = subprocess.run([sys.executable, str(EXAMPLES / "protect-files.py")],
                                input=json.dumps({"hook_event_name": "PreToolUse", "tool_name": "Edit",
                                                  "tool_input": {"file_path": "src/app.py"}}),
                                text=True, capture_output=True, env=env)
        self.assertEqual(result.returncode, 2)

    def test_shell_gate_denies_all_commands_including_original_bypasses(self):
        marker = self.root / "must-not-exist"
        for command in ["rm -rf /tmp/cache /project", "rm -rf node_modules-important",
                        "git push origin -f", "git push --force", "echo harmless",
                        f"touch '{marker}'"]:
            with self.subTest(command=command):
                result = subprocess.run(["/bin/sh", str(EXAMPLES / "block-shell.sh")],
                                        input=json.dumps({"tool_input": {"command": command}}),
                                        text=True, capture_output=True)
                self.assertEqual(result.returncode, 2)
        self.assertFalse(marker.exists())

    def test_kiro_shell_action_has_nonzero_exit(self):
        result = subprocess.run(["/bin/sh", str(EXAMPLES / "kiro-audit-gate.sh")],
                                input="{}", text=True, capture_output=True)
        self.assertEqual(result.returncode, 1)
        self.assertIn("Blocked:", result.stderr)


if __name__ == "__main__":
    unittest.main()
