"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.command = void 0;
const path = require("path");
const clc = require("colorette");
const requireInteractive_1 = require("../requireInteractive");
const command_1 = require("../command");
const error_1 = require("../error");
const iam_1 = require("../gcp/iam");
const logger_1 = require("../logger");
const prompt_1 = require("../prompt");
const requirePermissions_1 = require("../requirePermissions");
const utils_1 = require("../utils");
const functional_1 = require("../functional");
const configExport = require("../functions/runtimeConfigExport");
const requireConfig_1 = require("../requireConfig");
const projectConfig_1 = require("../functions/projectConfig");
const REQUIRED_PERMISSIONS = [
    "runtimeconfig.configs.list",
    "runtimeconfig.configs.get",
    "runtimeconfig.variables.list",
    "runtimeconfig.variables.get",
];
const RESERVED_PROJECT_ALIAS = ["local"];
const MAX_ATTEMPTS = 3;
function checkReservedAliases(pInfos) {
    for (const pInfo of pInfos) {
        if (pInfo.alias && RESERVED_PROJECT_ALIAS.includes(pInfo.alias)) {
            (0, utils_1.logWarning)(`Project alias (${clc.bold(pInfo.alias)}) is reserved for internal use. ` +
                `Saving exported config in .env.${pInfo.projectId} instead.`);
            delete pInfo.alias;
        }
    }
}
async function checkRequiredPermission(pInfos) {
    pInfos = pInfos.filter((pInfo) => !pInfo.config);
    const testPermissions = pInfos.map((pInfo) => (0, iam_1.testIamPermissions)(pInfo.projectId, REQUIRED_PERMISSIONS));
    const results = await Promise.all(testPermissions);
    for (const [pInfo, result] of (0, functional_1.zip)(pInfos, results)) {
        if (result.passed) {
            throw new error_1.FirebaseError(`Unexpectedly failed to fetch runtime config for project ${pInfo.projectId}`);
        }
        (0, utils_1.logWarning)("You are missing the following permissions to read functions config on project " +
            `${clc.bold(pInfo.projectId)}:\n\t${result.missing.join("\n\t")}`);
        const confirm = await (0, prompt_1.promptOnce)({
            type: "confirm",
            name: "skip",
            default: true,
            message: `Continue without importing configs from project ${pInfo.projectId}?`,
        });
        if (!confirm) {
            throw new error_1.FirebaseError("Command aborted!");
        }
    }
}
async function promptForPrefix(errMsg) {
    (0, utils_1.logWarning)("The following configs keys could not be exported as environment variables:\n");
    (0, utils_1.logWarning)(errMsg);
    return await (0, prompt_1.promptOnce)({
        type: "input",
        name: "prefix",
        default: "CONFIG_",
        message: "Enter a PREFIX to rename invalid environment variable keys:",
    }, {});
}
function fromEntries(itr) {
    const obj = {};
    for (const [k, v] of itr) {
        obj[k] = v;
    }
    return obj;
}
exports.command = new command_1.Command("functions:config:export")
    .description("Export environment config as environment variables in dotenv format")
    .before(requirePermissions_1.requirePermissions, [
    "runtimeconfig.configs.list",
    "runtimeconfig.configs.get",
    "runtimeconfig.variables.list",
    "runtimeconfig.variables.get",
])
    .before(requireConfig_1.requireConfig)
    .before(requireInteractive_1.default)
    .action(async (options) => {
    const config = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions)[0];
    const functionsDir = config.source;
    let pInfos = configExport.getProjectInfos(options);
    checkReservedAliases(pInfos);
    (0, utils_1.logBullet)("Importing functions configs from projects [" +
        pInfos.map(({ projectId }) => `${clc.bold(projectId)}`).join(", ") +
        "]");
    await configExport.hydrateConfigs(pInfos);
    await checkRequiredPermission(pInfos);
    pInfos = pInfos.filter((pInfo) => pInfo.config);
    logger_1.logger.debug(`Loaded function configs: ${JSON.stringify(pInfos)}`);
    (0, utils_1.logBullet)(`Importing configs from projects: [${pInfos.map((p) => p.projectId).join(", ")}]`);
    let attempts = 0;
    let prefix = "";
    while (true) {
        if (attempts >= MAX_ATTEMPTS) {
            throw new error_1.FirebaseError("Exceeded max attempts to fix invalid config keys.");
        }
        const errMsg = configExport.hydrateEnvs(pInfos, prefix);
        if (errMsg.length === 0) {
            break;
        }
        prefix = await promptForPrefix(errMsg);
        attempts += 1;
    }
    const header = `# Exported firebase functions:config:export command on ${new Date().toLocaleDateString()}`;
    const dotEnvs = pInfos.map((pInfo) => configExport.toDotenvFormat(pInfo.envs, header));
    const filenames = pInfos.map(configExport.generateDotenvFilename);
    const filesToWrite = fromEntries((0, functional_1.zip)(filenames, dotEnvs));
    filesToWrite[".env.local"] = `${header}\n# .env.local file contains environment variables for the Functions Emulator.\n`;
    filesToWrite[".env"] = `${header}# .env file contains environment variables that applies to all projects.\n`;
    for (const [filename, content] of Object.entries(filesToWrite)) {
        await options.config.askWriteProjectFile(path.join(functionsDir, filename), content);
    }
});
