//! Test-only helpers for building throwaway git repositories.
//!
//! Consolidates the "run a git command in a temp repo" pattern shared by
//! the portfolio-loader and audit-hygiene tests, so there is exactly one
//! copy of the `GIT_*` scrubbing + `git -C <dir>` invocation + error
//! handling. The scrubbing itself is delegated to `git.scrubbedEnv` (the
//! same routine production `runGit` uses), so test setup and production
//! agree on how the inherited hook environment is neutralized.

const std = @import("std");
const git = @import("../git.zig");

/// Returns false when `git` isn't on PATH so a caller can skip (early
/// return) rather than fail in a sandbox without git:
///
///     if (!test_git.available(allocator)) return;
pub fn available(allocator: std.mem.Allocator) bool {
    const probe = std.process.run(allocator, std.testing.io, .{
        .argv = &.{ "git", "--version" },
        .stdout_limit = .limited(1024),
    }) catch return false;
    allocator.free(probe.stdout);
    allocator.free(probe.stderr);
    return true;
}

/// Run `git -C <cwd> <argv...>` for test setup.
///
/// Inherited `GIT_*` vars are stripped (via `git.scrubbedEnv`) so the
/// command works even when the test process runs inside a git hook whose
/// `GIT_DIR` / `GIT_WORK_TREE` point at the outer repo.
///
/// When `date_iso` is non-null it pins both author and committer dates
/// (e.g. `"2026-02-15T12:00:00"`), which makes commit timestamps
/// deterministic for history-walk tests. Returns `error.GitFailed`
/// (after logging the argv + stderr) on a non-zero exit.
pub fn run(
    allocator: std.mem.Allocator,
    cwd: []const u8,
    date_iso: ?[]const u8,
    argv: []const []const u8,
) !void {
    const full_argv = try allocator.alloc([]const u8, argv.len + 3);
    defer allocator.free(full_argv);
    full_argv[0] = "git";
    full_argv[1] = "-C";
    full_argv[2] = cwd;
    @memcpy(full_argv[3..], argv);

    var base = try std.testing.environ.createMap(allocator);
    defer base.deinit();
    var env = try git.scrubbedEnv(allocator, &base);
    defer env.deinit();

    if (date_iso) |d| {
        try env.put("GIT_AUTHOR_DATE", d);
        try env.put("GIT_COMMITTER_DATE", d);
    }

    const result = try std.process.run(allocator, std.testing.io, .{
        .argv = full_argv,
        .environ_map = &env,
        .stdout_limit = .limited(64 * 1024),
    });
    defer allocator.free(result.stdout);
    defer allocator.free(result.stderr);
    switch (result.term) {
        .exited => |code| if (code != 0) {
            const joined = std.mem.join(allocator, " ", argv) catch null;
            defer if (joined) |j| allocator.free(j);
            std.log.err("git command failed (code {d}): {s}\nstderr: {s}", .{ code, joined orelse "?", result.stderr });
            return error.GitFailed;
        },
        else => return error.GitFailed,
    }
}
