"""Thin wrappers around the git CLI, shared by the worktree lifecycle,
trial capture, and files_touched/present/absent assertions.

Subprocess, not a git binding library: one less dependency, and every
operation here is a command a user could paste into a terminal.
"""

from __future__ import annotations

import subprocess
from pathlib import Path


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


def diff_name_only(worktree: Path, base_commit: str) -> list[str]:
    """Files changed in the worktree relative to base_commit, including untracked."""
    tracked = run_git(["diff", "--name-only", base_commit], cwd=worktree).stdout.splitlines()
    untracked = run_git(
        ["ls-files", "--others", "--exclude-standard"], cwd=worktree
    ).stdout.splitlines()
    return sorted(set(tracked) | set(untracked))


def diff_text(worktree: Path, base_commit: str) -> str:
    """Unified diff against base_commit, including untracked files (as an /dev/null diff)."""
    tracked = run_git(["diff", base_commit], cwd=worktree).stdout
    untracked_files = run_git(
        ["ls-files", "--others", "--exclude-standard"], cwd=worktree
    ).stdout.splitlines()
    if not untracked_files:
        return tracked
    untracked = run_git(["diff", "--no-index", "/dev/null", *untracked_files], cwd=worktree, check=False)
    return tracked + untracked.stdout


def current_commit(repo: Path) -> str:
    return run_git(["rev-parse", "HEAD"], cwd=repo).stdout.strip()


def worktree_add(repo: Path, worktree_path: Path, base_commit: str) -> None:
    run_git(["worktree", "add", "--detach", str(worktree_path), base_commit], cwd=repo)


def worktree_remove(repo: Path, worktree_path: Path, force: bool = True) -> None:
    args = ["worktree", "remove"]
    if force:
        args.append("--force")
    args.append(str(worktree_path))
    run_git(args, cwd=repo, check=False)
