import { execFileSync } from "child_process";
import { promises as fs } from "fs";
import { tmpdir } from "os";
import { join, dirname } from "path";

// All git invocations use execFileSync with an argv array (never a shell
// string) so that untrusted event fields (name, description, ...) can never
// reach a shell for interpretation, even though they're already restricted
// to single-line/no-NUL by validateRequest.ts.
function git(args: string[], cwd: string): string {
  return execFileSync("git", args, { cwd, encoding: "utf-8" });
}

export async function cloneRepo(sshUrl: string): Promise<string> {
  const repoPath = await fs.mkdtemp(join(tmpdir(), "event-central-"));
  execFileSync("git", ["clone", sshUrl, repoPath], { encoding: "utf-8" });
  return repoPath;
}

export function createBranch(repoPath: string, branchName: string): void {
  git(["checkout", "-b", branchName], repoPath);
}

export async function writeFile(repoPath: string, relativePath: string, contents: string): Promise<void> {
  const absolutePath = join(repoPath, relativePath);
  await fs.mkdir(dirname(absolutePath), { recursive: true });
  await fs.writeFile(absolutePath, contents, "utf-8");
}

export async function writeBinaryFile(repoPath: string, relativePath: string, contents: Buffer): Promise<void> {
  const absolutePath = join(repoPath, relativePath);
  await fs.mkdir(dirname(absolutePath), { recursive: true });
  await fs.writeFile(absolutePath, contents);
}

export function commitAll(repoPath: string, message: string): void {
  git(["add", "."], repoPath);
  git(["commit", "-m", message], repoPath);
}

export function pushBranch(repoPath: string, branchName: string, remoteName = "origin"): void {
  git(["push", "-u", remoteName, branchName], repoPath);
}

export async function removeRepo(repoPath: string): Promise<void> {
  await fs.rm(repoPath, { recursive: true, force: true });
}
