"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFirebaseProject = exports.listFirebaseProjects = exports.getAvailableCloudProjectPage = exports.getFirebaseProjectPage = exports.addFirebaseToCloudProject = exports.createCloudProject = exports.promptAvailableProjectId = exports.getOrPromptProject = exports.addFirebaseToCloudProjectAndLog = exports.createFirebaseProjectAndLog = exports.PROJECTS_CREATE_QUESTIONS = exports.ProjectParentResourceType = void 0;
const clc = require("colorette");
const ora = require("ora");
const apiv2_1 = require("../apiv2");
const error_1 = require("../error");
const operation_poller_1 = require("../operation-poller");
const prompt_1 = require("../prompt");
const api = require("../api");
const logger_1 = require("../logger");
const utils = require("../utils");
const TIMEOUT_MILLIS = 30000;
const MAXIMUM_PROMPT_LIST = 100;
const PROJECT_LIST_PAGE_SIZE = 1000;
const CREATE_PROJECT_API_REQUEST_TIMEOUT_MILLIS = 15000;
var ProjectParentResourceType;
(function (ProjectParentResourceType) {
    ProjectParentResourceType["ORGANIZATION"] = "organization";
    ProjectParentResourceType["FOLDER"] = "folder";
})(ProjectParentResourceType = exports.ProjectParentResourceType || (exports.ProjectParentResourceType = {}));
exports.PROJECTS_CREATE_QUESTIONS = [
    {
        type: "input",
        name: "projectId",
        default: "",
        message: "Please specify a unique project id " +
            `(${clc.yellow("warning")}: cannot be modified afterward) [6-30 characters]:\n`,
    },
    {
        type: "input",
        name: "displayName",
        default: "",
        message: "What would you like to call your project? (defaults to your project ID)",
    },
];
const firebaseAPIClient = new apiv2_1.Client({
    urlPrefix: api.firebaseApiOrigin,
    auth: true,
    apiVersion: "v1beta1",
});
async function createFirebaseProjectAndLog(projectId, options) {
    const spinner = ora("Creating Google Cloud Platform project").start();
    try {
        await createCloudProject(projectId, options);
        spinner.succeed();
    }
    catch (err) {
        spinner.fail();
        throw err;
    }
    return addFirebaseToCloudProjectAndLog(projectId);
}
exports.createFirebaseProjectAndLog = createFirebaseProjectAndLog;
async function addFirebaseToCloudProjectAndLog(projectId) {
    let projectInfo;
    const spinner = ora("Adding Firebase resources to Google Cloud Platform project").start();
    try {
        projectInfo = await addFirebaseToCloudProject(projectId);
    }
    catch (err) {
        spinner.fail();
        throw err;
    }
    spinner.succeed();
    logNewFirebaseProjectInfo(projectInfo);
    return projectInfo;
}
exports.addFirebaseToCloudProjectAndLog = addFirebaseToCloudProjectAndLog;
function logNewFirebaseProjectInfo(projectInfo) {
    logger_1.logger.info("");
    if (process.platform === "win32") {
        logger_1.logger.info("=== Your Firebase project is ready! ===");
    }
    else {
        logger_1.logger.info("🎉🎉🎉 Your Firebase project is ready! 🎉🎉🎉");
    }
    logger_1.logger.info("");
    logger_1.logger.info("Project information:");
    logger_1.logger.info(`   - Project ID: ${clc.bold(projectInfo.projectId)}`);
    logger_1.logger.info(`   - Project Name: ${clc.bold(projectInfo.displayName)}`);
    logger_1.logger.info("");
    logger_1.logger.info("Firebase console is available at");
    logger_1.logger.info(`https://console.firebase.google.com/project/${clc.bold(projectInfo.projectId)}/overview`);
}
async function getOrPromptProject(options) {
    if (options.project) {
        return await getFirebaseProject(options.project);
    }
    return selectProjectInteractively();
}
exports.getOrPromptProject = getOrPromptProject;
async function selectProjectInteractively(pageSize = MAXIMUM_PROMPT_LIST) {
    const { projects, nextPageToken } = await getFirebaseProjectPage(pageSize);
    if (projects.length === 0) {
        throw new error_1.FirebaseError("There are no Firebase projects associated with this account.");
    }
    if (nextPageToken) {
        logger_1.logger.debug(`Found more than ${projects.length} projects, selecting via prompt`);
        return selectProjectByPrompting();
    }
    return selectProjectFromList(projects);
}
async function selectProjectByPrompting() {
    const projectId = await (0, prompt_1.promptOnce)({
        type: "input",
        message: "Please input the project ID you would like to use:",
    });
    return await getFirebaseProject(projectId);
}
async function selectProjectFromList(projects = []) {
    const choices = projects
        .filter((p) => !!p)
        .map((p) => {
        return {
            name: p.projectId + (p.displayName ? ` (${p.displayName})` : ""),
            value: p.projectId,
        };
    })
        .sort((a, b) => a.name.localeCompare(b.name));
    if (choices.length >= 25) {
        utils.logBullet(`Don't want to scroll through all your projects? If you know your project ID, ` +
            `you can initialize it directly using ${clc.bold("firebase init --project <project_id>")}.\n`);
    }
    const projectId = await (0, prompt_1.promptOnce)({
        type: "list",
        name: "id",
        message: "Select a default Firebase project for this directory:",
        choices,
    });
    const project = projects.find((p) => p.projectId === projectId);
    if (!project) {
        throw new error_1.FirebaseError("Unexpected error. Project does not exist");
    }
    return project;
}
function getProjectId(cloudProject) {
    const resourceName = cloudProject.project;
    return resourceName.substring(resourceName.lastIndexOf("/") + 1);
}
async function promptAvailableProjectId() {
    const { projects, nextPageToken } = await getAvailableCloudProjectPage(MAXIMUM_PROMPT_LIST);
    if (projects.length === 0) {
        throw new error_1.FirebaseError("There are no available Google Cloud projects to add Firebase services.");
    }
    if (nextPageToken) {
        return await (0, prompt_1.promptOnce)({
            type: "input",
            message: "Please input the ID of the Google Cloud Project you would like to add Firebase:",
        });
    }
    else {
        const choices = projects
            .filter((p) => !!p)
            .map((p) => {
            const projectId = getProjectId(p);
            return {
                name: projectId + (p.displayName ? ` (${p.displayName})` : ""),
                value: projectId,
            };
        })
            .sort((a, b) => a.name.localeCompare(b.name));
        return await (0, prompt_1.promptOnce)({
            type: "list",
            name: "id",
            message: "Select the Google Cloud Platform project you would like to add Firebase:",
            choices,
        });
    }
}
exports.promptAvailableProjectId = promptAvailableProjectId;
async function createCloudProject(projectId, options) {
    try {
        const client = new apiv2_1.Client({ urlPrefix: api.resourceManagerOrigin, apiVersion: "v1" });
        const data = {
            projectId,
            name: options.displayName || projectId,
            parent: options.parentResource,
        };
        const response = await client.request({
            method: "POST",
            path: "/projects",
            body: data,
            timeout: CREATE_PROJECT_API_REQUEST_TIMEOUT_MILLIS,
        });
        const projectInfo = await (0, operation_poller_1.pollOperation)({
            pollerName: "Project Creation Poller",
            apiOrigin: api.resourceManagerOrigin,
            apiVersion: "v1",
            operationResourceName: response.body.name,
        });
        return projectInfo;
    }
    catch (err) {
        if (err.status === 409) {
            throw new error_1.FirebaseError(`Failed to create project because there is already a project with ID ${clc.bold(projectId)}. Please try again with a unique project ID.`, {
                exit: 2,
                original: err,
            });
        }
        else {
            throw new error_1.FirebaseError("Failed to create project. See firebase-debug.log for more info.", {
                exit: 2,
                original: err,
            });
        }
    }
}
exports.createCloudProject = createCloudProject;
async function addFirebaseToCloudProject(projectId) {
    try {
        const response = await firebaseAPIClient.request({
            method: "POST",
            path: `/projects/${projectId}:addFirebase`,
            timeout: CREATE_PROJECT_API_REQUEST_TIMEOUT_MILLIS,
        });
        const projectInfo = await (0, operation_poller_1.pollOperation)({
            pollerName: "Add Firebase Poller",
            apiOrigin: api.firebaseApiOrigin,
            apiVersion: "v1beta1",
            operationResourceName: response.body.name,
        });
        return projectInfo;
    }
    catch (err) {
        logger_1.logger.debug(err.message);
        throw new error_1.FirebaseError("Failed to add Firebase to Google Cloud Platform project. See firebase-debug.log for more info.", { exit: 2, original: err });
    }
}
exports.addFirebaseToCloudProject = addFirebaseToCloudProject;
async function getProjectPage(apiResource, options) {
    const queryParams = {
        pageSize: `${options.pageSize}`,
    };
    if (options.pageToken) {
        queryParams.pageToken = options.pageToken;
    }
    const res = await firebaseAPIClient.request({
        method: "GET",
        path: apiResource,
        queryParams,
        timeout: TIMEOUT_MILLIS,
        skipLog: { resBody: true },
    });
    const projects = res.body[options.responseKey];
    const token = res.body.nextPageToken;
    return {
        projects: Array.isArray(projects) ? projects : [],
        nextPageToken: typeof token === "string" ? token : undefined,
    };
}
async function getFirebaseProjectPage(pageSize = PROJECT_LIST_PAGE_SIZE, pageToken) {
    let projectPage;
    try {
        projectPage = await getProjectPage("/projects", {
            responseKey: "results",
            pageSize,
            pageToken,
        });
    }
    catch (err) {
        logger_1.logger.debug(err.message);
        throw new error_1.FirebaseError("Failed to list Firebase projects. See firebase-debug.log for more info.", { exit: 2, original: err });
    }
    return projectPage;
}
exports.getFirebaseProjectPage = getFirebaseProjectPage;
async function getAvailableCloudProjectPage(pageSize = PROJECT_LIST_PAGE_SIZE, pageToken) {
    try {
        return await getProjectPage("/availableProjects", {
            responseKey: "projectInfo",
            pageSize,
            pageToken,
        });
    }
    catch (err) {
        logger_1.logger.debug(err.message);
        throw new error_1.FirebaseError("Failed to list available Google Cloud Platform projects. See firebase-debug.log for more info.", { exit: 2, original: err });
    }
}
exports.getAvailableCloudProjectPage = getAvailableCloudProjectPage;
async function listFirebaseProjects(pageSize) {
    const projects = [];
    let nextPageToken;
    do {
        const projectPage = await getFirebaseProjectPage(pageSize, nextPageToken);
        projects.push(...projectPage.projects);
        nextPageToken = projectPage.nextPageToken;
    } while (nextPageToken);
    return projects;
}
exports.listFirebaseProjects = listFirebaseProjects;
async function getFirebaseProject(projectId) {
    try {
        const res = await firebaseAPIClient.request({
            method: "GET",
            path: `/projects/${projectId}`,
            timeout: TIMEOUT_MILLIS,
        });
        return res.body;
    }
    catch (err) {
        let message = err.message;
        if (err.original) {
            message += ` (original: ${err.original.message})`;
        }
        logger_1.logger.debug(message);
        throw new error_1.FirebaseError(`Failed to get Firebase project ${projectId}. ` +
            "Please make sure the project exists and your account has permission to access it.", { exit: 2, original: err });
    }
}
exports.getFirebaseProject = getFirebaseProject;
