"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const core_1 = require("@oclif/core");
const command_base_enterprise_1 = require("../../command-base-enterprise");
const cli_common_1 = require("../../cli-common");
const config_ui_utils_1 = require("../../uilogic/config-ui-utils");
const _ = require("lodash");
const codiac_utils_1 = require("../../codiac-utils");
class SnapshotDeploy extends command_base_enterprise_1.CommandBaseEnterprise {
    constructor() {
        super(...arguments);
        /** CLI Command-line string to rerun this execution as a non-TTY script. */
        this.cmd = [];
        this.infrxAliases = ["cluster:install:snapshot"];
    }
    run() {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            this.bootstrap();
            const { flags } = yield this.parse(SnapshotDeploy);
            // Initialize command string for echo/to-script modes
            this.cmd = [this.config.bin, ...((_b = (_a = this.id) === null || _a === void 0 ? void 0 : _a.split(":")) !== null && _b !== void 0 ? _b : [])];
            try {
                // 1. ESTABLISH CONTEXT
                this._userCntx = yield this.establishTenantContext(this._userCntx);
                let rootMap = yield this.getRootMap();
                const interactionMode = this.getInteractionMode(flags.silent, flags.echo, flags['to-script']);
                let currentEnterprise = (yield this.establishEnterpriseContext(rootMap, flags.enterprise, interactionMode)).enterprise;
                const isInfrxEnterprise = codiac_utils_1.CodiacUtils.isInfrxEnterprise(currentEnterprise.name);
                this.enforceEnterpriseType(currentEnterprise.name);
                // 2. FETCH SNAPSHOTS
                if (interactionMode !== config_ui_utils_1.InteractionModes.silent) {
                    this._ui.startSpinner("Retrieving snapshots");
                }
                let crit = {
                    enterprise: currentEnterprise.name,
                    take: 50,
                    sort: "created~desc"
                };
                let result = yield this._codiacApi.EnterpriseVersionConfig.tryReadMany(crit);
                if (result.error) {
                    if (interactionMode !== config_ui_utils_1.InteractionModes.silent) {
                        this._ui.stopSpinner("failed");
                    }
                    this.logger.error(`Error retrieving snapshots: ${result.error.message || result.message || "Unknown error"}`);
                    throw result.error;
                }
                let snapshots = (_c = result.output) !== null && _c !== void 0 ? _c : [];
                if (interactionMode !== config_ui_utils_1.InteractionModes.silent) {
                    this._ui.stopSpinner(`found ${snapshots.length}`);
                }
                if (snapshots.length === 0) {
                    this._ui.userMessage("No snapshots found in enterprise: " + currentEnterprise.name);
                    return;
                }
                // 3. SELECT SNAPSHOT
                let selectedSnapshot;
                if (interactionMode === config_ui_utils_1.InteractionModes.silent) {
                    selectedSnapshot = yield this.selectSnapshot_silently(snapshots, flags.version);
                }
                else {
                    selectedSnapshot = yield this.selectSnapshot_interactively(snapshots, flags.version);
                }
                if (!selectedSnapshot) {
                    this._ui.userMessage("No snapshot selected. Operation cancelled.");
                    return;
                }
                // Display selected snapshot info
                const assetCount = (_e = (_d = selectedSnapshot.contents) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0;
                if (interactionMode !== config_ui_utils_1.InteractionModes.silent) {
                    this._ui.userMessage(`Selected snapshot: ${this._styler.hilite(selectedSnapshot.version)} (${assetCount} assets)`);
                }
                if (!selectedSnapshot.contents || selectedSnapshot.contents.length === 0) {
                    this._ui.userMessage("Selected snapshot has no assets to deploy.");
                    return;
                }
                // 4. SELECT CABINET
                let cab;
                let envName;
                if (interactionMode === config_ui_utils_1.InteractionModes.silent) {
                    [cab, envName] = yield this.selectCabinet_silently(rootMap, currentEnterprise.name, flags.cabinet);
                }
                else {
                    [cab, envName] = yield this.selectCabinet_interactively(rootMap, currentEnterprise.name, flags.cabinet);
                }
                // ------------------------------------------------------------------
                // Infrx lexicon...
                let destType = isInfrxEnterprise ? "cluster" : "cabinet";
                let dest = isInfrxEnterprise ? ((_f = cab.cluster) !== null && _f !== void 0 ? _f : envName) : `${currentEnterprise.name}/${envName}/${this._styler.hilite(cab.name)}`;
                let destAbbrv = isInfrxEnterprise ? ((_g = cab.cluster) !== null && _g !== void 0 ? _g : envName) : cab.name;
                let action = isInfrxEnterprise ? "install" : "deploy";
                // ------------------------------------------------------------------
                if (isInfrxEnterprise) {
                    this._ui.userMessage(`Target ${destType}: ${this._styler.hilite(dest)}`);
                }
                else {
                    this._ui.userMessage(`Target ${destType}: ${this._styler.hilite(cab.name)} (environment: ${envName})`);
                }
                // 5. PROMPT FOR CLOUD PROVIDER CREDS
                let cluster = rootMap.clusters.find(c => c.name == cab.cluster);
                if (cluster == undefined) {
                    throw new Error(`Did not recognize cluster [${cab.cluster}]`);
                }
                const suppressCspPrompts = interactionMode === config_ui_utils_1.InteractionModes.silent;
                yield this.cspContextualizer.promptForCloudProviderCreds(this.ioc, cluster.provider, suppressCspPrompts, undefined, undefined, cluster.providerSubscriptionId);
                // 6. BUILD DEPLOY REQUEST
                let assets = this.buildAssetVersionsFromSnapshot(selectedSnapshot);
                // Show what will be deployed (unless silent)
                if (interactionMode !== config_ui_utils_1.InteractionModes.silent) {
                    this._ui.userMessage(`\nThe following assets will be ${action}ed:`);
                    for (let asset of assets) {
                        this._ui.userMessage(`  - ${asset.a}: ${asset.v}`);
                    }
                    this._ui.userMessage(`\n${action}ing ${assets.length} assets\n`);
                }
                // 7. BUILD COMMAND STRING (for echo and to-script modes)
                if (interactionMode && [config_ui_utils_1.InteractionModes.echo, config_ui_utils_1.InteractionModes.toscript].includes(interactionMode)) {
                    this.cmd = this.buildCommandString(currentEnterprise.name, selectedSnapshot.version, cab.name);
                    this._ui.userMessage(this.cmd.join(" "));
                }
                // 8. CONFIRM DEPLOYMENT (skip for silent and to-script)
                if (interactionMode !== config_ui_utils_1.InteractionModes.silent && interactionMode !== config_ui_utils_1.InteractionModes.toscript) {
                    let confirmed = yield this._ui.promptConfirm("confirmDeploy", `${action} snapshot ${this._styler.hilite(selectedSnapshot.version)} to ${this._styler.hilite(destAbbrv)}?`, true);
                    if (!confirmed) {
                        this._ui.userMessage(`${action} cancelled.`);
                        return;
                    }
                }
                // 9. EXECUTE DEPLOYMENT (skip for to-script mode)
                if (interactionMode === config_ui_utils_1.InteractionModes.toscript) {
                    // to-script mode: don't execute, just output the command
                    return;
                }
                let target = {
                    enterprise: currentEnterprise.name,
                    environment: envName,
                    cabinet: cab.name
                };
                let deployRequest = {
                    target: target,
                    assets: assets
                };
                yield this._ui.startSpinner(`${action}ing snapshot to [${dest}]...`);
                let deployResult = yield this.genRelayClient.Call.postExecuteDeployment(deployRequest);
                yield this._ui.stopSpinner(deployResult.success ? "SUCCESS" : (deployResult.error ? "ERROR" : "FAILED"));
                if (deployResult.success) {
                    this._ui.userMessage(`${this._styler.colorGood("SUCCESS")}: Snapshot ${selectedSnapshot.version} ${action}ed to ${destAbbrv}`);
                    this._ui.userMessage(`${(_h = deployResult.message) !== null && _h !== void 0 ? _h : ""}`);
                }
                else if (deployResult.error) {
                    this._ui.userMessage(`${this._styler.colorCritical("ERROR")}: ${deployResult.message}`);
                    throw deployResult.error;
                }
                else {
                    this._ui.userMessage(`FAILED: ${deployResult.message}`);
                    throw new Error((_j = deployResult.message) !== null && _j !== void 0 ? _j : `${action} failed`);
                }
                return;
            }
            catch (err) {
                throw err;
            }
            finally {
                yield this.closeConnections();
            }
        });
    }
    /**
     * Builds the equivalent CLI command string for echo/to-script modes.
     */
    buildCommandString(enterprise, snapshotVersion, cabinetName) {
        var _a, _b;
        let cmd = [this.config.bin, ...((_b = (_a = this.id) === null || _a === void 0 ? void 0 : _a.split(":")) !== null && _b !== void 0 ? _b : [])];
        cmd.push("-e", enterprise);
        cmd.push("-v", snapshotVersion);
        cmd.push("-c", cabinetName);
        cmd.push("--silent");
        return cmd;
    }
    /**
     * Selects a snapshot in silent mode - requires the version to be provided.
     */
    selectSnapshot_silently(snapshots, snapshotVersion) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!snapshotVersion) {
                throw new Error("Snapshot version (--version or -v) is required in silent mode.");
            }
            let found = snapshots.find(s => s.version.toLowerCase() === snapshotVersion.toLowerCase());
            if (!found) {
                throw new Error(`Snapshot version "${snapshotVersion}" not found in the enterprise.`);
            }
            return found;
        });
    }
    /**
     * Prompts the user to select a snapshot from the available list interactively.
     */
    selectSnapshot_interactively(snapshots, preselectedVersion) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            // If a version was provided via flag, try to find it
            if (preselectedVersion) {
                let found = snapshots.find(s => s.version.toLowerCase() === preselectedVersion.toLowerCase());
                if (found) {
                    return found;
                }
                this._ui.userMessage(`Snapshot version "${preselectedVersion}" not found. Please select from available snapshots.`);
            }
            // Build choices for the fuzzy selector
            let choices = snapshots.map(s => {
                var _a, _b, _c, _d;
                const assetCount = (_b = (_a = s.contents) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
                const dateStr = this.formatDate(s.created);
                const displayName = `${s.version} - ${assetCount} asset${assetCount !== 1 ? 's' : ''} - ${dateStr} - by ${s.createdBy}`;
                const tag = (_d = (_c = s.tags) === null || _c === void 0 ? void 0 : _c.map(t => t.name).join("|")) !== null && _d !== void 0 ? _d : "";
                return { name: `${displayName} - ${tag}`, value: s.version };
            });
            let fuzzySelector = new cli_common_1.PromptSelectFuzzy(this.logger, this._ui, this._styler);
            let selectedVersion = yield fuzzySelector.go("snapshotVersion", "Select a snapshot to deploy:", false, undefined, false, () => tslib_1.__awaiter(this, void 0, void 0, function* () { return choices; }));
            return snapshots.find(s => s.version === selectedVersion);
        });
    }
    /**
     * Selects a cabinet in silent mode - requires the cabinet name to be provided.
     */
    selectCabinet_silently(rootMap, enterpriseName, cabinetName) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!cabinetName) {
                throw new Error("Cabinet name (--cabinet or -c) is required in silent mode.");
            }
            // Find the cabinet in the rootMap
            let ent = rootMap.enterprises.find(e => e.name === enterpriseName);
            if (!ent) {
                throw new Error(`Enterprise "${enterpriseName}" not found.`);
            }
            for (let env of ent.environments) {
                let cab = env.cabinets.find(c => c.name.toLowerCase() === cabinetName.toLowerCase());
                if (cab) {
                    this.validateCabinet(cab);
                    return [cab, env.name];
                }
            }
            throw new Error(`Cabinet "${cabinetName}" not found in enterprise "${enterpriseName}".`);
        });
    }
    /**
     * Prompts the user to select a target cabinet interactively.
     */
    selectCabinet_interactively(rootMap, enterpriseName, preselectedCabinet) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            let [cab, envName] = yield this.rootMapUtils.promptForCabinet(enterpriseName, rootMap, preselectedCabinet, false, undefined, preselectedCabinet);
            this.validateCabinet(cab);
            return [cab, envName];
        });
    }
    /**
     * Validates that a cabinet has the required infrastructure.
     */
    validateCabinet(cab) {
        if (cab.infrx == undefined || _.isEmpty(cab.infrx)) {
            throw new Error(`Cannot connect to cabinet ${cab.name}: it has no infrastructure record.`);
        }
        if (cab.cluster == undefined || _.isEmpty(cab.cluster)) {
            throw new Error(`Cannot connect to cabinet ${cab.name}: it has no cluster assigned.`);
        }
    }
    /**
     * Builds an array of AssetVersion from the snapshot's contents.
     */
    buildAssetVersionsFromSnapshot(snapshot) {
        if (!snapshot.contents || !Array.isArray(snapshot.contents)) {
            return [];
        }
        return snapshot.contents.map(content => {
            // The content has assetName and version properties
            const assetName = content.assetName || content.entityName || 'unknown';
            const assetVersion = content.version || 'unknown';
            return { a: assetName, v: assetVersion };
        });
    }
    /**
     * Formats a date for display.
     */
    formatDate(date) {
        if (date instanceof Date) {
            return date.toISOString().split('T')[0];
        }
        return new Date(date).toISOString().split('T')[0];
    }
}
exports.default = SnapshotDeploy;
SnapshotDeploy.description = 'Deploys all assets in a snapshot to a target cabinet.';
SnapshotDeploy.aliases = ["cluster:install:snapshot"];
SnapshotDeploy.flags = {
    enterprise: core_1.Flags.string({ char: 'e', required: false, description: 'The name of the enterprise containing the snapshot to deploy.' }),
    version: core_1.Flags.string({ char: 'v', required: false, description: 'The version of the snapshot to deploy (e.g., 5CJRH7XB).' }),
    cabinet: core_1.Flags.string({ char: 'c', required: false, description: 'The target cabinet name to deploy to.' }),
    help: core_1.Flags.help({ char: 'h', description: 'Displays the help document for this command.' }),
    // Interaction mode flags
    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.' })
};
SnapshotDeploy.args = {};
//# sourceMappingURL=deploy.js.map