"""Git repository context capture for debug snapshots."""

from __future__ import annotations

import subprocess
from typing import Any


class _GitError:
    """Sentinel to distinguish 'command failed' from 'not applicable'."""

    __slots__ = ("reason",)

    def __init__(self, reason: str) -> None:
        self.reason = reason


def _run_git_command(
    args: list[str],
    timeout: float = 2.0,
    repo_root: str | None = None,
) -> str | _GitError | None:
    """Run a git command with timeout.

    Returns:
        stdout string on success, _GitError on failure, None is never returned
        (kept for backward compat type but all failures are _GitError).
    """
    cmd = ["git"]
    if repo_root:
        cmd.extend(["-C", repo_root])
    cmd.extend(args)
    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            timeout=timeout,
            check=False,
        )
        if result.returncode == 0:
            return result.stdout.strip()
        return _GitError(f"exit code {result.returncode}")
    except subprocess.TimeoutExpired:
        return _GitError("timeout")
    except FileNotFoundError:
        return _GitError("git not found")
    except OSError as e:
        return _GitError(f"OSError: {e}")


def get_git_context() -> dict[str, Any] | None:
    """Capture git repository context.

    Returns:
        Dict with commit, branch, dirty status, or None if not in a git repo.

    Example output:
        {
            "commit": "abc123",
            "commit_full": "abc123def456...",
            "branch": "main",
            "dirty": True
        }
    """
    repo_root_result = _run_git_command(["rev-parse", "--show-toplevel"])
    if isinstance(repo_root_result, _GitError):
        # Not a git repo, or git not available
        return None
    repo_root: str = repo_root_result  # type: ignore[assignment]

    context: dict[str, Any] = {}
    _warnings: list[str] = []

    # Get commit hash (short and full)
    commit_short = _run_git_command(["rev-parse", "--short", "HEAD"], repo_root=repo_root)
    if isinstance(commit_short, str):
        context["commit"] = commit_short
    elif isinstance(commit_short, _GitError):
        _warnings.append(f"commit hash: {commit_short.reason}")

    commit_full = _run_git_command(["rev-parse", "HEAD"], repo_root=repo_root)
    if isinstance(commit_full, str):
        context["commit_full"] = commit_full

    # Get branch name
    branch = _run_git_command(["rev-parse", "--abbrev-ref", "HEAD"], repo_root=repo_root)
    if isinstance(branch, str):
        context["branch"] = branch
        # Check for detached HEAD
        if branch == "HEAD":
            context["detached"] = True

    # Check if working directory is dirty
    status = _run_git_command(["status", "--porcelain"], repo_root=repo_root)
    if isinstance(status, str):
        context["dirty"] = len(status) > 0

    if _warnings:
        context["_git_warnings"] = _warnings

    return context if context else None
