"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.printDeploymentEventResultOutput = printDeploymentEventResultOutput;
exports.getDeploymentEventWebappUrl = getDeploymentEventWebappUrl;
const env_1 = require("@mablhq-internal/runtime/env/env");
const mablApiClientFactory_1 = require("@mablhq-internal/runtime/api/mablApiClientFactory");
const mablApi_1 = require("@mablhq-internal/runtime/mablApi");
const util_1 = require("../../commandUtil/util");
const cliUtil_1 = require("@mablhq-internal/runtime/core/cliUtil");
const branches_1 = require("@mablhq-internal/runtime/util/branches/branches");
const awaitDeploymentCompletion_1 = require("./awaitDeploymentCompletion");
const scmContextProvider_1 = require("@mablhq-internal/runtime/providers/scmContextProvider");
const scmContextProviderV2_1 = require("@mablhq-internal/runtime/providers/scmContextProviderV2");
const cliConfigProvider_1 = require("@mablhq-internal/runtime/providers/cliConfigProvider");
const configKeys_1 = require("@mablhq-internal/runtime/providers/configKeys");
const codeInsights_1 = require("../../commandUtil/codeInsights");
const constants_1 = require("@mablhq-internal/runtime/core/constants");
const constants_2 = require("../../constants");
const describe_1 = require("../../commandUtil/describe");
const loggingProvider_1 = require("@mablhq-internal/runtime/providers/logging/loggingProvider");
const chalk = require('chalk');
const CommandArgAwaitCompletion = 'await-completion';
const CommandArgRebaselineImages = 'rebaseline-images';
const CommandArgRepositoryUrl = 'repository-url';
const CommandArgSetStaticBaseline = 'set-static-baseline';
const CommandArgSnapshotBranch = 'snapshot-from';
exports.command = 'create';
exports.describe = 'Trigger a deployment event via the mabl API';
exports.builder = (yargs) => {
    yargs
        .option(constants_1.CommandArgApplicationId, {
        alias: constants_2.CommandArgAliases.ApplicationId,
        describe: 'Application to run the mabl tests for. If provided, mabl will only trigger plans that contain this application',
        nargs: 1,
        type: 'string',
    })
        .option(constants_1.CommandArgEnvironmentId, {
        alias: constants_2.CommandArgAliases.EnvironmentId,
        describe: 'Environment to run the mabl tests for. If provided, mabl will only trigger plans that contain this environment',
        nargs: 1,
        type: 'string',
    })
        .option(constants_2.CommandArgOverrideEnvironmentId, {
        describe: 'Override environment to run the tests against. Not used to select plans to run. Requires `--application-id`.',
        nargs: 1,
        type: 'string',
    })
        .option(CommandArgAwaitCompletion, {
        describe: 'Block until mabl tests complete.',
        type: 'boolean',
        default: false,
    })
        .option(constants_2.CommandArgFastFailure, {
        describe: `If using \'--${CommandArgAwaitCompletion}\', exit on first failed test`,
        type: 'boolean',
        default: false,
    })
        .implies(constants_2.CommandArgFastFailure, CommandArgAwaitCompletion)
        .option(constants_2.CommandArgSilent, {
        describe: "Don't output runtime status",
        type: 'boolean',
        default: false,
    })
        .option(constants_2.CommandArgOutput, {
        alias: constants_2.CommandArgAliases.OutputType,
        describe: 'Specify final result output format. Implies: --silent',
        choices: constants_2.DefaultOutputFormatChoices,
    })
        .option(constants_1.CommandArgWorkspaceId, {
        alias: constants_2.CommandArgAliases.WorkspaceId,
        describe: 'Workspace containing target environment and/or application',
        nargs: 1,
        type: 'string',
    })
        .option(constants_2.CommandArgBrowsers, {
        alias: constants_2.CommandArgAliases.Browsers,
        describe: 'Space delimited browsers to test against (e.g. "chrome firefox")',
        type: 'array',
        default: [],
        choices: constants_2.BrowserTypeSelections,
    })
        .option(constants_1.CommandArgLabelsInclude, {
        alias: 'l',
        describe: 'Space delimited plan labels. Run plans that match any label.',
        type: 'array',
        default: [],
    })
        .option(constants_2.CommandArgHttpHeaders, {
        describe: 'Space delimited HTTP headers added to browser requests (e.g. "x-header:foo")',
        type: 'array',
        default: [],
    })
        .option(constants_1.CommandArgUrl, {
        alias: constants_2.CommandArgAliases.Url,
        describe: 'URL to use for deployment',
        type: 'string',
    })
        .option(constants_2.CommandArgUrlApp, {
        describe: 'Application/web URL to use for deployment',
        nargs: 1,
        type: 'string',
    })
        .option(constants_1.CommandArgUrlApi, {
        describe: 'API URL to to use for deployment',
        nargs: 1,
        type: 'string',
    })
        .option(constants_2.CommandArgAndroidMobileAppFileId, {
        describe: 'Android mobile app file to use for deployment',
        nargs: 1,
        type: 'string',
    })
        .option(constants_2.CommandArgiOSdMobileAppFileId, {
        describe: 'iOS mobile app file to use for deployment',
        nargs: 1,
        type: 'string',
    })
        .option(constants_2.CommandArgApiKey, {
        alias: constants_2.CommandArgAliases.ApiKey,
        describe: 'API key (found in the mabl web app)',
        nargs: 1,
        type: 'string',
    })
        .option(CommandArgRebaselineImages, {
        alias: 'rbi',
        describe: 'Rebaseline visual change models',
        type: 'boolean',
        default: false,
    })
        .option(CommandArgSetStaticBaseline, {
        alias: 'ssbi',
        describe: 'Set run as fixed baseline for visual change models',
        type: 'boolean',
        default: false,
    })
        .option(constants_2.CommandArgRevision, {
        alias: constants_2.CommandArgAliases.Revision,
        describe: 'Code revision hash (application under test)',
        nargs: 1,
        type: 'string',
    })
        .option(CommandArgRepositoryUrl, {
        describe: 'Code repository URL (e.g. git@github.com:foo/bar.git)',
        nargs: 1,
        type: 'string',
    })
        .option(constants_1.CommandArgMablBranch, {
        describe: 'mabl branch to run tests against',
        nargs: 1,
        type: 'string',
    })
        .option(constants_2.CommandArgMablAutoBranch, {
        describe: 'Create a mabl branch when a mabl-branch target is specified that does not exist',
        type: 'boolean',
        default: false,
    })
        .option(CommandArgSnapshotBranch, {
        describe: 'Snapshot the current state of the tests from a mabl branch onto the environment. Use "master" for the mainline branch.  Requires setting environment-id.',
        nargs: 1,
        type: 'string',
    })
        .option('target-runtime-version', {
        hidden: true,
        nargs: 1,
        type: 'string',
    })
        .option('runner-type', {
        hidden: true,
        nargs: 1,
        type: 'string',
        choices: [
            mablApi_1.PlanOverrideOptions.NodejsRuntimeVariantEnum.Playwright,
            mablApi_1.PlanOverrideOptions.NodejsRuntimeVariantEnum.Puppeteer,
        ],
    })
        .option('debug', {
        hidden: true,
        type: 'boolean',
        default: false,
    })
        .implies(constants_2.CommandArgMablAutoBranch, constants_1.CommandArgMablBranch)
        .implies(CommandArgSnapshotBranch, constants_1.CommandArgEnvironmentId)
        .implies(constants_2.CommandArgOverrideEnvironmentId, constants_1.CommandArgApplicationId)
        .check((argv) => {
        if (!(argv[constants_1.CommandArgApplicationId] || argv[constants_1.CommandArgEnvironmentId])) {
            throw new Error(`At least one of ${constants_1.CommandArgApplicationId} or ${constants_1.CommandArgEnvironmentId} required`);
        }
        const branchName = argv[constants_1.CommandArgMablBranch];
        if (branchName !== undefined && !(0, branches_1.isValidMablBranchName)(branchName)) {
            throw new Error(`[${branchName}] is not a valid mabl branch name. Please specify a valid name`);
        }
        const snapshotFromBranch = argv[CommandArgSnapshotBranch];
        if (snapshotFromBranch !== undefined &&
            !(0, branches_1.isValidMablBranchName)(snapshotFromBranch)) {
            throw new Error(`[${snapshotFromBranch}] is not a valid mabl branch name to snapshot from. Please specify a valid name`);
        }
        (0, util_1.validateArrayInputs)(argv[constants_2.CommandArgBrowsers], 'browser names must be SPACE delimited, e.g. "--browsers=chrome firefox"');
        (0, util_1.validateArrayInputs)(argv[constants_1.CommandArgLabelsInclude], 'label names must be SPACE delimited, e.g. "--labels=foo bar"');
        (0, util_1.validateArrayInputs)(argv[constants_2.CommandArgHttpHeaders], 'HTTP headers must be SPACE delimited, e.g. "--http-headers="foo:bar" "baz:qux"');
        const httpHeaders = argv[constants_2.CommandArgHttpHeaders];
        argv[constants_2.CommandArgHttpHeaders] = (0, util_1.validateValuePairInputs)('HTTP header', httpHeaders);
        const repositoryUrl = argv['repository-url'];
        if (repositoryUrl !== undefined && repositoryUrl.trim() === '') {
            throw new Error(`repository-url cannot be set to empty value`);
        }
        return true;
    });
};
exports.handler = (0, cliUtil_1.failWrapper)(createDeployment);
async function createDeployment(parsed) {
    const apiClient = await mablApiClientFactory_1.MablApiClientFactory.createApiClientFromOptionalApiKey(parsed['api-key']);
    if (parsed.output) {
        parsed.silent = true;
    }
    if (!parsed.silent) {
        loggingProvider_1.logger.info('Creating Deployment...');
    }
    const applicationId = parsed['application-id'];
    const environmentId = parsed['environment-id'];
    const workspaceId = await (0, util_1.getWorkspaceIdFromAppOrEnv)(apiClient, applicationId, environmentId);
    const environmentOverride = parsed[constants_2.CommandArgOverrideEnvironmentId];
    let deploymentIds;
    if (environmentOverride) {
        deploymentIds = (await apiClient.queryDeploymentEntities(workspaceId, environmentOverride, applicationId)).deployments?.map((deployment) => deployment.id);
        if (!deploymentIds || deploymentIds.length === 0) {
            loggingProvider_1.logger.error(chalk.red.bold(`Application ${applicationId} is not defined in override environment ${environmentOverride}`));
            process.exit(1);
        }
    }
    const androidMobileAppFileId = parsed[constants_2.CommandArgAndroidMobileAppFileId];
    if (!!androidMobileAppFileId) {
        await apiClient.getMobileAppFile(androidMobileAppFileId);
    }
    const iOSMobileAppFileId = parsed[constants_2.CommandArgiOSdMobileAppFileId];
    if (!!iOSMobileAppFileId) {
        await apiClient.getMobileAppFile(iOSMobileAppFileId);
    }
    const mablBranch = parsed['mabl-branch'];
    await (0, branches_1.checkBranchNameAndAutoBranchMaybe)(workspaceId, apiClient, mablBranch, parsed['auto-branch']);
    const snapshotFromBranch = parsed['snapshot-from'];
    const httpHeaders = parsed['http-headers'];
    const scmMetadata = await attemptSourceControlMetadataCollection(parsed);
    const deploymentEvent = await apiClient.postDeploymentEvent({
        browserTypes: parsed.browsers,
        labels: parsed.labels,
        rebaselineImages: parsed['rebaseline-images'],
        setStaticBaseline: parsed['set-static-baseline'],
        applicationId,
        httpHeaders,
        environmentId,
        uri: parsed.url,
        apiUrl: parsed.apiUrl,
        appUrl: parsed.appUrl,
        androidMobileAppFileId,
        iOSMobileAppFileId,
        mablBranch,
        snapshotFromBranch,
        sourceControlMetadata: scmMetadata,
        deploymentIds,
        targetRuntimeVersion: parsed['target-runtime-version'],
        debug: parsed.debug,
    });
    const outputWebappLink = getDeploymentEventWebappUrl(deploymentEvent);
    if (!parsed.output) {
        loggingProvider_1.logger.info(chalk.magenta.bold(`Deployment triggered. View output at: ${outputWebappLink}`));
    }
    if (!parsed['await-completion']) {
        return printDeploymentEventResultOutput(deploymentEvent.id, outputWebappLink, parsed.output);
    }
    const waiter = new awaitDeploymentCompletion_1.AwaitDeploymentCompletion(apiClient, parsed['fast-failure'], parsed.silent);
    const pollingResult = await waiter.awaitCompletion(deploymentEvent.id);
    const isSuccess = pollingResult?.newEntity?.event_status?.succeeded || false;
    try {
        await (0, codeInsights_1.maybeOutputToBitbucket)(pollingResult.newEntity, deploymentEvent.id, outputWebappLink);
    }
    catch (error) {
        loggingProvider_1.logger.info(chalk.yellow.bold('Unable to generate code insights due to BitBucket integration API error'));
        loggingProvider_1.logger.info(`details: ${error}`);
    }
    const textOutput = printDeploymentEventResultOutput(deploymentEvent.id, outputWebappLink, parsed.output, pollingResult?.newEntity);
    if (!isSuccess) {
        process.exit(1);
    }
    return textOutput;
}
function printDeploymentEventResultOutput(deploymentEventId, outputWebappLink, outputMode, executionResult) {
    switch (outputMode) {
        case constants_1.OutputFormats.Json:
        case constants_1.OutputFormats.Yaml:
            const resultPayload = {
                id: deploymentEventId,
                href: outputWebappLink,
                execution_results: executionResult,
            };
            return (0, describe_1.outputEntity)(resultPayload, outputMode);
        default:
            return outputWebappLink;
    }
}
async function attemptSourceControlMetadataCollection(parsed) {
    const userSuppliedRepositoryUrl = parsed['repository-url'];
    if (await cliConfigProvider_1.CliConfigProvider.getConfigProperty(configKeys_1.configKeys.enableSourceControlMetadataCollection)) {
        const buildInfo = await new scmContextProvider_1.ScmContextProvider(process.env).extractScmAndBuildInfo();
        const sourceControlBranch = buildInfo?.scmInfo?.branchName || buildInfo?.buildInfo?.branchName;
        const sourceControlTag = buildInfo?.scmInfo?.tagName || buildInfo?.buildInfo?.tagName;
        const sourceControlRepoUrl = userSuppliedRepositoryUrl ||
            buildInfo?.scmInfo?.repoUrl ||
            buildInfo?.buildInfo?.repoUrl;
        const sourceControlRepoName = buildInfo?.scmInfo?.repoName || buildInfo?.buildInfo?.repoName;
        const revision = parsed.revision ||
            buildInfo?.scmInfo?.commitSha ||
            buildInfo?.buildInfo?.commitSha ||
            buildInfo?.buildInfo?.revision;
        return {
            branchName: sourceControlBranch,
            tag: sourceControlTag,
            revision,
            repoUrl: sourceControlRepoUrl,
            repoName: sourceControlRepoName,
        };
    }
    const buildInfo = new scmContextProviderV2_1.ScmContextProviderV2(process.env).extractScmAndBuildInfo();
    if (buildInfo) {
        const sourceControlBranch = buildInfo?.buildInfo?.branchName;
        const sourceControlTag = buildInfo?.buildInfo?.tagName;
        const sourceControlRepoUrl = userSuppliedRepositoryUrl ?? buildInfo?.buildInfo?.repoUrl;
        const sourceControlRepoName = buildInfo?.buildInfo?.repoName;
        const revision = parsed.revision ??
            buildInfo?.buildInfo?.commitSha ??
            buildInfo?.buildInfo?.revision ??
            buildInfo?.buildInfo?.revisionId;
        return {
            branchName: sourceControlBranch,
            tag: sourceControlTag,
            revision,
            repoUrl: sourceControlRepoUrl,
            repoName: sourceControlRepoName,
            integrationType: buildInfo?.buildInfo?.buildType,
            buildInfoUrl: buildInfo?.buildInfo?.buildInfoUrl,
            buildId: buildInfo?.buildInfo?.buildId,
        };
    }
    return {
        revision: parsed.revision,
        repoUrl: userSuppliedRepositoryUrl,
    };
}
function getDeploymentEventWebappUrl(deploymentEvent) {
    return `${env_1.BASE_APP_URL}/workspaces/${deploymentEvent.workspace_id}/events/${deploymentEvent.id}`;
}
