"""Tests for git context capture."""

import os
import subprocess

import llmdebug.git_context as git_context_module
from llmdebug.git_context import _GitError, get_enhanced_git_context, get_git_context


def test_git_context_in_repo():
    """Test git context capture when in a git repository."""
    # This test runs from the llmdebug repo itself
    context = get_git_context()

    # Should have context since we're in a git repo
    assert context is not None
    assert "commit" in context
    assert "commit_full" in context
    assert "branch" in context
    assert "dirty" in context

    # Commit should be a short hash (7+ chars)
    assert len(context["commit"]) >= 7
    # Full commit should be 40 chars
    assert len(context["commit_full"]) == 40


def test_git_context_not_in_repo(tmp_path, monkeypatch):
    """Test git context capture when not in a git repository."""
    # Change to a non-git directory
    monkeypatch.chdir(tmp_path)

    context = get_git_context()

    # Should return None when not in a git repo
    assert context is None


def test_git_context_dirty_status(tmp_path):
    """Test that dirty status is correctly detected."""
    # Initialize a new git repo
    subprocess.run(["git", "init"], cwd=tmp_path, capture_output=True, check=True)
    subprocess.run(
        ["git", "config", "user.email", "test@test.com"],
        cwd=tmp_path,
        capture_output=True,
        check=True,
    )
    subprocess.run(
        ["git", "config", "user.name", "Test"],
        cwd=tmp_path,
        capture_output=True,
        check=True,
    )

    # Create and commit a file
    test_file = tmp_path / "test.txt"
    test_file.write_text("content")
    subprocess.run(["git", "add", "."], cwd=tmp_path, capture_output=True, check=True)
    subprocess.run(
        ["git", "commit", "-m", "Initial commit"],
        cwd=tmp_path,
        capture_output=True,
        check=True,
    )

    # Save current dir and change to temp repo
    old_cwd = os.getcwd()
    try:
        os.chdir(tmp_path)

        # Should be clean
        context = get_git_context()
        assert context is not None
        assert context["dirty"] is False

        # Make it dirty
        test_file.write_text("modified")
        context = get_git_context()
        assert context is not None
        assert context["dirty"] is True
    finally:
        os.chdir(old_cwd)


def test_git_context_detached_head(tmp_path):
    """Test git context when HEAD is detached."""
    # Initialize a new git repo
    subprocess.run(["git", "init"], cwd=tmp_path, capture_output=True, check=True)
    subprocess.run(
        ["git", "config", "user.email", "test@test.com"],
        cwd=tmp_path,
        capture_output=True,
        check=True,
    )
    subprocess.run(
        ["git", "config", "user.name", "Test"],
        cwd=tmp_path,
        capture_output=True,
        check=True,
    )

    # Create and commit a file
    test_file = tmp_path / "test.txt"
    test_file.write_text("content")
    subprocess.run(["git", "add", "."], cwd=tmp_path, capture_output=True, check=True)
    subprocess.run(
        ["git", "commit", "-m", "Initial commit"],
        cwd=tmp_path,
        capture_output=True,
        check=True,
    )

    # Detach HEAD
    result = subprocess.run(
        ["git", "rev-parse", "HEAD"],
        cwd=tmp_path,
        capture_output=True,
        text=True,
        check=True,
    )
    commit_hash = result.stdout.strip()
    subprocess.run(
        ["git", "checkout", commit_hash],
        cwd=tmp_path,
        capture_output=True,
        check=True,
    )

    # Save current dir and change to temp repo
    old_cwd = os.getcwd()
    try:
        os.chdir(tmp_path)

        context = get_git_context()
        assert context is not None
        assert context.get("detached") is True
    finally:
        os.chdir(old_cwd)


def test_enhanced_git_context_parses_metadata(monkeypatch):
    snapshot = {
        "crash_frame_index": 0,
        "frames": [{"file_rel": "app.py", "line": 42}],
    }

    def fake_run(args, timeout=2.0, repo_root=None):
        _ = timeout
        _ = repo_root
        if args[:2] == ["rev-parse", "--show-toplevel"]:
            return "/repo"
        if args[:3] == ["rev-parse", "--short", "HEAD"]:
            return "abc1234"
        if args[:2] == ["rev-parse", "HEAD"]:
            return "abc1234567890abcdef1234567890abcdef1234"
        if args[:3] == ["rev-parse", "--abbrev-ref", "HEAD"]:
            return "main"
        if args[:2] == ["status", "--porcelain"]:
            return ""
        if args and args[0] == "blame":
            return (
                "def56789abcdef0123456789abcdef0123456789 42 42 1\n"
                "author Alice\n"
                "author-time 1700000000\n"
                "summary Fix parser line\n"
                "filename app.py\n"
                "\treturn int(value)\n"
            )
        if args and args[0] == "log":
            return (
                "__LLMDEBUG_COMMIT__\n"
                "abc1234\n"
                "abc1234567890abcdef1234567890abcdef1234\n"
                "Alice\n"
                "2026-02-10T10:00:00+00:00\n"
                "Fix parser\n"
                " 1 file changed, 2 insertions(+), 1 deletion(-)\n"
            )
        if args and args[0] == "show":
            return "12\t3\tapp.py\n"
        return _GitError("unexpected")

    monkeypatch.setattr(git_context_module, "_run_git_command", fake_run)
    context = get_enhanced_git_context(
        snapshot,
        out_dir=".llmdebug",
        max_commits=5,
        per_cmd_timeout_sec=1.0,
        total_budget_sec=5.0,
    )
    assert context is not None
    assert context["head"]["commit"] == "abc1234"
    assert context["crash_file"]["file_rel"] == "app.py"
    assert context["crash_file"]["blame"]["author"] == "Alice"
    assert len(context["recent_commits"]) == 1
    assert context["crash_file_diffstat"]["files_changed"] == 1


def test_enhanced_git_context_discovers_repo_from_out_dir(monkeypatch, tmp_path):
    target_repo = (tmp_path / "target_repo").resolve()
    target_repo_str = target_repo.as_posix()
    target_out_dir = (target_repo / ".llmdebug").resolve()
    target_out_dir_str = target_out_dir.as_posix()
    wrong_repo_str = (tmp_path / "wrong_repo").resolve().as_posix()
    snapshot = {
        "crash_frame_index": 0,
        "frames": [{"file_rel": "app.py"}],
    }

    def fake_run(args, timeout=2.0, repo_root=None):
        _ = timeout
        repo = str(repo_root) if repo_root is not None else None
        if args[:2] == ["rev-parse", "--show-toplevel"]:
            if repo in {target_out_dir_str, target_repo_str}:
                return target_repo_str
            return wrong_repo_str
        if args[:3] == ["rev-parse", "--short", "HEAD"]:
            return "abc1234" if repo == target_repo_str else "badcafe"
        if args[:2] == ["rev-parse", "HEAD"]:
            return ("a" * 40) if repo == target_repo_str else ("b" * 40)
        if args[:3] == ["rev-parse", "--abbrev-ref", "HEAD"]:
            return "main" if repo == target_repo_str else "wrong-branch"
        if args[:2] == ["status", "--porcelain"]:
            return ""
        if args and args[0] == "log":
            return ""
        return _GitError("unexpected")

    monkeypatch.setattr(git_context_module, "_run_git_command", fake_run)
    context = get_enhanced_git_context(snapshot, out_dir=target_out_dir_str)
    assert context is not None
    assert context["repo_root"] == target_repo_str
    assert context["head"]["commit"] == "abc1234"
    assert context["head"]["branch"] == "main"


def test_enhanced_git_context_marks_outside_repo(monkeypatch):
    snapshot = {
        "crash_frame_index": 0,
        "frames": [{"file": "/tmp/outside.py", "line": 8}],
    }
    calls: list[tuple[list[str], str | None]] = []

    def fake_run(args, timeout=2.0, repo_root=None):
        _ = timeout
        calls.append((args, repo_root))
        if args[:2] == ["rev-parse", "--show-toplevel"]:
            return "/repo"
        if args[:3] == ["rev-parse", "--short", "HEAD"]:
            return "abc1234"
        if args[:2] == ["rev-parse", "HEAD"]:
            return "abc1234567890abcdef1234567890abcdef1234"
        if args[:3] == ["rev-parse", "--abbrev-ref", "HEAD"]:
            return "main"
        if args[:2] == ["status", "--porcelain"]:
            return ""
        if args and args[0] == "log":
            return ""
        return _GitError("unexpected")

    monkeypatch.setattr(git_context_module, "_run_git_command", fake_run)
    context = get_enhanced_git_context(snapshot, out_dir=".llmdebug")
    assert context is not None
    assert context["crash_file"]["in_repo"] is False
    assert "blame" not in context["crash_file"]
    assert all(args[0] not in {"blame", "show"} for args, _ in calls)


def test_enhanced_git_context_budget_exhausted(monkeypatch):
    snapshot = {"frames": [{"file_rel": "app.py", "line": 1}], "crash_frame_index": 0}

    def fake_run(args, timeout=2.0, repo_root=None):
        _ = args
        _ = timeout
        _ = repo_root
        return "/repo"

    monkeypatch.setattr(git_context_module, "_run_git_command", fake_run)
    context = get_enhanced_git_context(snapshot, out_dir=".llmdebug", total_budget_sec=0.0)
    assert context is None
