"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getGlobalLogHandler = exports.getLogger = exports.globalLogHandler = void 0;
/**
 * Module to build the data structures to eventually send to Honeycomb.
 */
const js_sdk_logging_1 = require("@optimizely/js-sdk-logging");
exports.globalLogHandler = new js_sdk_logging_1.ConsoleLogHandler({
    prefix: '[EDGE DECIDER]',
});
/**
 * Skip cicular references in JSON.stringify
 * @returns Replacer function
 */
function safeStringify() {
    const seen = new WeakSet();
    return (key, value) => {
        if (value instanceof Set) {
            return Array.from(value.values());
        }
        else if (value instanceof Map) {
            return Array.from(value.entries());
        }
        else if (typeof value === 'object' && value !== null) {
            if (seen.has(value)) {
                return '[circular ref]';
            }
            seen.add(value);
        }
        return value;
    };
}
exports.globalLogHandler.log = function (level, message, ...rest) {
    if (!this.shouldLog(level) || !this.logToConsole) {
        return;
    }
    message = [message, ...rest]
        .map(arg => {
        if (arg instanceof Object) {
            try {
                return JSON.stringify(arg, safeStringify());
            }
            catch (error) {
                this.consoleLog(js_sdk_logging_1.LogLevel.WARNING, [
                    'Error while stringifying object',
                    error,
                ]);
                return String(arg);
            }
        }
        return arg;
    })
        .join(', ');
    var logMessage = this.prefix +
        ' - ' +
        this.getLogLevelName(level) +
        ' ' +
        this.getTime() +
        ' ' +
        message;
    this.consoleLog(level, [logMessage]);
};
exports.globalLogHandler.setLogLevel(js_sdk_logging_1.LogLevel.DEBUG);
const LoggersStateMap = new WeakMap();
var stringToLogLevel = {
    NOTSET: 0,
    DEBUG: 1,
    INFO: 2,
    WARNING: 3,
    ERROR: 4,
};
function coerceLogLevel(level) {
    if (typeof level !== 'string') {
        return level;
    }
    level = level.toUpperCase();
    if (level === 'WARN') {
        level = 'WARNING';
    }
    if (!stringToLogLevel[level]) {
        return level;
    }
    return stringToLogLevel[level];
}
class Logger {
    constructor() {
        this.browserLogLevel = js_sdk_logging_1.LogLevel.ERROR;
        this.logState = {
            decisions: [],
            logs: [],
            lifeCycle: [],
            metadata: {},
            views: {
                evaluation: [],
                active: [],
            },
        };
    }
    setLogLevel(level) {
        this.browserLogLevel = coerceLogLevel(level);
        if (!this.browserLogLevel) {
            this.browserLogLevel = js_sdk_logging_1.LogLevel.ERROR;
        }
    }
    getLogState() {
        return this.logState;
    }
    getLifeCycleLogs() {
        return this.logState.lifeCycle || [];
    }
    setLogKey(type, key, value) {
        this.logState[key] = value;
    }
    updateMetadata(key, value) {
        let metadata = this.logState.metadata || {};
        metadata[key] = value;
        this.setLogKey('metadata', metadata);
    }
    logLifeCycleEvent(eventMsg, isError = false) {
        if (!isError)
            this.info(eventMsg);
        else
            this.error(eventMsg);
        this.logState.lifeCycle.push(eventMsg);
    }
    logViewEvaluation(viewId, url, isActive, reason) {
        let viewState = { viewId, url, isActive, reason };
        this.logState.views.evaluation.push(viewState);
        if (isActive) {
            this.logState.views.active.push(viewId);
            this.logLifeCycleEvent(`View ${viewId} is active for url ${url}`);
        }
        else {
            this.logLifeCycleEvent(`View ${viewId} is not active for url ${url}. Reason: ${reason}`);
        }
    }
    logDecision(decision) {
        this.logLifeCycleEvent(`Selected variation ${decision.variationId} in experiment ${decision.experimentId} (layer ${decision.layerId}) for view ${decision.viewId}`);
        this.logState.decisions.push(decision);
    }
    logError(error) {
        let name = error.name || 'InternalError';
        let errorObj = {
            errorClass: name,
            message: error.message,
            stacktrace: error.stack || null,
        };
        this.logLifeCycleEvent(`Error encountered. ${JSON.stringify(errorObj)}`, true);
    }
    debug(message, ...rest) {
        exports.globalLogHandler.log(js_sdk_logging_1.LogLevel.DEBUG, message, ...rest);
        this.logState.logs.push({ level: js_sdk_logging_1.LogLevel.DEBUG, message, rest });
    }
    info(message, ...rest) {
        exports.globalLogHandler.log(js_sdk_logging_1.LogLevel.INFO, message, ...rest);
        this.logState.logs.push({ level: js_sdk_logging_1.LogLevel.INFO, message, rest });
    }
    warning(message, ...rest) {
        exports.globalLogHandler.log(js_sdk_logging_1.LogLevel.WARNING, message, ...rest);
        this.logState.logs.push({ level: js_sdk_logging_1.LogLevel.WARNING, message, rest });
    }
    error(message, ...rest) {
        exports.globalLogHandler.log(js_sdk_logging_1.LogLevel.ERROR, message, ...rest);
        this.logState.logs.push({ level: js_sdk_logging_1.LogLevel.ERROR, message, rest });
    }
    getLogs() {
        // All logs needs to be pushed and then filter because
        // there are some logs added before setting the browser log level
        return this.logState.logs.filter(log => log.level >= this.browserLogLevel);
    }
}
function getLogger(cfEvent) {
    let logger = LoggersStateMap.get(cfEvent) || {};
    if (!Object.getOwnPropertyDescriptor(logger, 'getLogState')) {
        logger = new Logger();
        LoggersStateMap.set(cfEvent, logger);
    }
    return logger;
}
exports.getLogger = getLogger;
function getGlobalLogHandler() {
    return {
        debug: (...args) => {
            exports.globalLogHandler.log(js_sdk_logging_1.LogLevel.DEBUG, ...args);
        },
        info: (...args) => {
            exports.globalLogHandler.log(js_sdk_logging_1.LogLevel.INFO, ...args);
        },
        warning: (...args) => {
            exports.globalLogHandler.log(js_sdk_logging_1.LogLevel.WARNING, ...args);
        },
        error: (...args) => {
            exports.globalLogHandler.log(js_sdk_logging_1.LogLevel.ERROR, ...args);
        },
    };
}
exports.getGlobalLogHandler = getGlobalLogHandler;
exports.default = {
    Logger,
    getLogger,
    getGlobalLogHandler,
    globalLogHandler: exports.globalLogHandler,
};
