"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const core_1 = require("@oclif/core");
const _ = require("lodash");
// import { Kit, KitCriteria } from '@codiac.io/codiac-domain';
// import { Asset, AssetCriteria, AssetTypesEnum } from '@codiac.io/codiac-domain';
const contracts_1 = require("../../apis/codiac-api/contracts");
const contracts_2 = require("@codiac.io/codiac-common/contracts");
const codiac_constants_1 = require("../../codiac-constants");
const prettify_1 = require("../../prettify");
const cloud_resource_configurator_factory_1 = require("../../uilogic/cloud-resource-configurator-factory");
const image_registry_contextualizer_1 = require("../../uilogic/image-registry-contextualizer");
const image_contextualizer_1 = require("../../uilogic/image-contextualizer");
const non_empty_validator_1 = require("../../uilogic/validators/non-empty-validator");
// import { InteractionModes } from "../ /uilogic/config-ui-utils";
const config_ui_utils_1 = require("../../uilogic/config-ui-utils");
const command_base_exec_1 = require("../../command-base-exec");
const asset_configurator_1 = require("../../uilogic/cloud-resource-configurators/asset-configurator");
class AssetCreate extends command_base_exec_1.CommandBaseExec {
    bootstrap() {
        super.bootstrap();
        // -----------------------------------------------------------------------------
        //  NEEDS REWORK!!!
        // -----------------------------------------------------------------------------
        // CloudProviderContextualizer.bootstrap(this.ioc);  
        // 
        // Cant call this ^ here - you end up with: 
        //    "Error: @inject called with undefined 
        //     this could mean that the class ImageRegistryContextualizer 
        //     has a circular dependency problem."
        // -----------------------------------------------------------------------------
        this.ioc.bind(image_registry_contextualizer_1.ImageRegistryContextualizer).toSelf();
        this.ioc.bind(image_contextualizer_1.ImageContextualizer).toSelf();
    }
    get uiRegistry() {
        if (this._uiRegistry_backer === undefined) {
            let lazyBootstrap = this.cspContextualizer; // instead of calling it up ^ there
            this._uiRegistry_backer = this.ioc.get(image_registry_contextualizer_1.ImageRegistryContextualizer);
        }
        return this._uiRegistry_backer;
    }
    get uiImage() {
        if (this.uiImage_backer === undefined) {
            let lazyBootstrap = this.cspContextualizer; // instead of calling it up ^ there
            this.uiImage_backer = this.ioc.get(image_contextualizer_1.ImageContextualizer);
        }
        return this.uiImage_backer;
    }
    get uiAsset() {
        if (this.uiAsset_backer == undefined) {
            this.uiAsset_backer = new asset_configurator_1.AssetConfigurator(this._ui, this._styler, this.genRelayClient, this.logger); // this.ioc.get(AssetConfigurator);
        }
        return this.uiAsset_backer;
    }
    captureArgs() {
        var _a, _b;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { args, flags } = yield this.parse(AssetCreate);
            // ------BEGIN CAPTURE ARGS---------------------------------------------------------------
            let partial = {};
            if (flags.enterprise)
                partial.enterprise = flags.enterprise;
            partial.type = (flags.helm == true) ? contracts_1.AssetTypesEnum.helm : contracts_1.AssetTypesEnum.service;
            // If they called out for a kit, then either use it or prompt for one
            if (flags.kit == codiac_constants_1.CliConstants.PROMPT_ME_DESIGNATOR) {
                // Prompt for kit
                let kits = yield this._codiacApi.Kit.readMany({ all: true }, { throwNotFound: true });
                let choices = kits.map(k => k.name).sort();
                this._ui.userMessage("Select a kit from which to create an asset...");
                let kitName = (yield this._ui.promptSelect("kit", "Kit: ", choices, false));
                partial.kit = kits.find(k => k.name == kitName);
            }
            else if (flags.kit) {
                // Retrieve Kit
                let parts = flags.kit.split("/");
                let kitLibrary = (parts.length > 1) ? parts[0] : undefined;
                let kitName = (parts.length > 1) ? parts[1] : parts[0];
                let kitCrit = {};
                kitCrit.name = new contracts_2.StringFilter();
                kitCrit.name.$eq = kitName;
                kitCrit.library = kitLibrary;
                let kitResults = yield this._codiacApi.Kit.readMany(kitCrit, { throwNotFound: true });
                partial.kit = kitResults[0];
            }
            if (partial.kit) {
                //  COMMENTED OUT BC: this doesnt belong in capture args; it belongs in buildCandidate...
                // // let assetResult: SingleResult<Asset> = await this.relayClient.generateAssetFromKit(partial.kit.name, enterpriseCode, partial.kit.library);
                // let assetResult: SingleResult<Kit> = await this.genRelayClient.Call.getGenerateAssetFromKit({
                //   kitName: partial.kit.name, targetEnterprise: partial.enterprise, kitLibrary: partial.kit.library
                // });
                // if (assetResult.error) throw assetResult.error;
                // else if (assetResult.output == undefined) throw new Error(`Failed without error generating asset from kit [${partial.kit.name}]: ${assetResult.message}`);
                // else {
                //   partial = { ...assetResult.output, enterprise: partial.enterprise };
                // }
            }
            // overlay given values
            if (flags.name !== undefined)
                partial.name = flags.name;
            if (flags.code !== undefined)
                partial.code = flags.code;
            if (flags.port !== undefined) {
                partial.port = flags.port.map(p => this.uiAsset.toPortMapping(p));
            }
            if (flags.hasIngress !== undefined)
                partial.hasIngress = flags.hasIngress;
            if (flags.private !== undefined)
                partial.ingressInternal = flags.private;
            if (flags.routedWithoutName !== undefined)
                partial.routedWithoutName = flags.routedWithoutName;
            if (flags['allow-http'] != undefined)
                partial.allowHttp = flags['allow-http'];
            if (flags['disable-https'] != undefined)
                partial.disableHttps = flags['disable-https'];
            partial.regInput = {};
            if (flags.registry) {
                partial.regInput.url = flags.registry;
                // regRef.scope = undefined; 
            }
            partial.imgInput = { registry: partial.regInput };
            if (flags.image !== undefined) {
                partial.imgInput.name = flags.image;
            }
            // --------------END CAPTURE ARGS-------------------------------------------------
            // ---------------------------------------------------------------
            // TODO: move everything from here down into the base class
            // ---------------------------------------------------------------
            const interactionMode = (this.pipedInput) ? config_ui_utils_1.InteractionModes.silent : (_b = (_a = undefined !== null && undefined !== void 0 ? undefined : (flags.echo ? config_ui_utils_1.InteractionModes.echo : undefined)) !== null && _a !== void 0 ? _a : (flags["to-script"] ? config_ui_utils_1.InteractionModes.toscript : undefined)) !== null && _b !== void 0 ? _b : (flags.silent ? config_ui_utils_1.InteractionModes.silent : undefined);
            const takeDefaults = flags['take-defaults'];
            return [partial, interactionMode, takeDefaults];
        });
    }
    buildCandidate_silently(partial) {
        var _a, _b, _c, _d;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            // Rough validation for missing args
            if (!partial.name)
                throw new Error("Asset name must be set");
            if (!partial.code)
                partial.code = partial.name.replace(" ", "-");
            if (!partial.regInput)
                throw new Error("Asset registry must be set");
            if (!partial.imgInput)
                throw new Error("Asset image must be set");
            if (!((_a = partial.image) === null || _a === void 0 ? void 0 : _a.name))
                throw new Error("Asset image name must be set");
            if (!partial.port)
                throw new Error("Asset port must be set"); // TODO: Consider defaulting to 80 or next available...?
            // TODO: build the image declaration from the regInput and imgInput
            // partial.image = this.assembleImage(partial.image.name, partial.regInput, partial.imgInput);
            // We gotta validate the registry input, 
            // AND we should look it up by csp+name, 
            // not by requiring the user to enter all of its details
            // ALSO 
            // perhaps silent mode doesnt support creating a new registry, 
            // only referring to existing ones...?  
            // Perhaps there's a "cod registry create" command for that?
            //
            // TODO: this.validateImage(partial.image);
            try {
                let candidate = Object.assign({ enterprise: partial.enterprise, code: partial.code, name: partial.name, image: partial.image, port: partial.port, hasIngress: (_b = partial.hasIngress) !== null && _b !== void 0 ? _b : false, routedWithoutName: (_c = partial.routedWithoutName) !== null && _c !== void 0 ? _c : false }, this.createAuditable());
                candidate.product = partial.product;
                candidate.reservedFolders = partial.reservedFolders;
                candidate.type = (_d = partial.type) !== null && _d !== void 0 ? _d : contracts_1.AssetTypesEnum.service;
                if (partial.allowHttp != undefined)
                    candidate.allowHttp = partial.allowHttp;
                if (partial.disableHttps != undefined)
                    candidate.disableHttps = partial.disableHttps;
                if (partial.ingressInternal == true)
                    candidate.ingressInternal = partial.ingressInternal;
                // --------------------------------------------------------------------------------------
                // Throw until we get the registry and image properly declared without user interaction
                throw new Error("silent mode is not yet supported for asset creates");
                // --------------------------------------------------------------------------------------
                return candidate;
            }
            catch (err) {
                throw err;
            }
        });
    }
    /** Walks the user through a Q & A to build out the specs for the asset. */
    buildCandidate_interactively(partial) {
        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, _b, _c, _d, _e, _f;
                let candidate;
                try {
                    let lazyBootstrap = this.cspContextualizer;
                    cloud_resource_configurator_factory_1.RegistryConfiguratorFactory.bootstrap(this.ioc);
                    let assets = yield this.getAssetList(partial.enterprise);
                    if (!this.uiAsset.isLoaded)
                        this.uiAsset.load(assets); // Dont await: let this run in the background while the user is interacting.
                    let confirmed;
                    while (!confirmed) {
                        console.clear();
                        if (!partial.kit) {
                            if (partial.type == contracts_1.AssetTypesEnum.helm) {
                                // HELM REPOSITORY
                                let keepHelm = false;
                                if (partial.image) {
                                    keepHelm = yield this._ui.promptConfirm("editHelmChart", `Helm chart: ${this._styler.hilite(partial.image.name)} [in ${partial.image.registry.code}] `);
                                }
                                if (keepHelm !== true) {
                                    // let registry = await this.uiRegistry.promptForHelmRepository(this.ioc, partial.regInput ?? {});
                                    let registry = yield this.uiRegistry.promptForRegistry(contracts_1.PackageType.helm, this.ioc, (_a = partial.regInput) !== null && _a !== void 0 ? _a : {});
                                    if (partial.imgInput == undefined) {
                                        partial.imgInput = { registry: registry };
                                    }
                                    else {
                                        partial.imgInput.registry = registry;
                                    }
                                    partial.image = yield this.uiImage.promptForImage(registry, partial.imgInput);
                                }
                            }
                            else {
                                // IMAGE REGISTRY
                                let keepImage = false;
                                if (partial.image) {
                                    keepImage = yield this._ui.promptConfirm("editImage", `Image: ${this._styler.hilite(partial.image.name)} [in ${partial.image.registry.code}] `);
                                }
                                if (keepImage !== true) {
                                    let registry = yield this.uiRegistry.promptForRegistry(contracts_1.PackageType.container, this.ioc, (_b = partial.regInput) !== null && _b !== void 0 ? _b : {});
                                    if (partial.imgInput == undefined) {
                                        partial.imgInput = { registry: registry };
                                    }
                                    else {
                                        partial.imgInput.registry = registry;
                                    }
                                    partial.image = yield this.uiImage.promptForImage(registry, partial.imgInput);
                                }
                            }
                        }
                        // NOTE:  We are gating on "confirmed != undefined" because that means they asked to revise the values. 
                        // ASSET NAME
                        let newNameEntry = "";
                        if (partial.kit || !partial.name || partial.name.trim() == "" || confirmed != undefined) {
                            let entry = "";
                            // TODO:  Validate against assets list.  perhaps prvide a default name suffix to avoid collisions
                            while (!entry || entry.trim() == "") {
                                entry = (yield this._ui.prompt('assetName', 'Name for the asset within the enteprise: ', true, partial.name || ((_d = (_c = partial.image) === null || _c === void 0 ? void 0 : _c.name) === null || _d === void 0 ? void 0 : _d.split("/").pop()), new non_empty_validator_1.NonEmptyValidator()));
                            }
                            if (entry != partial.name)
                                newNameEntry = entry;
                            partial.name = entry;
                        }
                        // -------------------------------------
                        // EDITABLE PROPERTIES
                        // -------------------------------------
                        let edited = yield this.uiAsset.editAsset(partial);
                        // let reg = new ImageRegistry(imgRef.registry?.registryType!, imgRef.registry?.registryUrl!, imgReg.);
                        candidate = Object.assign({ enterprise: edited.enterprise, code: edited.code, name: edited.name, type: edited.type, image: edited.image, port: (_e = edited.port) !== null && _e !== void 0 ? _e : 0, hasIngress: edited.hasIngress, routedWithoutName: (_f = edited.routedWithoutName) !== null && _f !== void 0 ? _f : false }, this.createAuditable());
                        if (edited.allowHttp != undefined)
                            candidate.allowHttp = edited.allowHttp;
                        if (edited.disableHttps != undefined)
                            candidate.disableHttps = edited.disableHttps;
                        if (edited.ingressInternal == true)
                            candidate.ingressInternal = edited.ingressInternal;
                        this._ui.userMessage((0, prettify_1.prettify)(candidate));
                        this._ui.userMessage(`You are about to add this new asset as ${this._styler.bold(this._styler.hilite(candidate.name))} to enterprise ${this._styler.hilite(candidate.enterprise)} [Tenant: ${this._userCntx.currentTenantCode}].`);
                        confirmed = yield this._ui.promptConfirm("confirmCreateAsset", "Continue?", false);
                    }
                    if (candidate === undefined)
                        throw new Error("No Asset was defined."); // this shd never happen; it's just here to pacify typescript.
                    resolve(candidate);
                }
                catch (err) {
                    // this.logger.error(`${err.name ?? ''} ${err.message ?? ''}`, err.stack);
                    reject(err);
                }
            }));
        });
    }
    buildCommandString(candidate, cmd) {
        // kit: Flags.string({ char: 'k'
        cmd.push("--enterprise", candidate.enterprise);
        cmd.push("--name", candidate.name);
        cmd.push("--code", candidate.code);
        cmd.push("--image", candidate.image.name);
        cmd.push("--registry", candidate.image.registry.code);
        if (candidate.type == contracts_1.AssetTypesEnum.helm) {
            cmd.push("--helm");
        }
        else {
            // Ports
            if (candidate.port != undefined) {
                if (_.isArray(candidate.port)) {
                    for (let portDef of candidate.port) {
                        cmd.push("--port", this.uiAsset.toPortString(portDef));
                    }
                }
                else {
                    if (typeof candidate.port == 'number') {
                        cmd.push("--port", candidate.port.toString());
                    }
                    else {
                        cmd.push("--port", this.uiAsset.toPortString(candidate.port));
                    }
                }
            }
            if (candidate.hasIngress == true)
                cmd.push("--hasIngress");
            if (candidate.ingressInternal == true)
                cmd.push("--private");
            if (candidate.routedWithoutName == true)
                cmd.push("--routedWithoutName");
            if (candidate.allowHttp == true)
                cmd.push("--allow-http");
            if (candidate.disableHttps == true)
                cmd.push("--disable-https");
        }
        return cmd;
    }
    executeTask(candidate) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                let result = yield this._codiacApi.Asset.tryCreate(candidate);
                if (result.error)
                    throw result.error;
                if (!result.success || !result.output)
                    throw new Error(`Failed without error creating asset [${candidate.name}]: ${result.message}.`);
                this.logger.notice(`${this._styler.colorGood("SUCCESS!")} Created new asset ${this._styler.bold(this._styler.hilite(candidate.name))} in enterprise ${this._styler.bold(this._styler.hilite(candidate.enterprise))}.`, result.output);
                this._ui.userMessage(`${this._styler.colorGood("SUCCESS!")} Created new asset ${this._styler.bold(this._styler.hilite(candidate.name))} in enterprise ${this._styler.bold(this._styler.hilite(candidate.enterprise))}.`);
            }
            catch (err) {
                this.logger.error(err, candidate);
                throw err; // <-- base handles this for us
            }
            finally {
                return;
            }
        });
    }
    // --------------------------------------------------------------------------------------------
    // 
    //    SUPPORTING METHODS
    // 
    // --------------------------------------------------------------------------------------------
    /** Retrieves the list of existing assets in the current enteprise from the codiac api.
     * Doesnt throw, just silently logs any errors and returns empty.
    */
    getAssetList(enterpriseCode) {
        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 assets = [];
                try {
                    // Magic happens here...
                    assets = yield this._codiacApi.Asset.readMany({ enterprise: enterpriseCode }, { throwNotFound: true });
                }
                catch (err) {
                    this.logger.debug(`Error loading assets: ${(_a = err.message) !== null && _a !== void 0 ? _a : err.stack}`);
                }
                finally {
                    resolve(assets);
                }
            }));
        });
    }
}
exports.default = AssetCreate;
AssetCreate.description = 'Creates a new asset in the given enterprise from an image that exists in a container registry.';
AssetCreate.aliases = ['asset:new'];
AssetCreate.dependsOn = ["image", "registry", "name", "code", "enterprise", "port"]; //, "hasIngress", "routedWithoutName"]  // cant require these cuz including them means "true"
AssetCreate.flags = {
    help: core_1.Flags.help({ char: 'h' }),
    kit: core_1.Flags.string({ char: 'k', required: false, exclusive: ["image", "registry"], description: "The name of the kit (with library prefix, eg: 'library/kitName') to use to create the asset.  Use '?' to be prompted for a kit (Optional: used instead of calling for the image)." }),
    image: core_1.Flags.string({ char: 'i', required: false, exclusive: ["kit"], description: "The image name including scope prefix if applicable (eg: '@yourCompany/your-api-image')." }),
    registry: core_1.Flags.string({ char: 'r', required: false, exclusive: ["kit"], description: "The container registry in which the image exists (defaults to docker hub)." }),
    name: core_1.Flags.string({ char: 'n', required: false, description: "The name to give the enterprise asset (Optional: defaults to the image name without the scope prefix)." }),
    code: core_1.Flags.string({ char: 'c', required: false, description: "The host name to assign this asset in the domain url.  eg: 'myasset' in 'myasset.your-domain.com' (Optional: defaults to the asset name)." }),
    enterprise: core_1.Flags.string({ char: 'e', required: false, description: 'The name of the enterprise in which to create this asset. (defaults to the current enterprise context)' }),
    port: core_1.Flags.string({ char: 'p', multiple: true, required: false, description: 'The default port number (from 0 to 65535) to assign the asset when deployed to a cabinet (optional).  To specify port forwarding (betw the service and container), use format: `<service-port>:<container-port>`, eg: `3555:80`' }),
    hasIngress: core_1.Flags.boolean({ char: 'g', required: false, description: 'Declaration that the asset service is to be accessible from outside the cluster (optional: defaults to false).' }),
    private: core_1.Flags.boolean({ required: false, dependsOn: ["hasIngress"], exclusive: ["helm"], description: '(for use when hasIngress is also set) Prevents access from the internet.  That is, declares that asset service to only be accessible through ingress on internal networks (optional: defaults to false).  Useful for enabling private "east-west" (cross-cluster) communications without going out to the internet.' }),
    routedWithoutName: core_1.Flags.boolean({ char: 'o', required: false, description: 'Indicates that this asset will be the base ingress against your DNS. eg: true services yourwebsite.com (as root/naked) (optional: defaults to false).' }),
    "allow-http": core_1.Flags.boolean({ required: false, description: '(Advanced) Enable this setting to allow http (ie: non-SSL/TLS) requests in through the ingress controller without getting redirected to https.  That is, Setting this to true will prevent the ingress controller from invoking an automatic redirect to https on any inbound http requests. (optional: defaults to false).' }),
    "disable-https": core_1.Flags.boolean({ required: false, description: '(Advanced) (optional: defaults to false) Enable this setting to remove support for https (aka: SSL/TLS) in the ingress controller.  Use this setting when tls terminates upstream of the cluster and its ingress controller.  Setting this flag DOES NOT automatically set the `--allow-http` flag, so be diligent when using it.' }),
    helm: core_1.Flags.boolean({ required: false, description: 'Indicates that this asset will be base on a Helm chart instead of a container (optional: defaults to false).' }),
    // TODO: Move these into the base class
    silent: core_1.Flags.boolean({ exclusive: ["echo", "to-script"], description: 'Non-Interactive mode; executes without any user interaction and fails on any missing or invalid arguments.' }),
    echo: core_1.Flags.boolean({ exclusive: ["to-script", "silent"], description: 'Echo interaction mode; renders the equivalent non-interactive cli command for future use before final execution.' }),
    "to-script": core_1.Flags.boolean({ exclusive: ["echo", "silent"], description: 'Toscript interaction mode; renders the equivalent non-interactive cli command WITHOUT any execution.' }),
    "take-defaults": core_1.Flags.boolean({ exclusive: ["silent"], description: "Setting this to true prevents prompting for confirmation on parameters that were passed in or were automatically set to default values (Irrelevant in --silent mode).  This behavior does NOT apply to scenarios that the command needs to override invalid user input; such cases will always prompt for confirmation.  eg: when no argument was provided for file or type, and only one exists, the system will render the autoselected file unless this flag is set." })
};
AssetCreate.args = {};
//# sourceMappingURL=create.js.map