import json
import os
import pathlib
import stat
import subprocess
import tempfile
import unittest


ROOT = pathlib.Path(__file__).resolve().parents[1]
HOOK = ROOT / "scripts/codex-validate-stop"
DISPATCH = ROOT / "scripts/codex-stop-dispatch"


def write_command(path, name, exit_code=0, stdout=""):
    command = path / name
    command.write_text(
        "#!/usr/bin/env bash\n"
        f'printf \'{name} %s\\n\' "$*" >>"$CODEX_STOP_LOG"\n'
        f"printf '%s' {stdout!r}\n"
        f"exit {exit_code}\n"
    )
    command.chmod(command.stat().st_mode | stat.S_IXUSR)
    return command


def run_hook(checker_exit=0, checker_output="", stop_hook_active=False):
    directory = tempfile.TemporaryDirectory()
    temporary = pathlib.Path(directory.name)
    log = temporary / "commands.log"
    checker = write_command(temporary, "checker", checker_exit, checker_output)
    env = {
        **os.environ,
        "CODEX_STOP_CHECKER": str(checker),
        "CODEX_STOP_LOG": str(log),
    }
    result = subprocess.run(
        ["bash", str(HOOK)],
        cwd=ROOT,
        env=env,
        input=json.dumps(
            {
                "cwd": str(ROOT),
                "hook_event_name": "Stop",
                "stop_hook_active": stop_hook_active,
            }
        ),
        capture_output=True,
        text=True,
    )
    commands = log.read_text().splitlines() if log.exists() else []
    directory.cleanup()
    return result, commands


class CodexStopHookTests(unittest.TestCase):
    def test_repository_stop_hook_runs_validation_script(self):
        config = json.loads((ROOT / ".codex/hooks.json").read_text())
        hook = config["hooks"]["Stop"][0]["hooks"][0]

        self.assertEqual(hook["type"], "command")
        self.assertIn("scripts/codex-stop-dispatch", hook["command"])
        self.assertIn("scripts/codex-validate-stop", hook["command"])

    def test_dispatcher_stops_forced_continuation_before_worktree_hook(self):
        result = subprocess.run(
            ["bash", str(DISPATCH), "scripts/codex-validate-stop"],
            cwd=ROOT,
            input=json.dumps(
                {
                    "cwd": str(ROOT),
                    "hook_event_name": "Stop",
                    "stop_hook_active": True,
                }
            ),
            capture_output=True,
            text=True,
        )

        self.assertEqual(result.returncode, 0)
        self.assertEqual(result.stdout, "")
        self.assertEqual(result.stderr, "")

    def test_stop_hook_runs_completion_checker(self):
        result, commands = run_hook()

        self.assertEqual(result.returncode, 0)
        self.assertEqual(result.stdout, "")
        self.assertEqual(result.stderr, "")
        self.assertEqual(commands, ["checker "])

    def test_stop_hook_blocks_when_regressions_fail(self):
        reason = "Dotfiles regression tests failed; fix them before stopping."
        result, commands = run_hook(checker_exit=1, checker_output=reason)

        self.assertEqual(result.returncode, 0)
        self.assertEqual(result.stderr, "")
        self.assertEqual(
            json.loads(result.stdout),
            {
                "decision": "block",
                "reason": reason,
            },
        )
        self.assertEqual(commands, ["checker "])

    def test_stop_hook_blocks_when_hey_check_fails(self):
        reason = "hey check failed; fix it before stopping."
        result, commands = run_hook(checker_exit=1, checker_output=reason)

        self.assertEqual(result.returncode, 0)
        self.assertEqual(result.stderr, "")
        self.assertEqual(
            json.loads(result.stdout),
            {
                "decision": "block",
                "reason": reason,
            },
        )
        self.assertEqual(commands, ["checker "])

    def test_forced_continuation_does_not_block_or_rerun_checks(self):
        result, commands = run_hook(checker_exit=1, stop_hook_active=True)

        self.assertEqual(result.returncode, 0)
        self.assertEqual(result.stdout, "")
        self.assertEqual(result.stderr, "")
        self.assertEqual(commands, [])


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