import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { join, relative } from "node:path";
import { MAX_PRETTY_BRANCH_NAME as MAX_PRETTY_BRANCH_LENGTH, ZUPLO_SYSTEM_ENV_VAR, } from "../common/constants.js";
import { logger } from "../common/logger.js";
import { printCriticalFailureToConsoleAndExit, printDiagnosticsToConsole, printResultToConsoleAndExitGracefully, printSpinnerToConsole, printWarningToConsole, } from "../common/output.js";
import settings from "../common/settings.js";
import { normalizeUrl } from "../common/utils/urls.js";
import { pullSystemConfig } from "../common/populate.js";
import { archive, generateMetadata } from "./archive.js";
import { UnableToAutoLinkToExistingProject, retrieveOrCreateEnvironment, } from "./environments.js";
import { upload } from "./file-upload.js";
import { pollBuild, pollDeployment } from "./poll-deployment.js";
export async function deploy(argv) {
    if (argv["self-hosted-endpoint"]) {
        const args = argv;
        await deployToSelfHosted(args);
    }
    else {
        await deployToSaas(argv);
    }
}
async function deployToSaas(argv) {
    const archiveMetadata = await archive(argv);
    logger.debug(`Tarball created locally at ${archiveMetadata.tarball}`);
    const { account, project } = argv;
    const payload = {
        accountName: account,
        projectName: project,
        environment: archiveMetadata.metadata.branch,
        repositoryUrl: archiveMetadata.metadata.repoUrl,
        sha: archiveMetadata.metadata.sha,
    };
    const uploadUrlResponse = await fetch(`${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/deployments/source-url`, {
        method: "POST",
        headers: {
            Authorization: `Bearer ${argv.authToken}`,
        },
        body: JSON.stringify(payload),
    });
    logger.debug(`Upload URL response: ${uploadUrlResponse.status}`);
    if (uploadUrlResponse.ok) {
        const { uploadUrl, deploymentId } = await uploadUrlResponse.json();
        printDiagnosticsToConsole(`Uploading source for deployment (${deploymentId})...`);
        const uploadResponse = await upload({
            tarballPath: archiveMetadata.tarball,
            uploadUrl,
            deploymentId,
            ...payload,
        });
        logger.debug(`Upload response: ${uploadResponse.status}`);
        if (uploadResponse.ok) {
            const spinner = printSpinnerToConsole(`Deploying the '${payload.environment}' environment to '${project}' on account '${account}'...`);
            const { url, logUrl } = await pollDeployment(argv, deploymentId, account, project, spinner);
            if (url) {
                await printResultToConsoleAndExitGracefully(`Deployed to ${url}`, spinner);
            }
            else {
                await printCriticalFailureToConsoleAndExit(`Failed to deploy the environment ${archiveMetadata.metadata.branch} to project ${project} on account ${account}.\nFor more information, check the deployment logs in the Zuplo dashboard.\n
          ${logUrl}`, spinner);
            }
        }
        else {
            logger.error({
                status: uploadResponse.status,
                statusText: uploadResponse.statusText,
            }, "Failed to upload source to cloud storage");
            printDiagnosticsToConsole("Error: Failed to upload deployment source. Please try again.");
        }
    }
    else {
        logger.error({
            status: uploadUrlResponse.status,
            statusText: uploadUrlResponse.statusText,
        }, "Failed to retrieve uploadUrl");
        await printCriticalFailureToConsoleAndExit("Error: Failed to upload deployment source. Please try again.");
    }
}
async function deployToSelfHosted(argv) {
    const { account, project } = argv;
    let existingZuploEnv;
    try {
        const endpoint = normalizeUrl(argv["self-hosted-endpoint"]);
        existingZuploEnv = retrieveExistingZuploEnv(argv);
        const branch = (await generateMetadata(argv)).branch;
        try {
            const environmentToAutoLink = await retrieveOrCreateEnvironment(branch, argv);
            await pullSystemConfig({
                dir: argv.dir,
                environment: environmentToAutoLink.name,
                authToken: argv.authToken,
            });
        }
        catch (error) {
            if (error instanceof UnableToAutoLinkToExistingProject &&
                process.env.ZUPLO_ALLOW_DEPLOY_WITH_EMPTY_VARS) {
                printWarningToConsole(`We are unable to fetch the environment variables from Zuplo for this project. 
Deployment will proceed because ZUPLO_ALLOW_DEPLOY_WITH_EMPTY_VARS is set. 
However, the environment variables will not be available. 
To fix this, check that the project, ${argv.project} exists and this api-key has access to it.`);
            }
            else if (error instanceof UnableToAutoLinkToExistingProject) {
                await printCriticalFailureToConsoleAndExit(`We are unable to fetch the environment variables from Zuplo for this project. 
To fix this, check that the project, ${argv.project} exists and this api-key has access to it.
If you want to force deployment without the environment variables, set ZUPLO_ALLOW_DEPLOY_WITH_EMPTY_VARS to true.`);
            }
            else {
                logger.error(error, "Failed to fetch environment variables");
                await printCriticalFailureToConsoleAndExit(`We are unable to fetch the environment variables from Zuplo for this project. 
To fix this, check that the project, ${argv.project} exists and this api-key has access to it.`);
            }
        }
        const archiveMetadata = await archive(argv);
        logger.debug(`Tarball created locally at ${archiveMetadata.tarball}`);
        const form = new FormData();
        const deploymentName = `${project}-${getPrettyBranch(archiveMetadata.metadata.branch)}`;
        form.set("file", new Blob([readFileSync(archiveMetadata.tarball)], {
            type: "application/gzip",
        }));
        form.set("projectName", project);
        form.set("deploymentName", deploymentName);
        const uploadUrlResponse = await fetch(`${endpoint}/v1/deployments/build`, {
            method: "POST",
            headers: {
                Authorization: `Bearer ${argv.authToken}`,
            },
            body: form,
        });
        logger.debug(`Upload URL response: ${uploadUrlResponse.status}`);
        if (uploadUrlResponse.ok) {
            const spinner = printSpinnerToConsole(`Deploying the current branch ${archiveMetadata.metadata.branch} to project ${project} on account ${account}...`);
            const { buildName } = await uploadUrlResponse.json();
            logger.debug(`Deployment started for ${buildName}`);
            const buildResult = await pollBuild(argv, endpoint, buildName, spinner);
            if (buildResult.conditionType === "Complete") {
                const deploymentResponse = await fetch(`${endpoint}/v1/deployments/${deploymentName}`, {
                    method: "GET",
                    headers: {
                        Authorization: `Bearer ${argv.authToken}`,
                    },
                });
                const deploymentJSON = await deploymentResponse.json();
                restoreExistingZuploEnvAsNecessary(argv, existingZuploEnv);
                await printResultToConsoleAndExitGracefully(`Deployed to ${deploymentJSON.deploymentUrl}`, spinner);
            }
            else {
                restoreExistingZuploEnvAsNecessary(argv, existingZuploEnv);
                await printCriticalFailureToConsoleAndExit(`Failed to deploy the current environment. Here's the diagnostics: ${JSON.stringify(buildResult)}`, spinner);
            }
        }
        else {
            logger.error(await uploadUrlResponse.text(), "Failed to upload to self-hosted build endpoint");
            await printCriticalFailureToConsoleAndExit("Error: Failed to upload to self-hosted build endpoint");
        }
    }
    catch (error) {
        logger.error(error);
    }
    finally {
        restoreExistingZuploEnvAsNecessary(argv, existingZuploEnv);
    }
}
function retrieveExistingZuploEnv(argv) {
    const dir = argv.dir;
    const normalizedDir = join(relative(process.cwd(), dir));
    const envFilePath = join(normalizedDir, ZUPLO_SYSTEM_ENV_VAR);
    if (existsSync(envFilePath)) {
        const envFileContent = readFileSync(envFilePath, "utf-8");
        return envFileContent;
    }
    else {
        return undefined;
    }
}
function restoreExistingZuploEnvAsNecessary(argv, originalValue) {
    if (originalValue) {
        const dir = argv.dir;
        const normalizedDir = join(relative(process.cwd(), dir));
        const envFilePath = join(normalizedDir, ZUPLO_SYSTEM_ENV_VAR);
        writeFileSync(envFilePath, originalValue);
    }
}
const getPrettyBranch = (branch) => {
    return (branch
        .normalize("NFD")
        .replace(/[\u0300-\u036f]/g, "")
        .replace(/([^\w]+|\s+)/g, "-")
        .replace(/\-\-+/g, "-")
        .replace(/(^-+|-+$)/, "")
        .replaceAll("_", "-")
        .toLowerCase()
        .substring(0, MAX_PRETTY_BRANCH_LENGTH));
};
//# sourceMappingURL=handler.js.map