"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BootstrapperForCodiacCli = void 0;
require("reflect-metadata");
const path = require("path");
const fs = require("fs");
const envparser = require("dotenv");
const guid_typescript_1 = require("guid-typescript");
const codiac_common_1 = require("@codiac.io/codiac-common");
const IBetterLogger_1 = require("@codiac.io/codiac-common/IBetterLogger");
const better_logger_1 = require("@codiac.io/codiac-common/better-logger");
const cli_tools_1 = require("@codiac.io/cli-tools");
const codiac_relay_contracts_1 = require("@codiac.io/codiac-relay-contracts");
const codiac_ops_common_1 = require("@codiac.io/codiac-ops-common");
const ops_1 = require("./ops");
const entities_1 = require("./ops/entities");
const data_1 = require("./ops/data");
const built_in_cli_user_input_1 = require("./cli-common/built-in-cli-user-input");
const INTERFACES_CLI_1 = require("./ops/INTERFACES_CLI");
const codiac_constants_1 = require("./codiac-constants");
const relay_container_1 = require("./relay/relay-container");
const log4js_logger_1 = require("./log4js-logger");
const overlayUtils = require("./ops/overlayer");
const type_safety_utils_1 = require("./uilogic/type-safety-utils");
class BootstrapperForCodiacCli {
    /** (IDEMPOTENT) Initializes ioc (mutates it) with typical cli/codiac project entities. */
    static bootstrap(ioc, oclifConfig, requiresProjectScope) {
        if (this.isBootstrapped != true) {
            process.removeAllListeners('warning');
            const envPath = path.posix.join(oclifConfig.root, ops_1.OpsConstants.PROJECT_USER_CONFIG_FOLDER_NAME, ".env");
            if (fs.existsSync(envPath)) {
                this.loadEnvFiles([envPath]);
            }
            const localProgramFolder = path.normalize(codiac_ops_common_1.OpsUtils.resolveHomeDir(path.posix.join("~", codiac_constants_1.CliConstants.LOCAL_CONFIG_FOLDER_NAME)));
            ioc.bind(entities_1.CliEnviro).toDynamicValue(() => {
                return new entities_1.CliEnviro(oclifConfig.configDir, localProgramFolder, oclifConfig.version, relay_container_1.DEFAULT_IMAGE_TAG);
            }).inSingletonScope();
            ioc.bind(data_1.CliGlobalConfigRepo).toSelf(); // (() => new CliContextRepo(this.config.configDir, this._projectRoot));
            ioc.bind(entities_1.CliConfig).toDynamicValue(ctxt => {
                let cfgRepo = ctxt.container.get(data_1.CliGlobalConfigRepo);
                // ---------------------------
                // 0. Declare Defaults...
                // ---------------------------
                let cliConfig = entities_1.CliConfig.DEFAULTS;
                // ---------------------------
                // 1. Overlay with values in config file itself... 
                // ---------------------------
                let valuesFromFile = cfgRepo.read_sync() || new entities_1.CliConfig();
                cliConfig = type_safety_utils_1.TypeSafetyUtils.overlay(cliConfig, valuesFromFile, false);
                // ---------------------------
                // 2. Overlay with localConfig... 
                // (these are overrides from the install folder, 
                // for use in adjusting behaviors of the installed version)
                // ---------------------------
                cliConfig = this.overlayWithLocalConfig(cliConfig, localProgramFolder);
                // ---------------------------
                // 3. Overlay with ENV values...  
                // (these govern so that a bare-metal IDE instance 
                // can move and thrust independently from the installed version)
                // ---------------------------
                const mergeOptz = {
                    fromSource: {
                        sourceKeys: [],
                        targetObjectCase: overlayUtils.EnumCase.camelCase,
                        sourcePrefix: entities_1.CliConfig.ENV_PREFIX,
                        sourceHierarchyDelim: overlayUtils.Overlayer.ENV_HIERARCHY_DELIM
                    },
                    // fromTarget: {
                    //   targetPropertyNames: [],
                    //   sourceCase: overlayUtils.EnumCase.constantCase,
                    //   sourcePrefix: CliConfig.ENV_PREFIX,
                    //   sourceHierarchyDelim: overlayUtils.Overlayer.ENV_HIERARCHY_DELIM
                    // }
                };
                cliConfig = overlayUtils.Overlayer.overlay(cliConfig, process.env, mergeOptz);
                // ---------------------------
                // 4. Ensure typesafety on booleans...
                // ---------------------------
                cliConfig.addBranchNameToPrereleases = type_safety_utils_1.TypeSafetyUtils.toBoolean(cliConfig.addBranchNameToPrereleases);
                cliConfig.localEntities = type_safety_utils_1.TypeSafetyUtils.toBoolean(cliConfig.localEntities);
                cliConfig.skipRelayAutoStart = type_safety_utils_1.TypeSafetyUtils.toBoolean(cliConfig.skipRelayAutoStart);
                cliConfig.useLocalRelay = type_safety_utils_1.TypeSafetyUtils.toBoolean(cliConfig.useLocalRelay);
                return cliConfig;
            }); // .inSingletonScope();
            // PROJECT SCOPE (all nullables)
            ioc.bind(entities_1.ProjectEnviro).toDynamicValue(() => {
                let projRoot = ops_1.CliPathWalker.getProjectRoot(undefined, requiresProjectScope);
                return (projRoot === "") ? undefined : new entities_1.ProjectEnviro(projRoot);
            }).inSingletonScope();
            ioc.bind(data_1.ProjectContextRepo).toDynamicValue(ctx => {
                let projEnv = ctx.container.get(entities_1.ProjectEnviro);
                return projEnv ? new data_1.ProjectContextRepo(projEnv) : undefined;
            }); // toSelf();  // (() => new CliContextRepo(this.config.configDir, this._projectRoot));
            ioc.bind(codiac_relay_contracts_1.ProjectConfig).toDynamicValue(ctxt => {
                let cfgRepo = ctxt.container.get(data_1.ProjectContextRepo);
                return cfgRepo === null || cfgRepo === void 0 ? void 0 : cfgRepo.readProjectConfig();
            }); // .inSingletonScope();
            ioc.bind(codiac_relay_contracts_1.ProjectUserConfig).toDynamicValue(ctxt => {
                let cfgRepo = ctxt.container.get(data_1.ProjectContextRepo);
                return cfgRepo === null || cfgRepo === void 0 ? void 0 : cfgRepo.readProjectUserConfig();
            }); // .inSingletonScope();
            ioc.bind(entities_1.OperationMeta).toDynamicValue(ctxt => {
                return {
                    correlationId: guid_typescript_1.Guid.create().toString()
                };
            }).inSingletonScope(); // <-- singleton for CLI ONLY!!!
            ioc.bind(INTERFACES_CLI_1.INTERFACES_CLI.IUserInputProvider).to(built_in_cli_user_input_1.BuiltInCliUserInput);
            ioc.bind(INTERFACES_CLI_1.INTERFACES_CLI.IOutputStyler).to(cli_tools_1.TtyOutputStyler);
            ioc.bind(IBetterLogger_1.LoggerSettings).toDynamicValue(ctxt => {
                // INITIALIZE LOGGER
                let settings = new IBetterLogger_1.LoggerSettings([IBetterLogger_1.LogLevel.notice, IBetterLogger_1.LogLevel.warn, IBetterLogger_1.LogLevel.error, IBetterLogger_1.LogLevel.fatal]);
                const cliCfg = ctxt.container.get(entities_1.CliConfig);
                if (cliCfg.verbosity) {
                    switch (cliCfg.verbosity) {
                        case entities_1.Verbosity.detail:
                            settings.LogLevelsToShow.push(IBetterLogger_1.LogLevel.detail);
                            break;
                        case entities_1.Verbosity.debug:
                            settings.LogLevelsToShow.push(IBetterLogger_1.LogLevel.detail);
                            settings.LogLevelsToShow.push(IBetterLogger_1.LogLevel.debug);
                            settings.LogLevelsToShow.push(IBetterLogger_1.LogLevel.trace);
                            break;
                    }
                }
                return settings;
            }).inSingletonScope();
            // ioc.bind<LoggerSettings>(LoggerSettings).toConstantValue(settings);
            if (ioc.isBound(codiac_common_1.INTERFACES.IBetterLogger) !== true) {
                ioc.bind(codiac_common_1.INTERFACES.IBetterLogger).to(better_logger_1.BetterConsoleLogger);
            }
            // TODO: move this to BootstrapperForCodiacCli
            log4js_logger_1.Log4jsLogger.bootstrap(ioc, {
                appenders: {
                    "console": { type: "console" },
                    "applog": { type: "file", maxLogSize: "2M", backups: 3, compress: false, keepFileExt: true, filename: path.posix.join(localProgramFolder, "logs", "cli.log") },
                    "errlog": { type: "file", maxLogSize: "2M", backups: 3, compress: false, keepFileExt: true, filename: path.posix.join(localProgramFolder, "logs", "cli-error.log") }
                },
                categories: {
                    default: { appenders: ["applog"], level: "trace" },
                    errors: { appenders: ["errlog"], level: "error" },
                    console: { appenders: ["console"], level: "off" }
                }
            });
            codiac_ops_common_1.OpsCommon.bootstrap(ioc);
            this.isBootstrapped = true;
        }
    }
    static overlayWithLocalConfig(cliConfig, localProgramFolder) {
        // Load local config file
        let localOverridesPathname = path.posix.join(localProgramFolder, codiac_constants_1.CliConstants.LOCAL_CONFIG_OVERRIDES_FILE_NAME);
        let localConfigRaw = codiac_ops_common_1.JsonUtils.deserializeJsonFile(localOverridesPathname);
        // REMAP 
        // localConfig properties have drifted 
        // and dont match the CliConfig class one-for-one, 
        // so we gotta fix it up before doing the overlay...
        const localConfig = {
            apiClientKind: localConfigRaw === null || localConfigRaw === void 0 ? void 0 : localConfigRaw.apiClientKind,
            codiacApiBaseUrl: localConfigRaw === null || localConfigRaw === void 0 ? void 0 : localConfigRaw.apiUrl,
            codiacNatsUrl: localConfigRaw === null || localConfigRaw === void 0 ? void 0 : localConfigRaw.apiNatsUrl,
            localEntities: (localConfigRaw === null || localConfigRaw === void 0 ? void 0 : localConfigRaw.localEntities) ? type_safety_utils_1.TypeSafetyUtils.toBoolean(localConfigRaw === null || localConfigRaw === void 0 ? void 0 : localConfigRaw.localEntities) : undefined,
            // authApiBaseUrl: localConfigRaw?.authApiUrl /* <-- NOT USED BY CLI */,
            // : localConfigRaw?.relayPort /* <-- NOT USED BY CLI */,
            // : localConfigRaw?.relayWebSocketPort /* <-- NOT USED BY CLI */
            natsRequestTimeoutInMs: localConfigRaw === null || localConfigRaw === void 0 ? void 0 : localConfigRaw.natsRequestTimeoutInMs,
            natsConnectTimeoutInMs: localConfigRaw === null || localConfigRaw === void 0 ? void 0 : localConfigRaw.natsConnectTimeoutInMs,
            useLocalRelay: localConfigRaw === null || localConfigRaw === void 0 ? void 0 : localConfigRaw.useLocalRelay,
            skipRelayAutoStart: localConfigRaw === null || localConfigRaw === void 0 ? void 0 : localConfigRaw.skipRelayAutoStart
        };
        const modified = type_safety_utils_1.TypeSafetyUtils.overlay(cliConfig, localConfig);
        return modified;
    }
    /** Uses dotenv to load env variables from a file into the process.env.
     * Throws on error, ignores empty pathnames.
     *
     */
    static loadEnvFiles(envPathNames, ignoreErrors = false, logger) {
        var _a;
        if (envPathNames && envPathNames.length > 0) {
            for (let rawPath of envPathNames) {
                const envFqn = path.isAbsolute(rawPath) ? rawPath : path.join(process.cwd(), rawPath);
                if (fs.existsSync(envFqn)) {
                    const result = envparser.config({ path: envFqn });
                    if (result.error) {
                        if (ignoreErrors === true) {
                            logger === null || logger === void 0 ? void 0 : logger.warn("Error loading env file: ".concat((_a = result.error.message) !== null && _a !== void 0 ? _a : "(no error message available)"));
                        }
                        else {
                            throw result.error;
                        }
                    }
                    else if (result.parsed) {
                        logger === null || logger === void 0 ? void 0 : logger.detail("Loaded ".concat(Object.keys(result.parsed).length.toString(), " env variables from [", envFqn, "]"));
                    }
                }
                else {
                    if (ignoreErrors === true) {
                        logger === null || logger === void 0 ? void 0 : logger.warn("Error loading env file: ".concat("Invalid console argument: env file not found."));
                    }
                    else {
                        throw new Error("Error loading env file: env file not found.");
                    }
                }
            }
        }
    }
}
exports.BootstrapperForCodiacCli = BootstrapperForCodiacCli;
BootstrapperForCodiacCli.isBootstrapped = false;
//# sourceMappingURL=bootstrapper-for-codiac-cli.js.map