"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runNativeGit = runNativeGit;
exports.readNativeGit = readNativeGit;
exports.runNativeGitCommand = runNativeGitCommand;
exports.runNativeGitWithLegacyRunner = runNativeGitWithLegacyRunner;
exports.readNativeGitStatusPorcelain = readNativeGitStatusPorcelain;
exports.isNativeGitExitCode = isNativeGitExitCode;
exports.withGitIdentityArgs = withGitIdentityArgs;
exports.withGitIdentityEnv = withGitIdentityEnv;
exports.redactNativeGitOutput = redactNativeGitOutput;
const promises_1 = require("node:fs/promises");
const node_os_1 = __importDefault(require("node:os"));
const node_path_1 = __importDefault(require("node:path"));
const node_process_1 = __importDefault(require("node:process"));
const command_1 = require("../core/command");
const git_command_env_1 = require("../core/git-command-env");
async function runNativeGit(workspacePath, args, options = {}) {
    await readNativeGit(workspacePath, args, options);
}
async function readNativeGit(workspacePath, args, options = {}) {
    const { stdout } = await runNativeGitCommand({
        ...options,
        cwd: workspacePath,
        args,
    });
    return stdout;
}
async function runNativeGitCommand(options) {
    const credentialAskPass = options.credentials
        ? await createGitAskPassScript()
        : null;
    try {
        const env = buildNativeGitEnv({
            ...(options.env !== undefined ? { baseEnv: options.env } : {}),
            credentials: options.credentials ?? null,
            askPassPath: credentialAskPass?.path ?? null,
        });
        const commandOptions = {
            command: "git",
            args: buildNativeGitArgs(options.args, {
                credentials: options.credentials ?? null,
                suppressHooks: options.suppressHooks === true,
            }),
            env,
        };
        if (options.cwd !== undefined) {
            commandOptions.cwd = options.cwd;
        }
        if (options.input !== undefined) {
            commandOptions.input = options.input;
        }
        if (options.allowNonZeroExit !== undefined) {
            commandOptions.allowNonZeroExit = options.allowNonZeroExit;
        }
        if (options.timeoutMs !== undefined) {
            commandOptions.timeoutMs = options.timeoutMs;
        }
        const result = await (0, command_1.runCommand)(commandOptions);
        return redactNativeGitCommandResult(result, options.credentials ?? null);
    }
    catch (error) {
        throw redactNativeGitCommandError(error, options.credentials ?? null);
    }
    finally {
        if (credentialAskPass !== null) {
            await credentialAskPass.cleanup();
        }
    }
}
async function runNativeGitWithLegacyRunner(runner, options) {
    if (!runner) {
        return runNativeGitCommand(options);
    }
    const credentialAskPass = options.credentials
        ? await createGitAskPassScript()
        : null;
    try {
        const env = buildNativeGitEnv({
            ...(options.env !== undefined ? { baseEnv: options.env } : {}),
            credentials: options.credentials ?? null,
            askPassPath: credentialAskPass?.path ?? null,
        });
        const legacyOptions = {
            command: "git",
            args: buildNativeGitArgs(options.args, {
                credentials: options.credentials ?? null,
                suppressHooks: options.suppressHooks === true,
            }),
            env,
        };
        if (options.cwd !== undefined) {
            legacyOptions.cwd = options.cwd;
        }
        if (options.input !== undefined) {
            legacyOptions.input = options.input;
        }
        if (options.allowNonZeroExit !== undefined) {
            legacyOptions.allowNonZeroExit = options.allowNonZeroExit;
        }
        if (options.timeoutMs !== undefined) {
            legacyOptions.timeoutMs = options.timeoutMs;
        }
        if (options.locale !== undefined) {
            legacyOptions.locale = options.locale;
        }
        const result = await runner(legacyOptions);
        return redactNativeGitCommandResult(result, options.credentials ?? null);
    }
    catch (error) {
        throw redactNativeGitCommandError(error, options.credentials ?? null);
    }
    finally {
        if (credentialAskPass !== null) {
            await credentialAskPass.cleanup();
        }
    }
}
async function readNativeGitStatusPorcelain(workspacePath, pathspecs = []) {
    return (await readNativeGit(workspacePath, [
        "status",
        "--porcelain=v1",
        "--untracked-files=all",
        ...(pathspecs.length > 0 ? ["--", ...pathspecs] : []),
    ])).trim();
}
function isNativeGitExitCode(error, code) {
    return ((typeof error === "object" &&
        error !== null &&
        "code" in error &&
        error.code === code) ||
        (error instanceof command_1.CommandError && error.exitCode === code));
}
function withGitIdentityArgs(args, identity) {
    return [
        "-c",
        `user.name=${identity.name}`,
        "-c",
        `user.email=${identity.email}`,
        ...args,
    ];
}
function buildNativeGitArgs(args, options) {
    const configArgs = [];
    if (options.credentials) {
        configArgs.push("-c", "credential.helper=");
    }
    if (options.suppressHooks) {
        configArgs.push("-c", "core.hooksPath=/dev/null");
    }
    return [...configArgs, ...args];
}
function withGitIdentityEnv(identity, baseEnv = node_process_1.default.env) {
    const env = { ...baseEnv };
    setEnvValue(env, "GIT_AUTHOR_NAME", identity.name);
    setEnvValue(env, "GIT_AUTHOR_EMAIL", identity.email);
    setEnvValue(env, "GIT_COMMITTER_NAME", identity.name);
    setEnvValue(env, "GIT_COMMITTER_EMAIL", identity.email);
    return env;
}
function redactNativeGitOutput(text, credentials) {
    if (!credentials) {
        return text;
    }
    let redacted = text;
    for (const secret of [credentials.username, credentials.password]) {
        if (secret.length > 0) {
            redacted = redacted.split(secret).join("<redacted>");
        }
    }
    return redacted;
}
function buildNativeGitEnv(input) {
    const env = (0, git_command_env_1.buildGitCommandEnv)(input.baseEnv);
    setEnvValue(env, "GIT_TERMINAL_PROMPT", "0");
    setEnvValue(env, "GIT_CONFIG_NOSYSTEM", "1");
    setEnvValue(env, "GIT_OPTIONAL_LOCKS", "0");
    if (input.credentials && input.askPassPath) {
        setEnvValue(env, "GIT_ASKPASS", input.askPassPath);
        setEnvValue(env, "SSH_ASKPASS", input.askPassPath);
        setEnvValue(env, "ALPACA_GIT_ASKPASS_USERNAME", input.credentials.username);
        setEnvValue(env, "ALPACA_GIT_ASKPASS_PASSWORD", input.credentials.password);
    }
    return env;
}
function setEnvValue(env, name, value) {
    env[name] = value;
}
async function createGitAskPassScript() {
    const directory = await (0, promises_1.mkdtemp)(node_path_1.default.join(node_os_1.default.tmpdir(), "alpaca-git-"));
    const scriptPath = node_path_1.default.join(directory, "askpass.sh");
    await (0, promises_1.writeFile)(scriptPath, [
        "#!/bin/sh",
        'case "$1" in',
        '*Username*|*username*) printf "%s\\n" "$ALPACA_GIT_ASKPASS_USERNAME" ;;',
        '*) printf "%s\\n" "$ALPACA_GIT_ASKPASS_PASSWORD" ;;',
        "esac",
        "",
    ].join("\n"), "utf8");
    await (0, promises_1.chmod)(scriptPath, 0o700);
    return {
        path: scriptPath,
        cleanup: () => (0, promises_1.rm)(directory, { recursive: true, force: true }),
    };
}
function redactNativeGitCommandResult(result, credentials) {
    if (!credentials) {
        return result;
    }
    return {
        ...result,
        stdout: redactNativeGitOutput(result.stdout, credentials),
        stderr: redactNativeGitOutput(result.stderr, credentials),
    };
}
function redactNativeGitCommandError(error, credentials) {
    if (!credentials || !(error instanceof command_1.CommandError)) {
        return error;
    }
    const redactedResult = {
        stdout: redactNativeGitOutput(error.stdout, credentials),
        stderr: redactNativeGitOutput(error.stderr, credentials),
        exitCode: error.exitCode,
        signal: error.signal,
        timedOut: error.timedOut,
        timeoutKind: error.timeoutKind,
    };
    if (error.timeoutMs !== undefined) {
        redactedResult.timeoutMs = error.timeoutMs;
    }
    if (error.durationMs !== undefined) {
        redactedResult.durationMs = error.durationMs;
    }
    if (error.startedAt !== undefined) {
        redactedResult.startedAt = error.startedAt;
    }
    if (error.endedAt !== undefined) {
        redactedResult.endedAt = error.endedAt;
    }
    if (error.lastOutputAt !== undefined) {
        redactedResult.lastOutputAt = error.lastOutputAt;
    }
    const commandOptions = {
        command: error.command,
        args: error.args,
        locale: error.locale,
    };
    if (error.cwd !== undefined) {
        commandOptions.cwd = error.cwd;
    }
    return new command_1.CommandError(commandOptions, redactedResult);
}
