"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const core_1 = require("@oclif/core");
const command_base_exec_1 = require("../../command-base-exec");
const cli_common_1 = require("../../cli-common");
const _ = require("lodash");
class SnapshotDeploy extends command_base_exec_1.CommandBaseExec {
    constructor() {
        super(...arguments);
        this.sdlcOnly = true;
    }
    captureArgs() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { flags } = yield this.parse(SnapshotDeploy);
            let partial = {};
            if (flags.enterprise)
                partial.enterprise = flags.enterprise;
            if (flags.version)
                partial.version = flags.version;
            if (flags.cabinet)
                partial.cabinetName = flags.cabinet;
            if (flags['override-conditions'])
                partial.overrideConditions = true;
            return [partial, false];
        });
    }
    buildCandidate_silently(partial, rootMap) {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!partial.version)
                throw new Error("Snapshot version (--version or -v) is required in silent mode.");
            if (!partial.cabinetName)
                throw new Error("Cabinet name (--cabinet or -c) is required in silent mode.");
            // Fetch and find snapshot
            let result = yield this._codiacApi.EnterpriseVersionConfig.tryReadMany({
                enterprise: partial.enterprise,
                take: 100,
                sort: "created~desc"
            });
            if (result.error)
                throw result.error;
            let snapshot = ((_a = result.output) !== null && _a !== void 0 ? _a : []).find(s => s.version.toLowerCase() === partial.version.toLowerCase());
            if (!snapshot)
                throw new Error(`Snapshot version "${partial.version}" not found in the enterprise.`);
            // Find cabinet
            let ent = rootMap.enterprises.find(e => e.name === partial.enterprise);
            if (!ent)
                throw new Error(`Enterprise "${partial.enterprise}" not found.`);
            let cabinet;
            let envName;
            for (let env of ent.environments) {
                let cab = env.cabinets.find(c => c.name.toLowerCase() === partial.cabinetName.toLowerCase());
                if (cab) {
                    cabinet = cab;
                    envName = env.name;
                    break;
                }
            }
            if (!cabinet || !envName)
                throw new Error(`Cabinet "${partial.cabinetName}" not found in enterprise "${partial.enterprise}".`);
            this.validateCabinet(cabinet);
            // CSP creds (suppressed in silent mode)
            let cluster = rootMap.clusters.find(c => c.name == cabinet.cluster);
            if (!cluster)
                throw new Error(`Did not recognize cluster [${cabinet.cluster}]`);
            yield this.cspContextualizer.promptForCloudProviderCreds(this.ioc, cluster.provider, true, undefined, undefined, cluster.providerSubscriptionId);
            return { enterprise: partial.enterprise, snapshot, cabinet, envName, overrideConditions: partial.overrideConditions };
        });
    }
    buildCandidate_interactively(partial, rootMap, takeDefaults) {
        var _a, _b;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            // Fetch snapshots
            this._ui.startSpinner("Retrieving snapshots");
            let result = yield this._codiacApi.EnterpriseVersionConfig.tryReadMany({
                enterprise: partial.enterprise,
                take: 100,
                sort: "created~desc"
            });
            if (result.error) {
                this._ui.stopSpinner("failed");
                throw result.error;
            }
            let snapshots = (_a = result.output) !== null && _a !== void 0 ? _a : [];
            this._ui.stopSpinner(`found ${snapshots.length}`);
            if (snapshots.length === 0) {
                this._ui.userMessage("No snapshots found in enterprise: " + partial.enterprise);
                return undefined;
            }
            // Select snapshot
            let snapshot;
            if (partial.version) {
                snapshot = snapshots.find(s => s.version.toLowerCase() === partial.version.toLowerCase());
                if (!snapshot)
                    this._ui.userMessage(`Snapshot version "${partial.version}" not found. Please select from available snapshots.`);
            }
            if (!snapshot) {
                let choices = snapshots.map(s => {
                    var _a, _b;
                    const assetCount = this.countTotalAssets(s);
                    const dateStr = this.formatDate(s.created);
                    const displayName = `${s.version} - ${assetCount} asset${assetCount !== 1 ? 's' : ''} - ${dateStr} - by ${s.createdBy}`;
                    const tag = (_b = (_a = s.tags) === null || _a === void 0 ? void 0 : _a.map((t) => t.name).join("|")) !== null && _b !== void 0 ? _b : "";
                    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; }));
                snapshot = snapshots.find(s => s.version === selectedVersion);
            }
            if (!snapshot) {
                this._ui.userMessage("No snapshot selected. Operation cancelled.");
                return undefined;
            }
            const assetCount = this.countTotalAssets(snapshot);
            this._ui.userMessage(`Selected snapshot: ${this._styler.hilite(snapshot.version)} (${assetCount} assets)`);
            if (assetCount === 0) {
                this._ui.userMessage("Selected snapshot has no assets to deploy.");
                return undefined;
            }
            // Select cabinet
            let [cabinet, envName] = yield this.rootMapUtils.promptForCabinet(partial.enterprise, rootMap, partial.cabinetName, false, undefined, partial.cabinetName);
            this.validateCabinet(cabinet);
            // Display target
            this._ui.userMessage(`Target cabinet: ${this._styler.hilite(cabinet.name)} (environment: ${envName})`);
            // CSP creds
            let cluster = rootMap.clusters.find(c => c.name == cabinet.cluster);
            if (!cluster)
                throw new Error(`Did not recognize cluster [${cabinet.cluster}]`);
            yield this.cspContextualizer.promptForCloudProviderCreds(this.ioc, cluster.provider, false, undefined, undefined, cluster.providerSubscriptionId);
            // Show assets
            this._ui.userMessage(`\nThe following will be deployed:`);
            for (const content of (_b = snapshot.contents) !== null && _b !== void 0 ? _b : []) {
                if (content.entityType === "asset") {
                    const asset = content;
                    this._ui.userMessage(`  - ${asset.assetName}: ${asset.version}`);
                }
                else if (content.entityType === "product") {
                    const product = content;
                    this._ui.userMessage(`  - [Product] ${product.productName}: ${product.version}`);
                    if (product.contents) {
                        for (const nested of product.contents.filter((c) => c.entityType === "asset")) {
                            const asset = nested;
                            this._ui.userMessage(`      └─ ${asset.assetName}: ${asset.version}`);
                        }
                    }
                }
            }
            let assets = this.buildAssetVersionsFromSnapshot(snapshot);
            this._ui.userMessage(`\nDeploying ${assets.length} assets\n`);
            // Confirm
            const confirmed = yield this._ui.promptConfirm("confirmDeploy", `Deploy snapshot ${this._styler.hilite(snapshot.version)} to ${this._styler.hilite(cabinet.name)}?`, true);
            if (!confirmed) {
                this._ui.userMessage(`Deploy cancelled.`);
                return undefined;
            }
            return { enterprise: partial.enterprise, snapshot, cabinet, envName, overrideConditions: partial.overrideConditions };
        });
    }
    buildCommandString(candidate, cmd) {
        cmd.push("-e", candidate.enterprise);
        cmd.push("-v", candidate.snapshot.version);
        cmd.push("-c", candidate.cabinet.name);
        if (candidate.overrideConditions)
            cmd.push("--override-conditions");
        cmd.push("--silent");
        return cmd;
    }
    executeTask(candidate) {
        var _a, _b;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const dest = `${candidate.enterprise}/${candidate.envName}/${this._styler.hilite(candidate.cabinet.name)}`;
            let target = {
                enterprise: candidate.enterprise,
                environment: candidate.envName,
                cabinet: candidate.cabinet.name
            };
            let assets = this.buildAssetVersionsFromSnapshot(candidate.snapshot);
            let deployRequest = { target, assets, overrideConditions: candidate.overrideConditions };
            this._ui.startSpinner(`Deploying 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 ${candidate.snapshot.version} deployed to ${candidate.cabinet.name}`);
                this._ui.userMessage(`${(_a = deployResult.message) !== null && _a !== void 0 ? _a : ""}`);
                this.reportSkipped(deployResult.skipped);
            }
            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((_b = deployResult.message) !== null && _b !== void 0 ? _b : "Deploy failed");
            }
        });
    }
    reportSkipped(skipped) {
        var _a, _b;
        if (!skipped || skipped.length === 0)
            return;
        this._ui.userMessage(`Skipped (${skipped.length}):`);
        for (const item of skipped) {
            const reason = (_b = (_a = item.reason) !== null && _a !== void 0 ? _a : item.condition) !== null && _b !== void 0 ? _b : "condition evaluated to false";
            this._ui.userMessage(`  - ${item.name}: ${reason}`);
        }
    }
    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.`);
        }
    }
    buildAssetVersionsFromSnapshot(snapshot) {
        if (!snapshot.contents || !Array.isArray(snapshot.contents))
            return [];
        const assets = [];
        for (const content of snapshot.contents) {
            if (content.entityType === "asset") {
                const assetVersionConfig = content;
                assets.push({ a: assetVersionConfig.assetName, v: assetVersionConfig.version });
            }
            else if (content.entityType === "product") {
                const productVersionConfig = content;
                if (productVersionConfig.contents && Array.isArray(productVersionConfig.contents)) {
                    for (const nestedContent of productVersionConfig.contents) {
                        if (nestedContent.entityType === "asset") {
                            const nestedAsset = nestedContent;
                            assets.push({ a: nestedAsset.assetName, v: nestedAsset.version });
                        }
                    }
                }
            }
        }
        return assets;
    }
    formatDate(date) {
        if (date instanceof Date)
            return date.toISOString().split('T')[0];
        return new Date(date).toISOString().split('T')[0];
    }
    countTotalAssets(snapshot) {
        if (!snapshot.contents)
            return 0;
        let count = 0;
        for (const content of snapshot.contents) {
            if (content.entityType === "asset") {
                count++;
            }
            else if (content.entityType === "product" && content.contents) {
                count += content.contents.filter((c) => c.entityType === "asset").length;
            }
        }
        return count;
    }
}
exports.default = SnapshotDeploy;
SnapshotDeploy.description = 'Deploys all assets in a snapshot to a target cabinet.';
SnapshotDeploy.aliases = [];
SnapshotDeploy.flags = {
    help: core_1.Flags.help({ char: 'h', description: 'Displays the help document for this command.' }),
    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.' }),
    "override-conditions": core_1.Flags.boolean({ required: false, description: 'Deploy all assets regardless of any configured deploy conditions.' }),
    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