"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const core_1 = require("@oclif/core");
const _ = require("lodash");
const contracts_1 = require("@codiac.io/codiac-common/contracts");
const command_base_exec_1 = require("../../../command-base-exec");
const prettify_1 = require("../../../prettify");
class AssetVolumeCreate extends command_base_exec_1.CommandBaseExec {
    captureArgs() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { args, flags } = yield this.parse(AssetVolumeCreate);
            let partial = {};
            if (flags.enterprise)
                partial.enterprise = flags.enterprise;
            if (flags.asset)
                partial.assetName = flags.asset;
            if (args.mountPath)
                partial.mountPath = args.mountPath;
            if (flags.name)
                partial.mountName = flags.name;
            if (flags.description)
                partial.description = flags.description;
            return [partial, false];
        });
    }
    buildCandidate_silently(partial, rootMap) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const ent = rootMap.enterprises.find(e => e.name === partial.enterprise);
            if (!ent)
                throw new Error(`Enterprise not found: ${partial.enterprise}`);
            if (!partial.assetName)
                throw new Error("An asset name must be provided.");
            const asset = ent.assets.find(a => a.name === partial.assetName);
            if (!asset)
                throw new Error(`Asset not found: ${partial.assetName}`);
            if (!partial.mountPath)
                throw new Error("A mount path must be provided.");
            if (!partial.mountName)
                throw new Error("A name must be provided for the volume mount.");
            return {
                enterprise: partial.enterprise,
                assetName: asset.name,
                asset,
                mountPath: partial.mountPath,
                mountName: partial.mountName,
                description: partial.description,
            };
        });
    }
    buildCandidate_interactively(partial, rootMap, takeDefaults) {
        var _a, _b, _c, _d, _e, _f;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const ent = rootMap.enterprises.find(e => e.name === partial.enterprise);
            // Asset
            // let assetName: string | undefined = partial.assetName;
            // if (!assetName) {
            //   const choices = ent.assets.map(a => ({ name: a.name, value: a.name }));
            //   assetName = (await this._ui.promptSelectFuzzy("asset", "Asset:", async () => choices))!;
            // }
            let [assetName] = yield this.promptForAsset(ent.assets, partial.assetName);
            const asset = ent.assets.find(a => a.name === assetName);
            // Mount path, name, description — loop until confirmed
            let mountPath = (_a = partial.mountPath) !== null && _a !== void 0 ? _a : '';
            let mountName = partial.mountName;
            let desc = partial.description;
            let confirmed = false;
            let folderRes;
            while (!confirmed || !folderRes) {
                const pathRevisit = (folderRes != undefined);
                const nameRevisit = (folderRes != undefined);
                const descRevisit = (folderRes != undefined);
                // Mount path
                let existingByPath = (_b = asset === null || asset === void 0 ? void 0 : asset.reservedFolders) === null || _b === void 0 ? void 0 : _b.find(f => f.path == mountPath);
                while (existingByPath || !mountPath || pathRevisit) {
                    if (existingByPath)
                        this._ui.userMessage(`The path [${mountPath}] is already in use by the volume mount [${existingByPath.name}] ("${existingByPath.description}").`);
                    mountPath = (yield this._ui.prompt("mountPath", `Enter a mountPath for the volume: `, true, mountPath || undefined));
                    existingByPath = (_c = asset === null || asset === void 0 ? void 0 : asset.reservedFolders) === null || _c === void 0 ? void 0 : _c.find(f => f.path == mountPath);
                    break; // pathRevisit only triggers one re-prompt per outer loop iteration
                }
                // Mount name
                if (mountName == undefined) {
                    const ptn = /(^[\/\s]+|[\/\s]+$)/g;
                    const defaultCandidate = mountPath.replace(ptn, "").replace(/\//g, "-");
                    if (!((_d = asset.reservedFolders) === null || _d === void 0 ? void 0 : _d.find(f => f.name == defaultCandidate))) {
                        mountName = defaultCandidate;
                    }
                }
                let existingByName = (_e = asset.reservedFolders) === null || _e === void 0 ? void 0 : _e.find(f => f.name == mountName);
                while (mountName == undefined || existingByName || nameRevisit) {
                    if (existingByName)
                        this._ui.userMessage(`The name [${mountName}] already refers to the mount folder [${existingByName.path}] ("${existingByName.description}").`);
                    mountName = (yield this._ui.prompt("name", `Refer to this mount folder as: `, true, mountName));
                    existingByName = (_f = asset.reservedFolders) === null || _f === void 0 ? void 0 : _f.find(f => f.name == mountName);
                    break; // nameRevisit only triggers one re-prompt per outer loop iteration
                }
                // Description
                if (desc == undefined || descRevisit) {
                    desc = (yield this._ui.prompt("desc", `Describe the intended purpose for this volume mount: `, false, desc));
                }
                folderRes = { name: mountName, path: mountPath };
                if (desc)
                    folderRes.description = desc;
                this._ui.userMessage((0, prettify_1.prettify)(folderRes));
                confirmed = yield this._ui.promptConfirm("confirm", "You are about to set this path as a volume mount.  Continue?", true);
            }
            return {
                enterprise: partial.enterprise,
                assetName: asset.name,
                asset,
                mountPath,
                mountName: mountName,
                description: desc,
            };
        });
    }
    buildCommandString(candidate, cmd) {
        cmd.push("-e", candidate.enterprise);
        cmd.push("-a", candidate.assetName);
        cmd.push(candidate.mountPath);
        cmd.push("--name", candidate.mountName);
        if (candidate.description)
            cmd.push("--description", candidate.description);
        cmd.push("--silent");
        return cmd;
    }
    executeTask(candidate) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const folderRes = { name: candidate.mountName, path: candidate.mountPath };
            if (candidate.description)
                folderRes.description = candidate.description;
            const resultPatch = yield this.writeVolumeMountToAsset(candidate.enterprise, candidate.asset, folderRes);
            if (resultPatch.success) {
                this.logger.notice(`Successfully saved volume mount [${folderRes.name}] with path [${folderRes.path}] to asset [${candidate.asset.enterprise}/${candidate.asset.name}]`);
                this._ui.userMessage(`Successfully saved volume mount [${folderRes.name}] with path [${folderRes.path}] to asset [${candidate.asset.enterprise}/${candidate.asset.name}]`);
            }
            else if (resultPatch.error) {
                this.logger.error(`Error saving volume mount: ${resultPatch.message || ""}; ${resultPatch.error.message || resultPatch.error.stack}`);
                this._ui.userMessage(`Error saving volume mount [${folderRes.name}] with path [${folderRes.path}] to asset [${candidate.asset.enterprise}/${candidate.asset.name}]: ${resultPatch.message || ""}; ${resultPatch.error.message || resultPatch.error.stack}`);
            }
            else {
                this._ui.userMessage(`Failed without error to save volume mount [${folderRes.name}] with path [${folderRes.path}] to asset [${candidate.asset.enterprise}/${candidate.asset.name}]: ${resultPatch.message || ""}`);
            }
        });
    }
    promptForAsset(assets, current) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let selected = current;
            let changed = false;
            if (assets.length === 0) {
                throw new Error("This enterprise has no assets. You must create an asset to run this command.");
            }
            if (selected != undefined) {
                let found = assets.find(x => x.name === selected);
                if (found == undefined) {
                    selected = undefined;
                }
            }
            if (selected == undefined) {
                if (assets.length === 1) {
                    selected = assets[0].name;
                }
                else {
                    let choices = assets.map(a => { return { name: a.name, value: a.name }; });
                    selected = (yield this._ui.promptSelect("asset", "Asset: ", choices));
                }
                changed = Boolean(current != undefined);
            }
            return [selected, changed];
        });
    }
    writeVolumeMountToAsset(enterprise, asset, folderRes) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                if (!asset.reservedFolders)
                    asset.reservedFolders = [];
                asset.reservedFolders.push(folderRes);
                asset.reservedFolders = _.sortBy(asset.reservedFolders, f => f.path);
                this.logger.trace("Asset update", asset);
                return yield this._codiacApi.Asset.tryUpdate(asset);
            }
            catch (err) {
                const result = new contracts_1.SingleResult(false);
                result.message = `Error updating asset ${asset.name}: ${err.message}`;
                result.error = err;
                return result;
            }
        });
    }
}
exports.default = AssetVolumeCreate;
AssetVolumeCreate.description = 'Declares a named path for mounting a volume to instances of the asset.';
AssetVolumeCreate.aliases = ['asset:vol:create', 'asset:vol:add', 'asset:volume:add'];
AssetVolumeCreate.flags = {
    help: core_1.Flags.help({ char: 'h' }),
    enterprise: core_1.Flags.string({ char: 'e', required: false, description: 'The name of the enterprise to which the asset belongs.' }),
    asset: core_1.Flags.string({ char: 'a', required: false, description: "The name of the asset to which this volume is being applied." }),
    name: core_1.Flags.string({ char: 'n', required: false, description: "Moniker for identifying, retrieving, and accessing this folder mapping.  Must be unique for the asset." }),
    description: core_1.Flags.string({ char: 'd', required: false, description: 'Any notes or other information to help clarify the intended purpose of this folder, to assist during the configuration phase when assigning an actual physical file store to it.' }),
    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.' }),
};
AssetVolumeCreate.args = {
    mountPath: core_1.Args.string({ required: false, description: 'Fully qualified mounting path, inside the container, of the folder being mapped as a volume.  Must be unique for the asset.' })
};
//# sourceMappingURL=create.js.map