"""Stop hook: run the fast data tests before Claude finishes, if data code has changed.

Skips when nothing under WATCHED differs from the last commit, or from the last passing
run in this session. On a failure it exits 2, which keeps Claude working and shows it
the output. If the tests still fail on the next stop (stop_hook_active is true), it
lets the turn end and warns you instead of looping.
"""

import hashlib
import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path

WATCHED = ("sql", "pipeline", "tests")
TEST_COMMAND = ["uv", "run", "pytest", "-q", "-x", "tests/data"]
TIMEOUT_SECONDS = 240
MAX_LINES = 40


def git(project: Path, *args: str) -> str:
    return subprocess.run(
        ["git", *args], cwd=project, capture_output=True, text=True, check=True
    ).stdout


def watched_state(project: Path) -> str:
    """A hash of uncommitted changes under WATCHED, or '' when there are none."""
    diff = git(project, "diff", "HEAD", "--", *WATCHED)
    untracked = git(project, "ls-files", "--others", "--exclude-standard", "--", *WATCHED)
    if not diff and not untracked:
        return ""
    digest = hashlib.sha256(diff.encode())
    for name in untracked.splitlines():
        digest.update(name.encode() + (project / name).read_bytes())
    return digest.hexdigest()


def run_tests(project: Path) -> tuple[bool, str]:
    try:
        result = subprocess.run(
            TEST_COMMAND, cwd=project, capture_output=True, text=True, timeout=TIMEOUT_SECONDS
        )
    except subprocess.TimeoutExpired:
        return False, f"The data tests took longer than {TIMEOUT_SECONDS} seconds."
    tail = (result.stdout + result.stderr).strip().splitlines()[-MAX_LINES:]
    return result.returncode == 0, "\n".join(tail)


def main() -> int:
    event = json.load(sys.stdin)
    project = Path(os.environ.get("CLAUDE_PROJECT_DIR", event.get("cwd", ".")))
    marker = Path(tempfile.gettempdir()) / f"data-tests-{event.get('session_id', 'none')}"
    state = watched_state(project)
    if not state or (marker.exists() and marker.read_text() == state):
        return 0
    passed, output = run_tests(project)
    if passed:
        marker.write_text(state)
        return 0
    if event.get("stop_hook_active"):
        print(json.dumps({"systemMessage": f"Data tests still failing:\n{output}"}))
        return 0
    print(f"Data tests failed. Fix them before finishing:\n{output}", file=sys.stderr)
    return 2


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