"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPlaywrightTest = getPlaywrightTest;
const mablApi_1 = require("@mablhq-internal/runtime/mablApi");
const mablApiClientFactory_1 = require("@mablhq-internal/runtime/api/mablApiClientFactory");
const cliUtil_1 = require("@mablhq-internal/runtime/core/cliUtil");
const flowResolver_1 = require("@mablhq-internal/runtime/util/flowResolver");
const js_yaml_1 = require("js-yaml");
const constants_1 = require("@mablhq-internal/runtime/core/constants");
const constants_2 = require("../../constants");
const loggingProvider_1 = require("@mablhq-internal/runtime/providers/logging/loggingProvider");
const fileUtil_1 = require("../../commandUtil/fileUtil");
const execution_1 = require("@mablhq-internal/runtime/execution");
const ApiTestUtils_1 = require("@mablhq-internal/runtime/core/execution/ApiTestUtils");
const chalk = require('chalk');
const os = require('os');
const JSON_REPLACER = null;
exports.command = `export <${constants_1.CommandArgId}>`;
exports.describe = 'Export a mabl browser test in a specified file format';
exports.builder = (yargs) => {
    yargs
        .positional(constants_1.CommandArgId, {
        describe: 'test id to export',
        type: 'string',
    })
        .option(constants_1.CommandArgFormat, {
        alias: 'fmt',
        default: 'yaml',
        describe: `Specify a format for the test export`,
        choices: [
            constants_1.OutputFormats.Json,
            constants_1.OutputFormats.Yaml,
            constants_1.OutputFormats.Csv,
            constants_1.OutputFormats.Playwright,
            constants_1.OutputFormats.Postman,
            constants_1.OutputFormats.SeleniumIde,
        ],
    })
        .option(constants_2.CommandArgDetailLevel, {
        default: 'simple',
        hidden: true,
        nargs: 1,
        type: 'string',
    })
        .option(constants_1.CommandArgMablBranch, {
        describe: 'Which branch of the test to export',
        nargs: 1,
        type: 'string',
    })
        .option(constants_1.CommandArgEnvironmentId, {
        alias: 'e',
        describe: 'Generate selectors based on the find information learned in this environment (Playwright only)',
        nargs: 1,
        type: 'string',
    })
        .option(constants_2.CommandArgOutputFilePath, {
        describe: 'Export to specific file, default: local path/export name',
        nargs: 1,
        type: 'string',
    });
};
exports.handler = (0, cliUtil_1.failWrapper)(exportTest);
async function exportTest(parsed) {
    const testId = parsed.id;
    const format = parsed.format;
    const environmentId = parsed[constants_1.CommandArgEnvironmentId];
    const detailLevel = parsed[constants_2.CommandArgDetailLevel];
    const fileName = parsed[constants_2.CommandArgOutputFilePath];
    const apiClient = await mablApiClientFactory_1.MablApiClientFactory.createApiClient();
    const branchName = parsed['mabl-branch'] ?? constants_1.DefaultBranchName;
    const journey = await apiClient.getJourney(testId, branchName, format);
    if (journey.test_type === mablApi_1.TestTypeEnum.Performance) {
        throw new Error('Export functionality is not available for performance tests.');
    }
    const flowArray = await (0, flowResolver_1.getJourneyFlowArray)(journey, apiClient, branchName);
    if (flowArray
        .map((flow) => flow.flow_type === mablApi_1.FlowTypeEnum.Mablscript ||
        flow.flow_type === mablApi_1.FlowTypeEnum.Api ||
        flow.flow_type === mablApi_1.FlowTypeEnum.Mobile)
        .includes(false)) {
        loggingProvider_1.logger.info(chalk.red(`Default mabl tests can not be exported`));
        return;
    }
    const nestedFlows = await (0, flowResolver_1.resolveAllNestedFlowReferences)(flowArray, apiClient, branchName);
    switch (format) {
        case constants_1.OutputFormats.Csv:
            return dumpToCsv(journey, flowArray, nestedFlows, fileName);
        case constants_1.OutputFormats.SeleniumIde:
            if (journey.test_type !== mablApi_1.TestTypeEnum.Browser) {
                throw new Error('Selenium IDE tests can only be exported for browser tests.');
            }
            return dumpToSeleniumIde(journey, flowArray, nestedFlows, fileName);
        case constants_1.OutputFormats.Playwright:
            if (journey.test_type !== mablApi_1.TestTypeEnum.Browser) {
                throw new Error('Playwright tests can only be exported for browser tests.');
            }
            return dumpToPlaywrightTest(journey, flowArray, apiClient, fileName, environmentId, nestedFlows);
        case constants_1.OutputFormats.Postman:
            if (journey.test_type !== mablApi_1.TestTypeEnum.Api) {
                throw new Error('Only API tests can be exported as Postman collections.');
            }
            return dumpToPostmanCollection(journey, flowArray, apiClient, nestedFlows);
        case constants_1.OutputFormats.Json:
        case constants_1.OutputFormats.Yaml:
        default:
            return dumpYamlOrJsonToFile(format, journey, flowArray, nestedFlows, detailLevel, fileName);
    }
}
function dumpToSeleniumIde(journey, flows, nestedFlows, fileName) {
    const selIdeConfig = new execution_1.SelIdeConfigGenerator(journey, flows, nestedFlows);
    const configGenerated = selIdeConfig.generateConfigFile();
    const jsonified = JSON.stringify(configGenerated, JSON_REPLACER, 2);
    return (0, fileUtil_1.writeExportedEntityToFile)(jsonified, 'side', journey.id, fileName);
}
async function getPlaywrightTest(journey, flows, apiClient, template, environmentId, nestedFlows) {
    let findModel = [];
    let findOverrides = [];
    if (journey.test_type !== mablApi_1.TestTypeEnum.Browser) {
        throw new Error('Playwright tests can only be generated for browser tests.');
    }
    if (!environmentId) {
        loggingProvider_1.logger.warn(chalk.yellow('Warning: No', chalk.underline(constants_1.CommandArgEnvironmentId), `specified. XPath selectors will be generated.
        You can take advantage of the find information learned in a specific environment by specifying the environment id with the --${constants_1.CommandArgEnvironmentId} flag.`));
    }
    else {
        findModel = await apiClient.getTestFindModels(journey.invariant_id, environmentId);
        if (!findModel.length) {
            loggingProvider_1.logger.warn(chalk.yellow('Warning: No find information was found on the environment', chalk.underline(environmentId), `XPath selectors will be generated.`));
        }
        findOverrides = await apiClient.getTestOverrides(journey.invariant_id, environmentId);
    }
    const generator = new execution_1.PlaywrightTestGenerator(journey, flows, findModel, findOverrides, nestedFlows);
    return generator.generateTest(template);
}
async function dumpToPlaywrightTest(journey, flows, apiClient, fileName, environmentId, nestedFlows, template) {
    const test = await getPlaywrightTest(journey, flows, apiClient, template ?? execution_1.playwrightExportTestTemplate, environmentId, nestedFlows);
    return (0, fileUtil_1.writeExportedEntityToFile)(test, 'spec.ts', journey.id, fileName);
}
async function dumpToPostmanCollection(test, flows, apiClient, nestedFlows, fileName) {
    const collection = await (0, ApiTestUtils_1.exportPostmanTestAsCollection)(test, flows, (snippetInvariantId) => apiClient.getSnippet(snippetInvariantId), nestedFlows);
    return (0, fileUtil_1.writeExportedEntityToFile)(JSON.stringify(collection, JSON_REPLACER, 2), 'json', test.id, fileName);
}
function dumpYamlOrJsonToFile(type, journey, flows, nestedFlows, detailLevel, fileName) {
    const journeyConfig = new execution_1.JourneyConfig(journey, flows, false, nestedFlows);
    const configGenerated = detailLevel === constants_2.DetailLevelFormats.Full
        ? journeyConfig.generateConfigFile()
        : journeyConfig.generateSimpleFormat();
    switch (type) {
        case constants_1.OutputFormats.Yaml:
            const yaml = (0, js_yaml_1.dump)(configGenerated, { skipInvalid: false });
            return (0, fileUtil_1.writeExportedEntityToFile)(yaml, 'yml', journey.id, fileName);
        case constants_1.OutputFormats.Json:
            return (0, fileUtil_1.writeExportedEntityToFile)(JSON.stringify(configGenerated, JSON_REPLACER, 2), 'json', journey.id, fileName);
        default:
            throw new Error(`Invalid Type supplied for exporting: ${type}`);
    }
}
function dumpToCsv(journey, flows, nestedFlows, fileName) {
    const journeyConfig = new execution_1.JourneyConfig(journey, flows, false, nestedFlows);
    const output = journeyConfig.generateSimpleCsv();
    return (0, fileUtil_1.writeExportedEntityToFile)(output.join(os.EOL), 'csv', journey.id, fileName);
}
