/*
 * Application Insights JavaScript SDK - Core, 3.2.0
 * Copyright (c) Microsoft and contributors. All rights reserved.
 */


import { dumpObj, isUndefined, objDefine, objForEachKey } from "@nevware21/ts-utils";
import { createUniqueNamespace } from "../JavaScriptSDK/DataCacheHelper";
import { STR_NOT_DYNAMIC_ERROR } from "../JavaScriptSDK/InternalConstants";
import { _DYN_BLK_VAL, _DYN_LOGGER, _DYN_NOTIFY, _DYN_RD_ONLY, _DYN_SET_DF, _DYN_THROW_INTERNAL, _DYN_WARN_TO_CONSOLE, _DYN_WATCH } from "../__DynamicConstants";
import { _applyDefaultValue } from "./ConfigDefaults";
import { _makeDynamicObject, _setDynamicProperty, _setDynamicPropertyState, _throwDynamicError } from "./DynamicProperty";
import { _createState } from "./DynamicState";
import { CFG_HANDLER_LINK, _cfgDeepCopy, getDynamicConfigHandler, throwInvalidAccess } from "./DynamicSupport";
/**
 * Identifies a function which will be re-called whenever any of it's accessed configuration values
 * change.
 * @param configHandler - The callback that will be called for the initial request and then whenever any
 * accessed configuration changes are identified.
 */
function _createAndUseHandler(state, configHandler) {
    var handler = {
        fn: configHandler,
        rm: function () {
            // Clear all references to the handler so it can be garbage collected
            // This will also cause this handler to never get called and eventually removed
            handler.fn = null;
            state = null;
            configHandler = null;
        }
    };
    objDefine(handler, "toJSON", { v: function () { return "WatcherHandler" + (handler.fn ? "" : "[X]"); } });
    state.use(handler, configHandler);
    return handler;
}
/**
 * Creates the dynamic config handler and associates with the target config as the root object
 * @param target - The config that you want to be root of the dynamic config
 * @param inPlace - Should the passed config be converted in-place or a new proxy returned
 * @returns The existing dynamic handler or a new instance with the provided config values
 */
function _createDynamicHandler(logger, target, inPlace) {
    var _a;
    var dynamicHandler = getDynamicConfigHandler(target);
    if (dynamicHandler) {
        // The passed config is already dynamic so return it's tracker
        return dynamicHandler;
    }
    var uid = createUniqueNamespace("dyncfg", true);
    var newTarget = (target && inPlace !== false) ? target : _cfgDeepCopy(target);
    var theState;
    function _notifyWatchers() {
        theState[_DYN_NOTIFY /* @min:%2enotify */]();
    }
    function _setValue(target, name, value) {
        try {
            target = _setDynamicProperty(theState, target, name, value);
        }
        catch (e) {
            // Unable to convert to dynamic property so just leave as non-dynamic
            _throwDynamicError(logger, name, "Setting value", e);
        }
        return target[name];
    }
    function _watch(configHandler) {
        return _createAndUseHandler(theState, configHandler);
    }
    function _block(configHandler, allowUpdate) {
        theState.use(null, function (details) {
            var prevUpd = theState.upd;
            try {
                if (!isUndefined(allowUpdate)) {
                    theState.upd = allowUpdate;
                }
                configHandler(details);
            }
            finally {
                theState.upd = prevUpd;
            }
        });
    }
    function _ref(target, name) {
        var _a;
        // Make sure it's dynamic and mark as referenced with it's current value
        return _setDynamicPropertyState(theState, target, name, (_a = {}, _a[0 /* _eSetDynamicPropertyFlags.inPlace */] = true, _a))[name];
    }
    function _rdOnly(target, name) {
        var _a;
        // Make sure it's dynamic and mark as readonly with it's current value
        return _setDynamicPropertyState(theState, target, name, (_a = {}, _a[1 /* _eSetDynamicPropertyFlags.readOnly */] = true, _a))[name];
    }
    function _blkPropValue(target, name) {
        var _a;
        // Make sure it's dynamic and mark as readonly with it's current value
        return _setDynamicPropertyState(theState, target, name, (_a = {}, _a[2 /* _eSetDynamicPropertyFlags.blockDynamicProperty */] = true, _a))[name];
    }
    function _applyDefaults(theConfig, defaultValues) {
        if (defaultValues) {
            // Resolve/apply the defaults
            objForEachKey(defaultValues, function (name, value) {
                // Sets the value and makes it dynamic (if it doesn't already exist)
                _applyDefaultValue(cfgHandler, theConfig, name, value);
            });
        }
        return theConfig;
    }
    var cfgHandler = (_a = {
            uid: null,
            cfg: newTarget
        },
        _a[_DYN_LOGGER /* @min:logger */] = logger,
        _a[_DYN_NOTIFY /* @min:notify */] = _notifyWatchers,
        _a.set = _setValue,
        _a[_DYN_SET_DF /* @min:setDf */] = _applyDefaults,
        _a[_DYN_WATCH /* @min:watch */] = _watch,
        _a.ref = _ref,
        _a[_DYN_RD_ONLY /* @min:rdOnly */] = _rdOnly,
        _a[_DYN_BLK_VAL /* @min:blkVal */] = _blkPropValue,
        _a._block = _block,
        _a);
    objDefine(cfgHandler, "uid", {
        c: false,
        e: false,
        w: false,
        v: uid
    });
    theState = _createState(cfgHandler);
    // Setup tracking for all defined default keys
    _makeDynamicObject(theState, newTarget, "config", "Creating");
    return cfgHandler;
}
/**
 * Log an invalid access message to the console
 * @param message
 */
function _logInvalidAccess(logger, message) {
    if (logger) {
        logger[_DYN_WARN_TO_CONSOLE /* @min:%2ewarnToConsole */](message);
        logger[_DYN_THROW_INTERNAL /* @min:%2ethrowInternal */](2 /* eLoggingSeverity.WARNING */, 108 /* _eInternalMessageId.DynamicConfigException */, message);
    }
    else {
        // We don't have a logger so just throw an exception
        throwInvalidAccess(message);
    }
}
/**
 * Create or return a dynamic version of the passed config, if it is not already dynamic
 * @param config - The config to be converted into a dynamic config
 * @param defaultConfig - The default values to apply on the config if the properties don't already exist
 * @param inPlace - Should the config be converted in-place into a dynamic config or a new instance returned, defaults to true
 * @returns The dynamic config handler for the config (whether new or existing)
 */
export function createDynamicConfig(config, defaultConfig, logger, inPlace) {
    var dynamicHandler = _createDynamicHandler(logger, config || {}, inPlace);
    if (defaultConfig) {
        dynamicHandler[_DYN_SET_DF /* @min:%2esetDf */](dynamicHandler.cfg, defaultConfig);
    }
    return dynamicHandler;
}
/**
 * Watch and track changes for accesses to the current config, the provided config MUST already be
 * a dynamic config or a child accessed via the dynamic config
 * @param config
 * @param configHandler
 * @param logger - The logger instance to use if there is no existing handler
 * @returns A watcher handler instance that can be used to remove itself when being unloaded
 * @throws TypeError if the provided config is not a dynamic config instance
 */
export function onConfigChange(config, configHandler, logger) {
    var handler = config[CFG_HANDLER_LINK] || config;
    if (handler.cfg && (handler.cfg === config || handler.cfg[CFG_HANDLER_LINK] === handler)) {
        return handler[_DYN_WATCH /* @min:%2ewatch */](configHandler);
    }
    _logInvalidAccess(logger, STR_NOT_DYNAMIC_ERROR + dumpObj(config));
    return createDynamicConfig(config, null, logger)[_DYN_WATCH /* @min:%2ewatch */](configHandler);
}
//# sourceMappingURL=DynamicConfig.js.map