"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PROMPT_ME_DESIGNATOR = void 0;
const tslib_1 = require("tslib");
const fs = require("fs");
const path = require("path");
const core_1 = require("@oclif/core");
const contracts_1 = require("../../apis/codiac-api/contracts");
const command_base_exec_1 = require("../../command-base-exec");
const config_file_target_1 = require("../../uilogic/config-file-target");
/** The value provided as input that indicates
 * a user's desire to be prompted with a list from which choose the value(s),
 * instead of entering them manually or taking a default. */
exports.PROMPT_ME_DESIGNATOR = "?";
class ConfigImport extends command_base_exec_1.CommandBaseExec {
    captureArgs() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { flags } = yield this.parse(ConfigImport);
            let partial = {};
            // Note: --enterprise (char 'n') is the scope flag here, not the enterprise context.
            // The enterprise context is resolved by the base class lifecycle using partial.enterprise.
            // If the user provides --enterprise, it doubles as both scope and context.
            if (flags.enterprise)
                partial.enterprise = flags.enterprise;
            return [partial, false];
        });
    }
    buildCandidate_silently(partial, rootMap) {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { flags } = yield this.parse(ConfigImport);
            if (!flags.asset)
                throw new Error("Asset name (--asset or -a) is required in silent mode.");
            if (!flags.sourceFile)
                throw new Error("Source file (--sourceFile or -s) is required in silent mode.");
            const ent = rootMap.enterprises.find(e => e.name === partial.enterprise);
            if (!ent)
                throw new Error(`Enterprise "${partial.enterprise}" not found.`);
            const assets = ent.assets.filter(a => a.name === flags.asset);
            if (!assets || assets.length === 0)
                throw new Error(`Asset "${flags.asset}" not found in enterprise "${partial.enterprise}".`);
            const asset = assets[0];
            const sourceFileAbs = path.isAbsolute(flags.sourceFile) ? flags.sourceFile : path.posix.join(process.cwd(), flags.sourceFile);
            if (!fs.existsSync(sourceFileAbs))
                throw new Error(`Source file not found: ${sourceFileAbs}`);
            const configType = asset.type === contracts_1.AssetTypesEnum.helm ? contracts_1.ConfigDefTypesEnum.helm : contracts_1.ConfigDefTypesEnum.file;
            let runtimeTargetFile;
            if (configType === contracts_1.ConfigDefTypesEnum.file) {
                const runtimePathName = (_a = flags["runtime-path"]) !== null && _a !== void 0 ? _a : path.basename(sourceFileAbs);
                runtimeTargetFile = path.posix.join("/app", runtimePathName);
            }
            let scopeLevel = contracts_1.ScopeLevel.enterprise;
            let scopeName = partial.enterprise;
            if (flags.enterprise) {
                scopeLevel = contracts_1.ScopeLevel.enterprise;
                scopeName = flags.enterprise;
            }
            else if (flags.environment) {
                scopeLevel = contracts_1.ScopeLevel.environment;
                scopeName = flags.environment;
            }
            else if (flags.cabinet) {
                scopeLevel = contracts_1.ScopeLevel.cabinet;
                scopeName = flags.cabinet;
            }
            return { enterprise: partial.enterprise, assetName: asset.name, fqImportFilePath: sourceFileAbs, configType, runtimeTargetFile, scopeLevel, scopeName };
        });
    }
    buildCandidate_interactively(partial, rootMap, takeDefaults) {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { flags } = yield this.parse(ConfigImport);
            const ent = rootMap.enterprises.find(e => e.name === partial.enterprise);
            if (!ent)
                throw new Error(`Enterprise "${partial.enterprise}" not found.`);
            // Determine asset
            let asset;
            if (flags.asset !== undefined) {
                const assets = ent.assets.filter(a => a.name === flags.asset);
                if (!Array.isArray(assets) || assets.length === 0)
                    throw new Error(`Asset "${flags.asset}" not found in enterprise "${partial.enterprise}".`);
                asset = assets[0];
            }
            else {
                const aCntx = yield this.establishAssetContext(ent, true);
                const assets = ent.assets.filter(a => aCntx.includes(a.name));
                asset = assets[0];
            }
            const cab = yield this.establishCabinet(flags.cabinet, true);
            // Determine source file
            let sourceFileAbs;
            let sourceFolder = process.cwd();
            if (flags.sourceFile && !this.isNullOrWhitespace(flags.sourceFile)) {
                if (path.isAbsolute(flags.sourceFile)) {
                    if (fs.existsSync(flags.sourceFile)) {
                        sourceFileAbs = flags.sourceFile;
                        sourceFolder = path.dirname(sourceFileAbs);
                    }
                }
                else if (flags.sourceFile !== exports.PROMPT_ME_DESIGNATOR) {
                    const candidate = path.posix.join(sourceFolder, flags.sourceFile);
                    if (fs.existsSync(candidate)) {
                        sourceFileAbs = candidate;
                    }
                }
            }
            if (sourceFileAbs === undefined) {
                const folderOptions = { startingPath: sourceFolder };
                sourceFolder = yield this._ui.promptSelectFolder("sourceFolder", "Select the distribution folder (aka: the build output folder)...", folderOptions);
                const fileOptions = {
                    startingPath: sourceFolder,
                    extensions: ["json", "yaml", "yml", "ini", "toml", "xml", "config", "properties"],
                    asRelative: false
                };
                sourceFileAbs = yield this._ui.promptSelectFile("configFile", "Select the file to import", fileOptions);
            }
            else {
                this._ui.userMessage("Importing ".concat(this._styler.hilite(sourceFileAbs)));
            }
            // Determine config type and target path
            const configType = asset.type === contracts_1.AssetTypesEnum.helm ? contracts_1.ConfigDefTypesEnum.helm : contracts_1.ConfigDefTypesEnum.file;
            let runtimeTargetFile;
            if (configType === contracts_1.ConfigDefTypesEnum.file) {
                let runtimeAppRootPath = "/app";
                runtimeAppRootPath = (yield this._ui.prompt("runtimeAppRootPath", "App root within the image", true, runtimeAppRootPath));
                const runtimeRelPathName = (yield this._ui.prompt("runtimeRelPathName", "Relative pathname to use within the image", true, (_a = flags["runtime-path"]) !== null && _a !== void 0 ? _a : path.basename(sourceFileAbs)));
                runtimeTargetFile = path.posix.join(runtimeAppRootPath, runtimeRelPathName);
            }
            // Determine scope
            let scopeLevel = contracts_1.ScopeLevel.enterprise;
            let scopeName = partial.enterprise;
            if (flags.enterprise) {
                scopeLevel = contracts_1.ScopeLevel.enterprise;
                scopeName = flags.enterprise;
            }
            else if (flags.environment) {
                scopeLevel = contracts_1.ScopeLevel.environment;
                scopeName = flags.environment;
            }
            else if (flags.cabinet) {
                scopeLevel = contracts_1.ScopeLevel.cabinet;
                scopeName = flags.cabinet;
            }
            else {
                const choices = [
                    { name: `Anywhere in the ${this._styler.bold(partial.enterprise)} enterprise`, value: contracts_1.ScopeLevel.enterprise },
                    { name: `A specific ${this._styler.bold("environment")}`, value: contracts_1.ScopeLevel.environment },
                    { name: `A specific ${this._styler.bold("cabinet")}`, value: contracts_1.ScopeLevel.cabinet },
                ];
                const selected = yield this._ui.promptSelect("level", "Scope: Where should this value apply? ", choices);
                if (selected)
                    scopeLevel = selected;
                if (scopeLevel !== contracts_1.ScopeLevel.enterprise) {
                    let scopeChoices = [];
                    if (scopeLevel === contracts_1.ScopeLevel.environment) {
                        const resultenvs = yield this._codiacApi.Environment.tryReadMany({ all: true }, { projection: { pathsIncluded: ["code"], pathsExcluded: [] } });
                        if (resultenvs.error)
                            throw resultenvs.error;
                        if (resultenvs.output)
                            scopeChoices = resultenvs.output.map(e => ({ name: e.code, value: e.code }));
                    }
                    else if (scopeLevel === contracts_1.ScopeLevel.cabinet) {
                        const resultCabs = yield this._codiacApi.Cabinet.tryReadMany({ enterpriseCode: partial.enterprise }, { projection: { pathsIncluded: ["name"], pathsExcluded: [] } });
                        if (resultCabs.error)
                            throw resultCabs.error;
                        if (resultCabs.output)
                            scopeChoices = resultCabs.output.map(c => ({ name: c.name, value: c.name }));
                    }
                    const selectedScope = yield this._ui.promptSelect("scopeName", `Which ${scopeLevel}? `, scopeChoices, true);
                    if (selectedScope)
                        scopeName = selectedScope;
                }
            }
            const configLabel = config_file_target_1.ConfigFileTarget.encodeOrFileOrThrow(configType, runtimeTargetFile);
            const proceed = yield this._ui.promptConfirm("confirmExec", `You are about to overlay settings for [${this._styler.hilite(asset.name)}] in [${this._styler.hilite(configLabel)}] at [${this._styler.hilite(scopeLevel)}: ${this._styler.hilite(scopeName)}] with file [${this._styler.hilite(sourceFileAbs)}]. Continue?`, true);
            if (!proceed) {
                this._ui.userMessage("Operation cancelled. No work was done.");
                return undefined;
            }
            return { enterprise: partial.enterprise, assetName: asset.name, fqImportFilePath: sourceFileAbs, configType, runtimeTargetFile, scopeLevel, scopeName };
        });
    }
    buildCommandString(candidate, cmd) {
        cmd.push("-a", candidate.assetName);
        cmd.push("-s", candidate.fqImportFilePath);
        if (candidate.scopeLevel === contracts_1.ScopeLevel.enterprise)
            cmd.push("-n", candidate.scopeName);
        else if (candidate.scopeLevel === contracts_1.ScopeLevel.environment)
            cmd.push("-e", candidate.scopeName);
        else if (candidate.scopeLevel === contracts_1.ScopeLevel.cabinet)
            cmd.push("-c", candidate.scopeName);
        if (candidate.runtimeTargetFile)
            cmd.push("-f", path.basename(candidate.runtimeTargetFile));
        cmd.push("--silent");
        return cmd;
    }
    executeTask(candidate) {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.logger.detail(`Importing config [${candidate.fqImportFilePath}] for asset [${candidate.assetName}] in enterprise [${candidate.enterprise}] at scope [${candidate.scopeLevel}:${candidate.scopeName}]...`);
            const contents = fs.readFileSync(candidate.fqImportFilePath, { encoding: 'utf-8' }).trim();
            const owner = { enterpriseCode: candidate.enterprise, assetName: candidate.assetName };
            const scope = { level: candidate.scopeLevel, target: candidate.scopeName };
            const relativePathName = config_file_target_1.ConfigFileTarget.encodeOrFileOrThrow(candidate.configType, candidate.runtimeTargetFile);
            const result = yield this.genRelayClient.Call.ImportConfig({
                fileContents: contents,
                relativePathName,
                owner,
                scope
            });
            if (result.error) {
                this.logger.error(`Error importing: ${result.message}`, result.error);
                this._ui.userMessage(`Error importing: ${result.message}`);
                throw result.error;
            }
            else if (result.success) {
                this.logger.notice(`Imported ${candidate.fqImportFilePath}`);
                this._ui.userMessage(`Imported ${candidate.fqImportFilePath}`);
            }
            else {
                this.logger.warn(`Import failed without error: ${result.message}`, result);
                this._ui.userMessage(`Import failed without error: ${result.message}`);
                throw new Error((_a = result.message) !== null && _a !== void 0 ? _a : "Import failed.");
            }
        });
    }
    isNullOrWhitespace(candidate) {
        return candidate === null || (candidate.match(/^\s*$/) !== null);
    }
}
exports.default = ConfigImport;
ConfigImport.description = 'Imports a local config file, overlaying its settings into the Codiac config store.';
ConfigImport.aliases = ['cfg:add'];
ConfigImport.flags = {
    help: core_1.Flags.help({ char: 'h' }),
    enterprise: core_1.Flags.string({ char: 'n', exclusive: ["environment", "cabinet"], description: 'The name of the enterprise scope to which this import applies.' }),
    environment: core_1.Flags.string({ char: 'e', exclusive: ["enterprise", "cabinet"], description: 'The name of the environment scope to which this import applies.' }),
    asset: core_1.Flags.string({ char: 'a', required: false, description: `Name of the asset for which to import the config (use ${exports.PROMPT_ME_DESIGNATOR} to be prompted).` }),
    cabinet: core_1.Flags.string({ char: 'c', exclusive: ["enterprise", "environment"], description: 'The name of the cabinet scope to which this import applies.' }),
    sourceFile: core_1.Flags.string({ char: 's', description: 'The local path of the file to import (absolute, or relative to the current working directory).' }),
    "runtime-path": core_1.Flags.string({ char: 'f', required: false, description: 'The pathname relative to the app root to assume in the container (optional; defaults to the source filename).' }),
    silent: core_1.Flags.boolean({ exclusive: ["echo", "to-script"], description: 'Executes without any user interaction; fails on missing or invalid arguments.' }),
    echo: core_1.Flags.boolean({ exclusive: ["to-script", "silent"], description: 'Renders the equivalent non-interactive command for future use before executing.' }),
    "to-script": core_1.Flags.boolean({ exclusive: ["echo", "silent"], description: 'Renders the equivalent non-interactive command without executing it.' }),
};
ConfigImport.args = {};
ConfigImport.examples = [
    { description: "Import a config file interactively.", command: "<%= config.bin %> <%= command.id %>" },
    { description: "Import a specific file at enterprise scope.", command: "<%= config.bin %> <%= command.id %> -a myApi -s ./dist/app-config.json -n myEnterprise" },
    { description: "Import at cabinet scope.", command: "<%= config.bin %> <%= command.id %> -a myApi -s ./dist/app-config.json -c dev" },
    { description: "Non-interactive (scripted) invocation.", command: "<%= config.bin %> <%= command.id %> -a myApi -s ./dist/app-config.json -n myEnterprise --silent" },
    { description: "Renders the equivalent silent command without executing.", command: "<%= config.bin %> <%= command.id %> -a myApi -s ./dist/app-config.json -n myEnterprise --to-script" },
];
//# sourceMappingURL=import.js.map