"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserContextManager = exports.UserContextLoader = void 0;
const tslib_1 = require("tslib");
const path = require("path");
const fs = require("fs");
const codiac_relay_contracts_1 = require("@codiac.io/codiac-relay-contracts");
const codiac_ops_common_1 = require("@codiac.io/codiac-ops-common");
const codiac_constants_1 = require("./codiac-constants");
/** Simply deserializes the currently cached user context from its config file.
 * This class exists so that the load methods can be called before an api client gets instantiated. */
class UserContextLoader {
    constructor(cliEnviro, logger) {
        this.cliEnviro = cliEnviro;
        this.logger = logger;
    }
    /** (context is Machine-User scoped)
     *  Attempts to load the user and select from their tenant tokens and enterprise contexts persisted locally by the CLI.
     *  Returns undefined for not found and throws any encountered exceptions.
     *
     * The full user context will STILL be returned no matter what,
     * whether or not there was a matching tenant profile.
     * But if the context has no matching tenant profile,
     * the CURRENT tenantCode, token, and enterprise will be undefined (ie: a tenantless user, a principal user).
     *
     * This way, if the cache is stale, the current user can still authenticate to refresh it.
     * eg: if the user was just seconds ago added to a tenant,
     * this pattern can pick up the new profile from the remote auth service without requiring a manual login.
     *
     * @param tenantCode Enforces the given tenantCode. When set, current context will be ignored if it does not match it.
     * */
    loadUserContext(tenantCode) {
        // let output: UserContext | undefined;
        // let homeDir = "~/"; // path.posix.resolve("~/");  // NOTE:  This tilde (~) may not be valid on a windows box.
        // let pathName = path.posix.join(homeDir, OpsConstants.CLI_CONFIG_FOLDER_NAME, CliConstants.USER_CONTEXT_FILENAME);
        let pathName = path.posix.join(this.cliEnviro.localProgramFolder, codiac_constants_1.CliConstants.USER_CONTEXT_FILENAME);
        this.logger.trace(`Reading ${pathName}...`);
        let machineUserContext = codiac_ops_common_1.JsonUtils.deserializeJsonFile(pathName);
        if (machineUserContext) {
            if (tenantCode === undefined) {
                // output = machineUserContext;
            }
            else { // ENFORCE the given tenantCode
                if (machineUserContext.currentTenantCode === tenantCode) {
                    // output = machineUserContext;
                }
                else {
                    this.trySetCurrentProfileFor(tenantCode, machineUserContext);
                    // output = machineUserContext;
                }
            }
            this.logger.trace(`Current user context (from cache).  User: ${machineUserContext.userName}, TenantCode: ${machineUserContext.currentTenantCode}`);
        }
        return machineUserContext; // output;
    }
    /** Mutates the given object's current tenant properties
     * (currentToken, currentEnterprise, currentTenantCode, tokenExpiration)
     * to those of the current tenant code,
     * by either allowing the current state,
     * or by applying any relevant tenant profile that exists onboard the given context object.
     *
     * If no matching profile exists, "current" properties will be set to undefined (like a principal user),
     * but the context itself WILL STILL BE RETURNED.
    */
    trySetCurrentProfileFor(tenantCode, userContext) {
        userContext.currentEnterprise = userContext.enterpriseContexts[tenantCode];
        userContext.currentToken = userContext.tokens[tenantCode];
        // These expirations really need to be stored in the tokens collection
        // machineUserContext.tokenExpiration = (machineUserContext.currentToken) ? parseTokenExpiration(machineUserContext.currentToken) : undefined;
        userContext.tokenExpiration = undefined;
        // Only set the tenant code if they have a profile for it
        userContext.currentTenantCode = undefined;
        if ((userContext.currentToken !== undefined && userContext.currentToken !== "")
            || (userContext.currentEnterprise !== undefined)) {
            userContext.currentTenantCode = tenantCode;
        }
    }
    /** Obliterates the current user state by deleting the context file. */
    clearUserContext() {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return new Promise((resolve, reject) => {
                try {
                    // let homeDir = "~/"; // path.posix.resolve("~/");  // NOTE:  This tilde (~) may not be valid on a windows box.
                    // let pathName = path.posix.join(OpsUtils.resolveHomeDir(homeDir), OpsConstants.CLI_CONFIG_FOLDER_NAME, CliConstants.USER_CONTEXT_FILENAME);
                    let pathName = path.posix.join(this.cliEnviro.localProgramFolder, codiac_constants_1.CliConstants.USER_CONTEXT_FILENAME);
                    pathName = path.normalize(pathName);
                    if (fs.existsSync(pathName)) {
                        fs.rm(pathName, { force: true }, () => {
                            this.logger.debug(`User context file deleted [${pathName}].`);
                            resolve();
                        });
                    }
                    else {
                        this.logger.debug(`Nothing to do: user context file does not exist at [${pathName}]`);
                        resolve();
                    }
                }
                catch (err) {
                    reject(err);
                }
            });
        });
    }
}
exports.UserContextLoader = UserContextLoader;
/** Provides cruds for the user context, persisting to the local context config file. */
class UserContextManager {
    constructor(cliEnviro, authClient, logger) {
        this.cliEnviro = cliEnviro;
        this.authClient = authClient;
        this.logger = logger;
        this.loader = new UserContextLoader(this.cliEnviro, this.logger);
    }
    /** Extends the user token so we can get the user's tenant profiles.
     *  If the token is expired, or something has changed, it fails.
     *  Throws any exceptions.
     */
    refreshUserContext(currentContext) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return new Promise((resolve, reject) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                try {
                    let result = {};
                    if (!currentContext)
                        currentContext = new codiac_relay_contracts_1.UserContext();
                    if (currentContext.currentToken) {
                        let refreshResult = yield this.authClient.Auth.TokenRefresh({});
                        if (refreshResult.message)
                            this.logger.debug(`Token refresh: ${refreshResult.message}`);
                        if (refreshResult.output) {
                            result.user = refreshResult.output; // We're not getting the user.profiles here
                        }
                        if (refreshResult.success && refreshResult.output) {
                            currentContext.currentToken = this.authClient.authToken;
                            if (currentContext.currentToken)
                                currentContext.tokenExpiration = this.authClient.extractTokenExpiration(currentContext.currentToken);
                            this.writeUserContext(currentContext);
                            result.context = currentContext;
                        }
                    }
                    resolve(result);
                }
                catch (err) {
                    reject(err);
                }
            }));
        });
    }
    /** Writes the given UserContext to the Toyhauler user config folder on the local machine.
     * TODO:  Consider writing this to the cache folder instead of the config folder.
     * Throws any encountered exceptions. */
    writeUserContext(userContext) {
        // let pathName = path.join("~/", OpsConstants.CLI_CONFIG_FOLDER_NAME, CliConstants.USER_CONTEXT_FILENAME);
        let pathName = path.join(this.cliEnviro.localProgramFolder, codiac_constants_1.CliConstants.USER_CONTEXT_FILENAME);
        this.logger.debug(`Writing UserContext to ${pathName}`);
        codiac_ops_common_1.JsonUtils.serializeJsonFile(userContext, pathName);
    }
    /** Persists the current user, its token, and its tenantCode to the user context
     *  and the userContext file stored on the local machine. */
    saveToUserContext(currentContext, userName, token, tenantCode, tokenExpiration, roles) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            return new Promise((resolve, reject) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                try {
                    // if (userName) {
                    if (!currentContext) { // wanna load it here?  this._cntx = await this.loadUserContext(user.tenantCode);
                        currentContext = new codiac_relay_contracts_1.UserContext(userName, tenantCode, token, tokenExpiration, undefined, roles);
                    }
                    else {
                        currentContext.userName = userName;
                        currentContext.currentTenantCode = tenantCode;
                        currentContext.currentToken = token;
                        currentContext.tokenExpiration = tokenExpiration;
                        currentContext.roles = roles;
                        if (tenantCode)
                            currentContext.tokens[tenantCode] = token;
                    }
                    yield this.writeUserContext(currentContext);
                    // } else {
                    //   // null input - do nothing.
                    //   TODO:  Consider either throwing an error here OR changing signature to return an undefined.
                    // }
                    resolve(currentContext);
                }
                catch (err) {
                    reject(err);
                }
            }));
        });
    }
}
exports.UserContextManager = UserContextManager;
//# sourceMappingURL=user-context-loader.js.map