import { spawnSync } from "node:child_process";
import { randomBytes } from "node:crypto";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { rootPath } from "../config";
import { REQUEST_TIMEOUT_MS } from "./types";

export interface RepositoryWriteCapability {
  status: "available" | "unavailable" | "unknown";
  available: boolean | null;
  evidence: "git-push-dry-run";
  reason: string;
  exitCode: number | null;
  writesRemote: false;
  mutation: false;
  valuesPrinted: false;
}

export function probeRepositoryWriteCapability(repo: string, token: string): RepositoryWriteCapability {
  const root = mkdtempSync(join(tmpdir(), "unidesk-gh-auth-"));
  const askpass = join(root, "askpass.sh");
  const probeRef = `refs/heads/unidesk-auth-capability-${randomBytes(6).toString("hex")}`;
  try {
    writeFileSync(askpass, [
      "#!/bin/sh",
      "case \"$1\" in",
      "  *Username*) printf '%s\\n' 'x-access-token' ;;",
      "  *Password*) printf '%s\\n' \"$UNIDESK_GH_AUTH_TOKEN\" ;;",
      "  *) printf '\\n' ;;",
      "esac",
      "",
    ].join("\n"), { encoding: "utf8", mode: 0o700 });
    const result = spawnSync("git", [
      "-c", "credential.helper=",
      "-c", "http.extraHeader=",
      "push", "--dry-run", "--porcelain",
      `https://github.com/${repo}.git`,
      `HEAD:${probeRef}`,
    ], {
      encoding: "utf8",
      cwd: rootPath(),
      env: {
        ...process.env,
        GH_TOKEN: undefined,
        GITHUB_TOKEN: undefined,
        GIT_ASKPASS: askpass,
        GIT_ASKPASS_REQUIRE: "force",
        GIT_TERMINAL_PROMPT: "0",
        UNIDESK_GH_AUTH_TOKEN: token,
      },
      stdio: ["ignore", "pipe", "pipe"],
      timeout: REQUEST_TIMEOUT_MS,
      maxBuffer: 64 * 1024,
    });
    if (result.status === 0) return capability(true, "git-push-dry-run-succeeded", result.status);
    const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
    if (/write access .* not granted|permission denied|authentication failed|requested url returned error: 403|repository not found|remote rejected/iu.test(output)) {
      return capability(false, "git-push-dry-run-permission-denied", result.status);
    }
    if (result.error?.name === "ETIMEDOUT") return capability(null, "git-push-dry-run-timeout", result.status);
    if ((result.error as NodeJS.ErrnoException | undefined)?.code === "ENOENT") return capability(null, "git-binary-missing", result.status);
    return capability(null, "git-push-dry-run-inconclusive", result.status);
  } finally {
    rmSync(root, { recursive: true, force: true });
  }
}

function capability(available: boolean | null, reason: string, exitCode: number | null): RepositoryWriteCapability {
  return {
    status: available === true ? "available" : available === false ? "unavailable" : "unknown",
    available,
    evidence: "git-push-dry-run",
    reason,
    exitCode,
    writesRemote: false,
    mutation: false,
    valuesPrinted: false,
  };
}
