#!/usr/bin/env python3
"""Create a path-scoped local git checkpoint commit."""

from __future__ import annotations

import argparse
import subprocess
import sys
from pathlib import Path


def run_git(args: list[str], cwd: Path, *, check: bool = True) -> subprocess.CompletedProcess[str]:
    result = subprocess.run(
        ["git", *args],
        cwd=cwd,
        text=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        timeout=60,
    )
    if check and result.returncode != 0:
        message = result.stderr.strip() or result.stdout.strip()
        raise SystemExit(f"git {' '.join(args)} failed: {message}")
    return result


def repo_root() -> Path:
    result = subprocess.run(
        ["git", "rev-parse", "--show-toplevel"],
        text=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        timeout=60,
    )
    if result.returncode != 0:
        raise SystemExit("Not inside a git repository.")
    return Path(result.stdout.strip())


def load_paths(args: argparse.Namespace) -> list[str]:
    paths = list(args.paths)
    if args.paths_from_file:
        raw_lines = Path(args.paths_from_file).read_text(encoding="utf-8").splitlines()
        paths.extend(line.strip() for line in raw_lines if line.strip() and not line.startswith("#"))
    return paths


def ensure_clean_index(root: Path) -> None:
    staged = run_git(["diff", "--cached", "--name-only"], root).stdout.strip()
    if staged:
        raise SystemExit(
            "Refusing to continue because the index already has staged changes:\n"
            f"{staged}\n"
            "Commit or unstage them before running this checkpoint helper."
        )


def status_for(root: Path, paths: list[str], allow_all: bool) -> str:
    command = ["status", "--short"]
    if not allow_all:
        command.extend(["--", *paths])
    return run_git(command, root).stdout.strip()


def infer_message(paths: list[str], allow_all: bool) -> str:
    if allow_all or not paths:
        return "chore(checkpoint): backup current task changes"
    first_parts = [Path(item).parts[0] for item in paths if Path(item).parts]
    scope = first_parts[0] if first_parts else "task"
    return f"chore(checkpoint): backup {scope} changes"


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("paths", nargs="*", help="Task-owned paths to stage and commit.")
    parser.add_argument("-m", "--message", help="Commit message subject/body.")
    parser.add_argument("--paths-from-file", help="Read additional pathspecs from a UTF-8 text file.")
    parser.add_argument("--dry-run", action="store_true", help="Show matching changes without staging.")
    parser.add_argument("--allow-all", action="store_true", help="Allow committing all dirty files.")
    parser.add_argument("--no-verify", action="store_true", help="Pass --no-verify to git commit.")
    return parser.parse_args()


def main() -> int:
    args = parse_args()
    root = repo_root()
    paths = load_paths(args)

    if not paths and not args.allow_all:
        raise SystemExit("Pass explicit paths, or use --allow-all when the current task owns all changes.")

    current_status = status_for(root, paths, args.allow_all)
    if not current_status:
        print("No matching changes to commit.")
        return 0

    print(current_status)
    if args.dry_run:
        return 0

    ensure_clean_index(root)

    # 使用 -A 保留删除/重命名等变更，但只作用于明确传入的 pathspec。
    if args.allow_all:
        run_git(["add", "-A"], root)
    else:
        run_git(["add", "-A", "--", *paths], root)

    staged_summary = run_git(["diff", "--cached", "--name-status"], root).stdout.strip()
    if not staged_summary:
        raise SystemExit("No staged changes after git add.")

    message = args.message or infer_message(paths, args.allow_all)
    commit_command = ["commit"]
    if args.no_verify:
        commit_command.append("--no-verify")
    commit_command.extend(["-m", message])
    commit_result = run_git(commit_command, root)
    sys.stdout.write(commit_result.stdout)

    commit_hash = run_git(["rev-parse", "--short", "HEAD"], root).stdout.strip()
    print(f"checkpoint_commit={commit_hash}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
