"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const IBetterLogger_1 = require("@codiac.io/codiac-common/IBetterLogger");
const codiac_ops_common_1 = require("@codiac.io/codiac-ops-common");
const core_1 = require("@oclif/core");
const guid_typescript_1 = require("guid-typescript");
const _ = require("lodash");
const contracts_1 = require("../../apis/codiac-api/contracts");
const command_base_exec_tenant_1 = require("../../command-base-exec-tenant");
const cloud_provider_contextualizer_1 = require("../../uilogic/cloud-provider-contextualizer");
class CspSetup extends command_base_exec_tenant_1.CommandBaseExecTenant {
    get uiCloudProvider() {
        if (this.__uiCloudProvider_backing === undefined) {
            this.__uiCloudProvider_backing = this.ioc.get(cloud_provider_contextualizer_1.CloudProviderContextualizer);
        }
        return this.__uiCloudProvider_backing;
    }
    bootstrap() {
        super.bootstrap();
        cloud_provider_contextualizer_1.CloudProviderContextualizer.bootstrap(this.ioc);
    }
    captureArgs() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const { args, flags } = yield this.parse(CspSetup);
            let partial = {};
            if (args.provider != undefined) {
                // throw new Error("Missing provider argument.");
                if (!Object.keys(contracts_1.CloudProviderEnum).includes(args.provider))
                    throw new Error("Unrecognized provider.");
                partial.provider = contracts_1.CloudProviderEnum[args.provider];
            }
            return [partial, undefined, false];
        });
    }
    buildCandidate_silently(partial) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            throw new Error('This command does not support silent mode.');
        });
    }
    buildCandidate_interactively(partial, takeDefaults) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const allPvds = Object.values(contracts_1.CloudProviderEnum);
            partial.provider = yield this.uiCloudProvider.promptForCloudProvider(allPvds, partial.provider);
            partial.providerTenantId = yield this.promptForTenantId(partial.provider, partial.providerTenantId);
            let candidate = {
                provider: partial.provider,
                providerTenantId: partial.providerTenantId
            };
            return candidate;
        });
    }
    promptForTenantId(provider, defaultValue) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                let validator = {
                    provider: provider,
                    isValid: (candidate) => {
                        let result = { isValid: true };
                        if (candidate == undefined || _.isEmpty(candidate))
                            result.isValid = false;
                        else {
                            switch (provider) {
                                case (contracts_1.CloudProviderEnum.azure):
                                    result.isValid = guid_typescript_1.Guid.isGuid(candidate);
                                    if (!result.isValid)
                                        result.reason = "Azure tenant ID must be a Guid (eg: 51d52cf1-83c9-4f02-b117-703ecb728b74)";
                                    break;
                                case (contracts_1.CloudProviderEnum.aws):
                                    result.isValid = /\d{12}/.test(candidate);
                                    if (!result.isValid)
                                        result.reason = "AWS Account ID must be a 12-digit string (eg: 123456789123)";
                                    break;
                            }
                        }
                        return result;
                    },
                };
                let prompt = provider.concat(" tenant ID:");
                switch (provider) {
                    case (contracts_1.CloudProviderEnum.azure):
                        prompt = provider.concat(" Tenant ID:");
                        break;
                    case (contracts_1.CloudProviderEnum.aws):
                        prompt = provider.concat(" Account ID:");
                        break;
                }
                let cspTenantId = (yield this._ui.prompt("cspTenantId", prompt, true, defaultValue, validator));
                return cspTenantId;
            }
            catch (err) {
                throw err;
            }
        });
    }
    buildCommandString(candidate, cmd) {
        cmd = ["csp setup", candidate.provider, "-t", candidate.providerTenantId];
        return cmd;
    }
    executeTask(candidate) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            // DISCLAIMER / FULL-TRANSPARENCY MESSAGE
            this._ui.userMessage("This is a one-time execution for your administrator to grant Codiac \npermission to operate within your cloud subscription.");
            if (candidate.provider == contracts_1.CloudProviderEnum.azure) {
                this._ui.userMessage("Codiac is going to register itself in your Azure Entra ID instance using the azure CLI...");
            }
            else {
                this._ui.userMessage("Codiac is going to register itself in your ".concat(candidate.provider, " instance using the azure CLI..."));
            }
            switch (candidate.provider) {
                case (contracts_1.CloudProviderEnum.azure):
                    yield this.executeCspSetupForAzure(candidate);
                    break;
                case (contracts_1.CloudProviderEnum.azure):
                    this._ui.userMessage(candidate.provider.concat(" setup not yet implemented."));
                    break;
                default:
                    this._ui.userMessage(candidate.provider.concat(" setup not yet implemented."));
                    break;
            }
        });
    }
    executeCspSetupForAzure(candidate) {
        var _a;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            try {
                let exec = new codiac_ops_common_1.CliExec(this.logger);
                // 1.  Execute interactive terminal login within the relay image
                yield this.authenticateTheRelay(candidate, exec);
                // 2.  Execute the Registration
                let cspResult = yield this.genRelayClient.Call.csp_tenant_profile_getsert(candidate);
                if (cspResult.error != undefined) {
                    throw cspResult.error;
                }
                else if (cspResult.success != true || cspResult.output == undefined) {
                    throw new Error("Failed without error registering Codiac with ".concat(candidate.provider, ": ", (_a = cspResult.message) !== null && _a !== void 0 ? _a : "(Check logs for details)"));
                }
                else {
                    this._ui.userMessage("Successfully registered Codiac with your Azure subscription.");
                }
                // 3.  Execute interactive terminal logout within the relay image
                let cmdPartsLogout = ["docker exec", "-it", "codiac-relay", "sh", "-c", "\"az logout\""];
                const cmdLogout = cmdPartsLogout.join(" ");
                let resultCodeLogout = yield exec.spawnAndLog(cmdLogout, process.cwd(), undefined, IBetterLogger_1.LogLevel.trace, true);
                if (resultCodeLogout != 0) {
                    throw new Error('Error logging out from Azure.');
                }
                else {
                    this._ui.userMessage("Successfully completed and logged you out of Azure.");
                }
            }
            catch (err) {
                throw err;
            }
        });
    }
    authenticateTheRelay(candidate, exec) {
        var _a, _b;
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (this.bareMetalRelay) {
                // Relay is running bare metal, log in on the host machine...
                try {
                    let cspUser = yield this.uiCloudProvider.promptForCloudProviderCreds(this.ioc, candidate.provider, false);
                    let subx = "";
                    if (cspUser.subscriptionId) {
                        subx = ((cspUser.provider == contracts_1.CloudProviderEnum.aws) ? "acct" : "subx").concat(": ", cspUser.subscriptionId);
                        subx = "(".concat(subx, ")");
                    }
                    this._ui.userMessage(this._styler.colorGood("Success!").concat(" Authenticated with ", candidate.provider, " as ", this._styler.hilite(cspUser.username), subx));
                }
                catch (err) {
                    this._ui.userMessage(`${this._styler.colorCritical("FAILED")} ${(_a = err.name) !== null && _a !== void 0 ? _a : ""} ${(_b = err.message) !== null && _b !== void 0 ? _b : ""}`);
                }
            }
            else {
                // Relay is running in container, log in INSIDE the relay container...
                let cmdLoginParts = ["docker exec", "-it", "codiac-relay", "sh", "-c", "\"az login\""];
                const cmdLogin = cmdLoginParts.join(" ");
                let resultCodeLogin = yield exec.spawnAndLog(cmdLogin, process.cwd(), undefined, IBetterLogger_1.LogLevel.trace, true);
                if (resultCodeLogin != 0) {
                    throw new Error('Error authenticating with Azure.');
                }
                else {
                    this._ui.userMessage("Successfully logged into Azure.");
                    // TODO: process.stdin.flush <--- gotta get rid of any stray input (eg: CTRL-C to copy becuase its carry forward and become a CTRL-C to cancel the command)
                    // let confirm = await this._ui.promptConfirm("continue", "Continue?", false);
                    // if (!confirm) {
                    //   this._ui.userMessage("Operation cancelled.  No work was done.");
                    //   this.exit(0);
                    //   return;
                    // }
                }
            }
            return;
        });
    }
    /** Hard-coded flag for debugging relay from an ide.
     *
     * ***THIS MUST ALWAYS BE FALSE WHEN COMMITTING CODE!!!!!***
     */
    get bareMetalRelay() {
        if (this._bareMetalRelay == undefined) {
            // TODO: true when relay ping succeeds while container image is NOT running.  
            // OR can we ask the relay whether its in a container or not?  Maybe its got an env var?
            this._bareMetalRelay = true;
        }
        return this._bareMetalRelay;
    }
}
exports.default = CspSetup;
CspSetup.description = 'Declares a Cloud Service Provider (CSP) account in Codiac and grants Codiac the right to operate there.  This command MUST be executed interactively with a user at the terminal who has administrative rights on the cloud service provider account. The user will be asked explicitly to authenticate and grant consent.  This command does NOT support silent mode.';
CspSetup.args = {
    provider: core_1.Args.string({ required: false, options: Object.keys(contracts_1.CloudProviderEnum), description: 'Code indicating the Cloud Service Provider (CSP) being authenticated with.' })
};
CspSetup.flags = {
    cspTenantId: core_1.Flags.string({ char: 't', required: false, description: "The system identifier for your csp account (eg: in Azure this is the Tenant ID guid; in AWS this is the 12-digit Account ID)." })
};
CspSetup.examples = [
    { description: "Interactively select the provider", command: '<%= config.bin %> <%= command.id %>' },
    '<%= config.bin %> <%= command.id %> azure'
];
CspSetup.hidden = true; // <---- UNTIL TIME FOR RELEASE
//# sourceMappingURL=setup.js.map