import { homedir } from "node:os";
import { join } from "node:path";
import { readdir } from "node:fs/promises";
import * as action from "@actions/core";
import { downloadTool, extractZip } from "@actions/tool-cache";
import * as cache from "@actions/cache";
import { restoreCache, saveCache } from "@actions/cache";
import { mv } from "@actions/io";
import { getExecOutput } from "@actions/exec";
export default async (options) => {
    const { url, cacheKey } = getDownloadUrl(options);
    const cacheEnabled = cacheKey && cache.isFeatureAvailable();
    const dir = join(homedir(), ".bun", "bin");
    action.addPath(dir);
    const path = join(dir, "bun");
    let version;
    let cacheHit = false;
    if (cacheEnabled) {
        const cacheRestored = await restoreCache([path], cacheKey);
        if (cacheRestored) {
            version = await verifyBun(path);
            if (version) {
                cacheHit = true;
                action.info("Using a cached version of Bun.");
            }
            else {
                action.warning("Found a cached version of Bun, but it appears to be corrupted? Attempting to download a new version.");
            }
        }
    }
    if (!cacheHit) {
        action.info(`Downloading a new version of Bun: ${url}`);
        const zipPath = await downloadTool(url);
        const extractedPath = await extractZip(zipPath);
        const exePath = await extractBun(extractedPath);
        await mv(exePath, path);
        version = await verifyBun(path);
    }
    if (!version) {
        throw new Error("Downloaded a new version of Bun, but failed to check its version? Try again in debug mode.");
    }
    if (cacheEnabled) {
        try {
            await saveCache([path], cacheKey);
        }
        catch (error) {
            action.warning("Failed to save Bun to cache.");
        }
    }
    return {
        version,
        cacheHit,
    };
};
function getDownloadUrl(options) {
    if (options?.customUrl) {
        return {
            url: options.customUrl,
            cacheKey: null,
        };
    }
    const release = options?.version ?? "latest";
    const os = options?.os ?? process.platform;
    const arch = options?.arch ?? process.arch;
    const avx2 = options?.avx2 ?? true;
    const profile = options?.profile ?? false;
    const { href } = new URL(`${release}/${os}/${arch}?avx2=${avx2}&profile=${profile}`, "https://bun.sh/download/");
    return {
        url: href,
        cacheKey: /^canary|latest$/i.test(release)
            ? null
            : `bun-${release}-${os}-${arch}-${avx2}-${profile}`,
    };
}
async function extractBun(path) {
    for (const entry of await readdir(path, { withFileTypes: true })) {
        const entryPath = join(path, entry.name);
        action.debug(`Looking: ${entryPath}`);
        if (entry.name === "bun" && entry.isFile()) {
            action.debug(`Found: ${entryPath}`);
            return entryPath;
        }
        if (entry.name.startsWith("bun") && entry.isDirectory()) {
            action.debug(`Continue looking: ${entryPath}`);
            return extractBun(entryPath);
        }
    }
    throw new Error("Could not find executable: bun");
}
async function verifyBun(path) {
    const { exitCode, stdout } = await getExecOutput(path, ["--version"], {
        ignoreReturnCode: true,
    });
    return exitCode === 0 ? stdout.trim() : undefined;
}
