const { execSync } = require("child_process");
const { existsSync, readFileSync } = require("fs");
const { join } = require("path");

/**
 * Logs to the console
 */
const log = (msg) => console.log(`\n${msg}`); // eslint-disable-line no-console

/**
 * Exits the current process with an error code and message
 */
const exit = (msg) => {
    console.error(msg);
    process.exit(1);
};

/**
 * Executes the provided shell command and redirects stdout/stderr to the console
 */
const run = (cmd, cwd) => execSync(cmd, { encoding: "utf8", stdio: "inherit", cwd });

/**
 * Determines the current operating system (one of ["mac", "windows", "linux"])
 */
const getPlatform = () => {
    switch (process.platform) {
        case "darwin":
            return "mac";
        case "win32":
            return "windows";
        default:
            return "linux";
    }
};

/**
 * Returns the value for an environment variable (or `null` if it's not defined)
 */
const getEnv = (name) => process.env[name.toUpperCase()] || null;

/**
 * Sets the specified env variable if the value isn't empty
 */
const setEnv = (name, value) => {
    if (value) {
        process.env[name.toUpperCase()] = value.toString();
    }
};

/**
 * Returns the value for an input variable (or `null` if it's not defined). If the variable is
 * required and doesn't have a value, abort the action
 */
const getInput = (name, required) => {
    const value = getEnv(`INPUT_${name}`);
    if (required && !value) {
        exit(`"${name}" input variable is not defined`);
    }
    return value;
};

/**
 * Installs NPM dependencies and builds/releases the Electron app
 */
const runAction = () => {
    const platform = getPlatform();
    const release = getInput("release", true) === "true";
    const pkgRoot = getInput("package_root", true);
    const buildScriptName = getInput("build_script_name", true);
    const skipBuild = getInput("skip_build") === "true";
    const useVueCli = getInput("use_vue_cli") === "true";
    const args = getInput("args") || "";
    const maxAttempts = Number(getInput("max_attempts") || "1");

    // TODO: Deprecated option, remove in v2.0. `electron-builder` always requires a `package.json` in
    // the same directory as the Electron app, so the `package_root` option should be used instead
    const appRoot = getInput("app_root") || pkgRoot;

    const pkgJsonPath = join(pkgRoot, "package.json");
    const pkgLockPath = join(pkgRoot, "package-lock.json");

    // Determine whether NPM should be used to run commands (instead of Yarn, which is the default)
    const useNpm = existsSync(pkgLockPath);
    log(`Will run ${useNpm ? "NPM" : "Yarn"} commands in directory "${pkgRoot}"`);

    // Make sure `package.json` file exists
    if (!existsSync(pkgJsonPath)) {
        exit(`\`package.json\` file not found at path "${pkgJsonPath}"`);
    }

    // Copy "github_token" input variable to "GH_TOKEN" env variable (required by `electron-builder`)
    setEnv("GH_TOKEN", getInput("github_token", true));

    // Require code signing certificate and password if building for macOS. Export them to environment
    // variables (required by `electron-builder`)
    if (platform === "mac") {
        const mac_certs = getInput("mac_certs");
        const mac_certs_password = getInput("mac_certs_password");
        if (mac_certs && mac_certs_password) {
            setEnv("CSC_LINK", mac_certs);
            setEnv("CSC_KEY_PASSWORD", mac_certs_password);
        } else {
            setEnv("CSC_IDENTITY_AUTO_DISCOVERY", "false");
        }
    } else if (platform === "windows") {
        setEnv("CSC_LINK", getInput("windows_certs"));
        setEnv("CSC_KEY_PASSWORD", getInput("windows_certs_password"));
    }

    // Disable console advertisements during install phase
    setEnv("ADBLOCK", true);

    // log(`Installing dependencies using ${useNpm ? "NPM" : "Yarn"}…`);
    // run(useNpm ? "npm install" : "yarn", pkgRoot);

    // Run NPM build script if it exists
    if (skipBuild) {
        log("Skipping build script because `skip_build` option is set");
    } else {
        log("Running the build script…");
        if (useNpm) {
            run(`npm run ${buildScriptName} --if-present`, pkgRoot);
        } else {
            // TODO: Use `yarn run ${buildScriptName} --if-present` once supported
            // https://github.com/yarnpkg/yarn/issues/6894
            const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf8"));
            if (pkgJson.scripts && pkgJson.scripts[buildScriptName]) {
                run(`yarn run ${buildScriptName}`, pkgRoot);
            }
        }
    }

    log(`Building${release ? " and releasing" : ""} the Electron app…`);
    const cmd = useVueCli ? "vue-cli-service electron:build" : `electron-builder`;
    for (let i = 0; i < maxAttempts; i += 1) {
        try {
            run(
                `${useNpm ? "npx --no-install" : "yarn run"} ${cmd} --${platform} ${
					release ? "--publish always" : ""
				} ${args}`,
                appRoot,
            );
            break;
        } catch (err) {
            if (i < maxAttempts - 1) {
                log(`Attempt ${i + 1} failed:`);
                log(err);
            } else {
                throw err;
            }
        }
    }
};

runAction();