"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CliConfigProvider = exports.AUTH_KEY_NAMES = exports.BROWSER_CONFIG_KEY = exports.IS_CONFIG_ENCRYPTED_KEY_NAME = void 0;
exports.getEncryptionKey = getEncryptionKey;
exports.getAuthConfigFile = getAuthConfigFile;
exports.__resetIsKeytarAvailable = __resetIsKeytarAvailable;
const env_1 = require("../env/env");
const conf_1 = __importDefault(require("conf"));
const types_1 = require("./types");
const types_2 = require("../api/types");
const configKeys_1 = require("./configKeys");
const httpUtil_1 = require("../http/httpUtil");
const crypto = __importStar(require("crypto"));
const loggingProvider_1 = require("./logging/loggingProvider");
const utilities_1 = require("../utilities");
const USER_ID_NAME = 'userId';
const USER_FULL_NAME_NAME = 'userFullName';
const EMAIL_NAME = 'email';
const WORKSPACE = 'workspace';
const AUTH_PROVIDER = 'authProvider';
const AUTH_TYPE = 'authType';
const ACCESS_TOKEN_NAME = 'accessToken';
const REFRESH_TOKEN_NAME = 'refreshToken';
const ACCESS_TOKEN_EXPIRATION_MILLISECONDS_NAME = 'accessTokenExpirationTimeMilliseconds';
const REFRESH_TOKEN_EXPIRATION_MILLISECONDS_NAME = `${REFRESH_TOKEN_NAME}ExpirationTimeMilliseconds`;
exports.IS_CONFIG_ENCRYPTED_KEY_NAME = '_isConfigEncrypted';
exports.BROWSER_CONFIG_KEY = 'browser.path';
exports.AUTH_KEY_NAMES = [
    AUTH_PROVIDER,
    AUTH_TYPE,
    ACCESS_TOKEN_NAME,
    ACCESS_TOKEN_EXPIRATION_MILLISECONDS_NAME,
    REFRESH_TOKEN_NAME,
    REFRESH_TOKEN_EXPIRATION_MILLISECONDS_NAME,
    USER_FULL_NAME_NAME,
    USER_ID_NAME,
    EMAIL_NAME,
];
let isKeytarAvailable = true;
async function getEncryptionKey() {
    if ((0, utilities_1.areWeTestingInJest)()) {
        return;
    }
    let maybeEncryptionKey;
    if (isKeytarAvailable) {
        try {
            const keytar = require('keytar');
            maybeEncryptionKey =
                (await keytar.getPassword(env_1.CONF_FILE_PROJECT_NAME, env_1.ENV)) ?? undefined;
            if (!maybeEncryptionKey) {
                maybeEncryptionKey = crypto
                    .randomFillSync(Buffer.alloc(48))
                    .toString('base64');
                await keytar.setPassword(env_1.CONF_FILE_PROJECT_NAME, env_1.ENV, maybeEncryptionKey);
            }
        }
        catch (error) {
            loggingProvider_1.logger.warn(`Creating finite login session because login auto-renewal is unsupported on this system.`);
            isKeytarAvailable = false;
        }
    }
    return maybeEncryptionKey;
}
async function getAuthConfigFile() {
    return getNonEncryptedConfig();
    const maybeEncryptionKey = await getEncryptionKey();
    if (!isKeytarAvailable || (0, utilities_1.areWeTestingInJest)()) {
        return getNonEncryptedConfig();
    }
    const conf = new conf_1.default({
        configName: 'access-enc',
        projectName: env_1.CONF_FILE_PROJECT_NAME,
        projectVersion: env_1.CONF_FILE_VERSION,
        accessPropertiesByDotNotation: false,
        encryptionKey: maybeEncryptionKey,
    });
    if (conf.has(exports.IS_CONFIG_ENCRYPTED_KEY_NAME)) {
        return conf;
    }
    const oldConf = getNonEncryptedConfig();
    copyConf(oldConf, conf);
    conf.set(exports.IS_CONFIG_ENCRYPTED_KEY_NAME, true);
    return conf;
}
function shouldStoreRefreshTokens() {
    return isKeytarAvailable || (0, utilities_1.areWeTestingInJest)();
}
function getNonEncryptedConfig() {
    return new conf_1.default({
        configName: 'access',
        projectName: env_1.CONF_FILE_PROJECT_NAME,
        projectVersion: env_1.CONF_FILE_VERSION,
        accessPropertiesByDotNotation: false,
    });
}
function copyConf(confFrom, confTo) {
    for (const key of [...exports.AUTH_KEY_NAMES, ...configKeys_1.validConfigKeyChoices]) {
        const value = confFrom.get(key);
        if (value !== undefined) {
            confTo.set(key, value);
        }
    }
}
class CliConfigProvider {
    static setWorkspace(workspace) {
        return setValue(WORKSPACE, workspace);
    }
    static clearWorkspace() {
        return clearValue(WORKSPACE);
    }
    static async getWorkspace() {
        return (await getAuthConfigFile()).get(WORKSPACE);
    }
    static async getProxyMode() {
        const proxyMode = await this.getConfigProperty(configKeys_1.configKeys.proxyMode);
        return (0, configKeys_1.isValidProxyMode)(proxyMode) ? proxyMode : 'mabl';
    }
    static async getProxyType() {
        const proxyType = await this.getConfigProperty(configKeys_1.configKeys.proxyType);
        return (0, types_2.isValidProxyType)(proxyType) ? proxyType : 'legacy';
    }
    static getConfigProperty(key) {
        return getValue(propertyKeyToStoredKey(key));
    }
    static clearConfigProperty(key) {
        return clearValue(propertyKeyToStoredKey(key));
    }
    static setConfigProperty(key, value) {
        return setValue(propertyKeyToStoredKey(key), value);
    }
    static async getCliConfig() {
        const config = await getAuthConfigFile();
        const authType = config.get(AUTH_TYPE);
        let authProvider = config.get(AUTH_PROVIDER);
        if (!authProvider && authType !== undefined) {
            authProvider =
                authType === types_2.AuthType.ApiKey
                    ? types_1.AuthProviderType.Mabl
                    : types_1.AuthProviderType.Auth0;
        }
        const authentication = {
            authProvider,
            authType,
            accessToken: config.get(ACCESS_TOKEN_NAME),
            refreshToken: config.get(REFRESH_TOKEN_NAME),
            accessTokenExpirationTimeMilliseconds: config.get(ACCESS_TOKEN_EXPIRATION_MILLISECONDS_NAME),
            refreshTokenExpirationTimeMilliseconds: config.get(REFRESH_TOKEN_EXPIRATION_MILLISECONDS_NAME),
        };
        const proxyType = await this.getProxyType();
        const httpConfig = {
            sslVerify: httpUtil_1.EXECUTION_ENGINE_SSL_VERIFY,
            proxyType,
        };
        const sslVerifyValue = await this.getConfigProperty(configKeys_1.configKeys.sslVerify);
        if (sslVerifyValue !== undefined) {
            if (typeof sslVerifyValue !== 'boolean') {
                throw new Error(`Invalid proxy sslVerify value`);
            }
            httpConfig.sslVerify = sslVerifyValue;
        }
        const mablHttpConfig = { ...httpConfig };
        const testHttpConfig = { ...httpConfig };
        const proxyHostValue = await this.getConfigProperty(configKeys_1.configKeys.proxy);
        const proxyMode = await this.getProxyMode();
        if (proxyHostValue) {
            if (typeof proxyHostValue !== 'string') {
                throw new Error('Invalid proxy host value');
            }
            if (proxyMode === 'mabl' || proxyMode === 'all') {
                mablHttpConfig.proxyHost = new URL(proxyHostValue);
            }
            if (proxyMode === 'test' || proxyMode === 'all') {
                testHttpConfig.proxyHost = new URL(proxyHostValue);
            }
        }
        return {
            authentication,
            userId: config.get(USER_ID_NAME),
            email: config.get(EMAIL_NAME),
            userFullName: config.get(USER_FULL_NAME_NAME),
            workspace: config.get(WORKSPACE),
            http: { mabl: mablHttpConfig, test: testHttpConfig },
        };
    }
    static clearAuthConfig() {
        exports.AUTH_KEY_NAMES.forEach((keyName) => clearValue(keyName));
        return this.getCliConfig();
    }
    static async setCliConfig(config) {
        await setValue(AUTH_PROVIDER, config?.authentication?.authProvider);
        await setValue(AUTH_TYPE, config?.authentication?.authType);
        await setValue(ACCESS_TOKEN_NAME, config?.authentication?.accessToken);
        await setValue(REFRESH_TOKEN_NAME, config?.authentication?.refreshToken);
        await setValue(ACCESS_TOKEN_EXPIRATION_MILLISECONDS_NAME, config?.authentication?.accessTokenExpirationTimeMilliseconds);
        await setValue(REFRESH_TOKEN_EXPIRATION_MILLISECONDS_NAME, config?.authentication?.refreshTokenExpirationTimeMilliseconds);
        await setValue(USER_ID_NAME, config.userId);
        await setValue(EMAIL_NAME, config.email);
        await setValue(USER_FULL_NAME_NAME, config.userFullName);
        return config;
    }
    static async setCliAuthInfo(authInfo, updateMilliseconds) {
        await setValue(AUTH_TYPE, authInfo?.auth_type);
        switch (authInfo.auth_type) {
            case types_2.AuthType.Bearer:
                const bearerAuthInfo = authInfo;
                const expirationTimeMilliseconds = updateMilliseconds + bearerAuthInfo.expires_in * 1000;
                await setValue(AUTH_PROVIDER, bearerAuthInfo.auth_provider);
                await setValue(ACCESS_TOKEN_NAME, bearerAuthInfo?.access_token);
                await setValue(REFRESH_TOKEN_NAME, bearerAuthInfo?.refresh_token);
                await setValue(ACCESS_TOKEN_EXPIRATION_MILLISECONDS_NAME, expirationTimeMilliseconds);
                await setValue(REFRESH_TOKEN_EXPIRATION_MILLISECONDS_NAME, bearerAuthInfo.refresh_token_expires_at
                    ? bearerAuthInfo.refresh_token_expires_at * 1000
                    : undefined);
                return;
            case types_2.AuthType.ApiKey:
                const apiKeyAuthInfo = authInfo;
                await setValue(AUTH_PROVIDER, apiKeyAuthInfo.auth_provider);
                await setValue(ACCESS_TOKEN_NAME, apiKeyAuthInfo.api_key);
                return;
        }
    }
}
exports.CliConfigProvider = CliConfigProvider;
function propertyKeyToStoredKey(key) {
    return `properties.${key}`;
}
async function setValue(key, value) {
    if (value === undefined) {
        return clearValue(key);
    }
    if (!shouldStoreRefreshTokens() && key.includes(REFRESH_TOKEN_NAME)) {
        return;
    }
    (await getAuthConfigFile()).set(key, value);
}
async function getValue(key) {
    return (await getAuthConfigFile()).get(key);
}
async function clearValue(key) {
    (await getAuthConfigFile()).delete(key);
}
function __resetIsKeytarAvailable() {
    isKeytarAvailable = true;
}
