"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 _ = require("lodash");
const contracts_1 = require("../../apis/codiac-api/contracts");
const contracts_2 = require("@codiac.io/codiac-common/contracts");
const command_base_enterprise_1 = require("../../command-base-enterprise");
/** 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 ConfigAdd extends command_base_enterprise_1.CommandBaseEnterprise {
    run() {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.bootstrap();
            const { args, flags } = yield this.parse(ConfigAdd);
            (_a = this._relayContainer) === null || _a === void 0 ? void 0 : _a.ready$.subscribe((ready) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                var _b, _c, _d, _e;
                try {
                    // let setting = "rootlevel.nextlevel.Top10\\.ServiceHost";
                    // for (let seg of setting.split(/(?<!\\)\./g)) {
                    //   this.logger.notice(seg.replace(/\\\./g, "."))
                    // }
                    // this._ui.prompt("hello", "hello");
                    // this._ops.getConfigRoot();  // Verify that it's being called from within a defined project.
                    // OpsEnterprise.bootstrap(this.ioc, { baseUrl: "" }, false);
                    this._userCntx = yield this.establishTenantContext(this._userCntx);
                    let rootMap = yield this.getRootMap();
                    const interactionMode = this.getInteractionMode(flags.silent, flags.echo, flags['to-script']);
                    let enterprise = (yield this.establishEnterpriseContext(rootMap, flags.enterprise, interactionMode)).enterprise;
                    _.remove(rootMap.enterprises, e => e.name != enterprise.name); // trim down to match legacy conditions
                    let enterpriseCode = enterprise.name;
                    let asset;
                    if (flags.asset !== undefined) {
                        const assetName = flags.asset;
                        let assets = enterprise.assets.filter(a => a.name === assetName);
                        if (!Array.isArray(assets) || assets.length === 0) {
                            const errorMsg = `Error importing: Assets does not exist: ${assetName}`;
                            this.logger.error(errorMsg);
                            this._ui.userMessage(errorMsg);
                            throw new Error(errorMsg);
                        }
                        asset = assets[0]; // There can be only one.
                    }
                    else {
                        let aCntx = yield this.establishAssetContext(enterprise, true);
                        let assets = enterprise.assets.filter(a => aCntx.includes(a.name));
                        asset = assets[0]; // There can be only one.
                    }
                    let cab = yield this.establishCabinet(flags.cabinet, true);
                    // 1. DEFINE THE SOURCE FILE
                    let sourceFileAbs;
                    let sourceFileChanged = false;
                    // Attempt to take the user input
                    let sourceFolder = process.cwd(); // Current dir
                    if (flags.sourceFile && !this.isNullOrWhitespace(flags.sourceFile)) {
                        if (path.isAbsolute(flags.sourceFile)) {
                            if (fs.existsSync(flags.sourceFile)) {
                                sourceFileAbs = flags.sourceFile;
                                sourceFolder = path.dirname(sourceFileAbs); // dir of given file (if it exists)
                            }
                            else {
                                // if (silent) { throw new Error("The given source file was not found.""); } else { ...null it out... } 
                                sourceFileAbs = undefined;
                                sourceFileChanged = true;
                            }
                        }
                        else if (flags.sourceFile == exports.PROMPT_ME_DESIGNATOR) {
                            // if (silent) { throw new Error("Must provide a source file when running in silent mode.""); } 
                        }
                        else {
                            // USERS VALUE WAS a relative path
                            sourceFileAbs = path.posix.join(sourceFolder, flags.sourceFile);
                            if (!fs.existsSync(sourceFileAbs)) {
                                sourceFileAbs = undefined;
                                sourceFileChanged = true;
                            }
                        }
                    }
                    // Prompt for source file
                    if (sourceFileAbs == undefined) {
                        // Step 1a) select the distribution folder (aka: the build output folder)
                        let folderOptions = { startingPath: sourceFolder };
                        sourceFolder = yield this._ui.promptSelectFolder("sourceFolder", "Select the distribution folder (aka: the build output folder)...", folderOptions);
                        // Step 1b) select the config file within it.
                        let options = {
                            startingPath: sourceFolder,
                            extensions: ["json", "yaml", "yml", "ini", "toml", "xml", "config", "properties"],
                            asRelative: false
                        };
                        sourceFileAbs = yield this._ui.promptSelectFile("configFile", "select the file to import", options);
                        // sourceFileAbs = path.posix.join(sourceFolder, relPath);  // TODO: Add an option to get the abs path back from the file selector.
                    }
                    else {
                        this._ui.userMessage("Importing ".concat(this._styler.hilite(sourceFileAbs)));
                    }
                    // 2. TARGET CONFIG
                    let runtimeTargetFile;
                    let configType;
                    if (asset.type == contracts_1.AssetTypesEnum.helm) {
                        configType = contracts_1.ConfigDefTypesEnum.helm;
                    }
                    else {
                        configType = contracts_1.ConfigDefTypesEnum.file;
                        let runtimeAppRootPath = "/app";
                        runtimeAppRootPath = (yield this._ui.prompt("runtimeAppRootPath", "App root within the image", true, runtimeAppRootPath));
                        let runtimeRelPathName = (yield this._ui.prompt("runtimeRelPathName", "Relative pathname to use within the image", true, (_b = flags.runtimePathName) !== null && _b !== void 0 ? _b : path.basename(sourceFileAbs)));
                        runtimeTargetFile = path.posix.join(runtimeAppRootPath, runtimeRelPathName);
                    }
                    // 3. DECLARE THE SCOPE
                    let level = contracts_1.ScopeLevel.enterprise;
                    let scopeName = enterpriseCode;
                    if (flags.enterprise) {
                        level = contracts_1.ScopeLevel.enterprise;
                        scopeName = (_c = flags.enterprise) !== null && _c !== void 0 ? _c : enterpriseCode;
                    }
                    else if (flags.environment) {
                        level = contracts_1.ScopeLevel.environment;
                        scopeName = (_d = flags.environment) !== null && _d !== void 0 ? _d : cab.environment;
                    }
                    else if (flags.cabinet) {
                        level = contracts_1.ScopeLevel.cabinet;
                        scopeName = (_e = flags.cabinet) !== null && _e !== void 0 ? _e : cab.name;
                    }
                    // 4. Run the import 
                    let doit = yield this._ui.promptConfirm("confirmExec", `You are about to overlay settings for [${this._styler.hilite(asset.name)}] in [${this._styler.hilite(runtimeTargetFile !== null && runtimeTargetFile !== void 0 ? runtimeTargetFile : configType.toString())}] at [${this._styler.hilite(level)}: ${this._styler.hilite(scopeName)}] with the file [${this._styler.hilite(sourceFileAbs)}]. \n Continue? `, true);
                    if (doit == true) {
                        let result = yield this.importConfigDoc(sourceFileAbs, enterpriseCode, asset.name, configType, runtimeTargetFile, level, scopeName);
                        if (result.error) {
                            this.logger.error(`Error importing: ${result.message}`, result.error);
                            this._ui.userMessage(`Error importing: ${result.message}`);
                        }
                        else if (result.success) {
                            this.logger.notice(`Imported ${sourceFileAbs}`);
                            this._ui.userMessage(`Imported ${sourceFileAbs}`);
                        }
                        else {
                            this.logger.warn(`Import failed without error: ${result.message}`, result);
                            this._ui.userMessage(`Import failed without error: ${result.message}`);
                        }
                    }
                    else {
                        this._ui.userMessage("Operation cancelled.  No work was done.");
                    }
                    return;
                }
                catch (err) {
                    this.handleError(err);
                    return;
                }
                finally {
                    yield this.closeConnections();
                }
            }));
        });
    }
    /**
     *
     * @param enterprise
     * @param assetName
     * @param configType
     * @param runtimeTargetFile Required only when `ConfigType == ConfigDefTypesEnum.file`
     * @param fqImportFilePath Fully Qualified local pathname to the file being imported
     * @param scopeLevel
     * @param scopeName
     * @returns
     */
    importConfigDoc(fqImportFilePath, enterprise, assetName, configType, runtimeTargetFile, scopeLevel, scopeName) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return new Promise((resolve, reject) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                var _a;
                let result = new contracts_2.Result(false);
                try {
                    this.logger.detail(`Importing config [${fqImportFilePath}] for asset [${assetName}] in enterprise [${enterprise}] at scope [${scopeLevel}:${scopeName}]...`);
                    let contents = fs.readFileSync(fqImportFilePath, { encoding: 'utf-8' }).trim();
                    this.logger.trace(contents);
                    let owner = { enterpriseCode: enterprise, assetName: assetName };
                    let scope = { level: scopeLevel, target: scopeName };
                    // let importer = this.ioc.get<ConfigImporter>(ConfigImporter);
                    // let importer = new ConfigImporter(this.logger, this._codiacApi);
                    // let result = await importer.import(contents.trim(), relativePathName, owner, appRootPathInContainer, scope);
                    // let result = await this.relayClient.configFileImport(contents.trim(), relativePathName, owner.enterpriseCode, owner.assetName, appRootPathInContainer, scope.level, scope.target);
                    const relativePathName = runtimeTargetFile !== null && runtimeTargetFile !== void 0 ? runtimeTargetFile : `[${configType}]`;
                    result = yield this.genRelayClient.Call.ImportConfig({
                        fileContents: contents,
                        relativePathName: runtimeTargetFile !== null && runtimeTargetFile !== void 0 ? runtimeTargetFile : "[".concat(configType, "]"),
                        owner: owner,
                        scope: scope
                    });
                    // this.logger.trace(result.success ? "Success!" : result.message ?? "Failed", result);
                    // resolve(result);
                }
                catch (err) {
                    result.error = err;
                    result.message = "Error importing config: ".concat((_a = err.message) !== null && _a !== void 0 ? _a : err.stack);
                    // reject(err);
                }
                finally {
                    resolve(result);
                }
            }));
        });
    }
    writeSetting_Api(enterprise, assetName, configName, setting, scopeLevel, scopeName, val) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return new Promise((resolve, reject) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                try {
                    // let cntx = await this.loadCodiacContext();
                    // let cntx = await this.establishTenantContext(this._userCntx);
                    let cntx = this._userCntx;
                    // TODO:  Look to enterprise context instead.
                    // let enterprise = /* this._cfg.enterprise || */ this._projCfg.tenantCode;  
                    // TODO: Look to asset context instead - and if none, fail and call for "$ cod asset:set"
                    // let assetName = this._projCfg.name;  // this needs to be taken from an image definition.  (AssetBinding[] needs to be added to each ImageExport)
                    // NOTE:  We could bind a config to an image name, 
                    //        and then allow for an Asset scope to set values on it...
                    let settingObj = Object.assign({ setting: setting, value: val, targetFile: configName, asset: assetName, enterprise: enterprise, scope: scopeLevel, target: scopeName }, this.createAuditable());
                    let crit = {
                        enterprise: settingObj.enterprise,
                        asset: settingObj.asset,
                        targetFile: settingObj.targetFile,
                        scope: scopeLevel,
                        target: scopeName,
                        setting: settingObj.setting
                    };
                    // TODO:  Replace this with an upsert based on:  enterprise, asset, configName, scopeLevel, scopeName, and setting
                    let check = yield this._codiacApi.ScopedSetting.tryReadMany(crit);
                    let resultSetting;
                    if (check.success && check.output && check.output.length > 0) {
                        settingObj.id = check.output[0].id;
                        resultSetting = yield this._codiacApi.ScopedSetting.tryUpdate(settingObj);
                    }
                    else {
                        resultSetting = yield this._codiacApi.ScopedSetting.tryCreate(settingObj);
                    }
                    if (resultSetting.success) {
                        this.logger.notice(`Successfully saved setting [${settingObj.setting}] for ${settingObj.scope} [${settingObj.target}] in ${settingObj.enterprise}/${settingObj.asset} config [${settingObj.targetFile}]`);
                        this._ui.userMessage(`Successfully saved setting [${settingObj.setting}] for ${settingObj.scope} [${settingObj.target}] in ${settingObj.enterprise}/${settingObj.asset} config [${settingObj.targetFile}]`);
                    }
                    else if (resultSetting.error) {
                        this.logger.error(`Error saving setting [${settingObj.setting}]: ${resultSetting.message || ""}; ${resultSetting.error.message || resultSetting.error.stack}; For ${settingObj.scope} [${settingObj.target}] in ${settingObj.enterprise}/${settingObj.asset} config [${settingObj.targetFile}]`);
                        this._ui.userMessage(`Error saving setting [${settingObj.setting}]: ${resultSetting.message || ""}; ${resultSetting.error.message || resultSetting.error.stack}; For ${settingObj.scope} [${settingObj.target}] in ${settingObj.enterprise}/${settingObj.asset} config [${settingObj.targetFile}]`);
                    }
                    else {
                        this.logger.warn(`Failed without error to save setting [${settingObj.setting}]: ${resultSetting.message || ""}; For ${settingObj.scope} [${settingObj.target}] in ${settingObj.enterprise}/${settingObj.asset} config [${settingObj.targetFile}]`);
                        this._ui.userMessage(`Failed without error to save setting [${settingObj.setting}]: ${resultSetting.message || ""}; For ${settingObj.scope} [${settingObj.target}] in ${settingObj.enterprise}/${settingObj.asset} config [${settingObj.targetFile}]`);
                    }
                    resolve();
                }
                catch (err) {
                    reject(err);
                }
            }));
        });
    }
    writeSetting_local(setting, value) {
        // Write the available setting
        if (!this._appConfig) {
            this._appConfig = {};
        }
        _.set(this._appConfig, setting, value);
        // this._appConfig[setting] = val.settingValue;
        this._ops.writeConfig(this._appConfig);
    }
    isNullOrWhitespace(candidate) {
        return candidate === null || (candidate.match(/^\s*$/) !== null);
    }
}
exports.default = ConfigAdd;
ConfigAdd.description = 'Adds a key value pair to the app config';
ConfigAdd.aliases = ['cfg:add'];
ConfigAdd.flags = {
    help: core_1.Flags.help({ char: 'h' }),
    enterprise: core_1.Flags.string({ char: 'n', exclusive: ["environment", "cabinet"], description: 'The name of the enterprise to which this value is to apply.' }),
    environment: core_1.Flags.string({ char: 'e', exclusive: ["enterprise", "cabinet"], description: 'The name of the environment to which this value is to apply.' }),
    asset: core_1.Flags.string({ char: 'a', required: false, description: `Search string for the name of the asset to deploy (use ${exports.PROMPT_ME_DESIGNATOR} to be prompted).` }),
    cabinet: core_1.Flags.string({ char: 'c', exclusive: ["enterprise", "environment"], description: 'The name of the cabinet to which this value is to apply.' }),
    sourceFile: core_1.Flags.string({ char: 's', description: 'The path-name to the LOCAL file to which the settings are written, can be either absolute, or relative to the current working directory.' }),
    runtimePathName: core_1.Flags.string({ char: 'f', required: false, description: '(optional, uses the original filename) The pathname relative to the app root to assume in the container.' })
};
ConfigAdd.args = {};
//# sourceMappingURL=import.js.map