"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.RpcDirectRequest = exports.RpcDirectProtocol = exports.RpcDefaultConfiguration = exports.RpcConfiguration = exports.RpcRoutingMap = void 0;
const RpcManager_1 = require("../../RpcManager");
const RpcControl_1 = require("./RpcControl");
const RpcProtocol_1 = require("./RpcProtocol");
const RpcRegistry_1 = require("./RpcRegistry");
const RpcRequest_1 = require("./RpcRequest");
const RpcRoutingToken_1 = require("./RpcRoutingToken");
/** @internal */
var RpcRoutingMap;
(function (RpcRoutingMap) {
    function create() {
        const configurations = new Map();
        return Object.assign((routing) => configurations.get(routing.id)(), { configurations });
    }
    RpcRoutingMap.create = create;
})(RpcRoutingMap || (exports.RpcRoutingMap = RpcRoutingMap = {}));
/** A RpcConfiguration specifies how calls on an RPC interface will be marshalled, plus other operating parameters.
 * RpcConfiguration is the base class for specific configurations.
 * @beta
 */
class RpcConfiguration {
    constructor() {
        /** @internal */
        this.attached = [];
        /** @internal */
        this.allowAttachedInterfaces = true;
        /** The target interval (in milliseconds) between connection attempts for pending RPC operation requests. */
        this.pendingOperationRetryInterval = 10000;
        /** The maximum number of transient faults permitted before request failure. */
        this.transientFaultLimit = 4;
        /** @internal */
        this.routing = RpcRoutingToken_1.RpcRoutingToken.default;
        /** The control channel for the configuration.
         * @internal
         */
        this.controlChannel = RpcControl_1.RpcControlChannel.obtain(this);
    }
    /** @internal Sets the configuration supplier for an RPC interface class. */
    static assign(definition, supplier) {
        const map = definition.prototype.configurationSupplier;
        if (!map || typeof (map.configurations) === "undefined") {
            definition.prototype.configurationSupplier = supplier;
        }
        else {
            map.configurations.set(RpcRoutingToken_1.RpcRoutingToken.default.id, supplier);
        }
    }
    /** Sets the configuration supplier for an RPC interface class for a given routing. */
    static assignWithRouting(definition, routing, configuration) {
        if (!definition.prototype.configurationSupplier) {
            _a.assign(definition, RpcRoutingMap.create());
        }
        let map = definition.prototype.configurationSupplier;
        if (typeof (map.configurations) === "undefined") {
            const existing = map;
            map = RpcRoutingMap.create();
            _a.assign(definition, map);
            map.configurations.set(RpcRoutingToken_1.RpcRoutingToken.default.id, existing);
        }
        const supplier = () => configuration;
        map.configurations.set(routing.id, supplier);
    }
    /** Obtains the instance of an RPC configuration class. */
    static obtain(configurationConstructor) {
        let instance = configurationConstructor[RpcRegistry_1.INSTANCE];
        if (!instance)
            instance = configurationConstructor[RpcRegistry_1.INSTANCE] = new configurationConstructor();
        return instance;
    }
    /** @internal */
    get attachedInterfaces() { return this.attached; }
    /** @internal */
    attach(definition) {
        if (!this.allowAttachedInterfaces) {
            return;
        }
        if (this.interfaces().indexOf(definition) !== -1 || this.attached.indexOf(definition) !== -1) {
            return;
        }
        this.attached.push(definition);
        _a.assign(definition, () => this.constructor);
        RpcManager_1.RpcManager.initializeInterface(definition);
    }
    /** Initializes the RPC interfaces managed by the configuration. */
    static initializeInterfaces(configuration) {
        configuration.interfaces().forEach((definition) => RpcManager_1.RpcManager.initializeInterface(definition));
    }
    /** @internal */
    static supply(definition) {
        return _a.obtain(definition.configurationSupplier ? definition.configurationSupplier(definition.routing) : RpcDefaultConfiguration);
    }
    /** @internal */
    onRpcClientInitialized(definition, client) {
        this.protocol.onRpcClientInitialized(definition, client);
    }
    /** @internal */
    onRpcImplInitialized(definition, impl) {
        this.protocol.onRpcImplInitialized(definition, impl);
    }
    /** @internal */
    onRpcClientTerminated(definition, client) {
        this.protocol.onRpcClientTerminated(definition, client);
    }
    /** @internal */
    onRpcImplTerminated(definition, impl) {
        this.protocol.onRpcImplTerminated(definition, impl);
    }
}
exports.RpcConfiguration = RpcConfiguration;
_a = RpcConfiguration;
/** Whether development mode is enabled.
 * @note This parameter determines whether developer convenience features like backend stack traces are available.
 */
RpcConfiguration.developmentMode = false;
/** Whether frontend checks that are relevant in a cloud-hosted routing scenario are disabled. */
RpcConfiguration.disableRoutingValidation = false;
/** Whether strict mode is enabled.
 * This parameter determines system behaviors relating to strict checking:
 * - Whether an error is thrown if the type marshaling system encounters an unregistered type (only in strict mode).
 */
RpcConfiguration.strictMode = false;
/**
 * Whether to throw an error when the IModelRpcProps in the operation parameter list differs from the token in the URL.
 * @note By default, a warning is logged and the operation is allowed to proceed.
 * @note The parameter token is always replaced by the url token (unless RpcOperationPolicy.allowTokenMismatch is set).
 */
RpcConfiguration.throwOnTokenMismatch = false;
/** @internal Enables passing of application-specific context with each RPC request. */
RpcConfiguration.requestContext = {
    getId: (_request) => "",
    serialize: async (request) => ({
        id: request.id,
        applicationId: "",
        applicationVersion: "",
        sessionId: "",
        authorization: "",
    }),
};
/** A default configuration that can be used for basic testing within a library.
 * @internal
 */
class RpcDefaultConfiguration extends RpcConfiguration {
    constructor() {
        super(...arguments);
        this.interfaces = () => [];
        this.protocol = new RpcDirectProtocol(this);
    }
}
exports.RpcDefaultConfiguration = RpcDefaultConfiguration;
/** A default protocol that can be used for basic testing within a library.
 * @internal
 */
class RpcDirectProtocol extends RpcProtocol_1.RpcProtocol {
    constructor() {
        super(...arguments);
        this.requestType = RpcDirectRequest;
    }
}
exports.RpcDirectProtocol = RpcDirectProtocol;
/** A default request type that can be used for basic testing within a library.
 * @internal
 */
class RpcDirectRequest extends RpcRequest_1.RpcRequest {
    constructor() {
        super(...arguments);
        this.headers = new Map();
        this.fulfillment = undefined;
    }
    async send() {
        const request = await this.protocol.serialize(this);
        return new Promise(async (resolve, reject) => {
            try {
                this.fulfillment = await this.protocol.fulfill(request);
                resolve(this.fulfillment.status);
            }
            catch (err) {
                reject(err); // eslint-disable-line @typescript-eslint/prefer-promise-reject-errors
            }
        });
    }
    setHeader(name, value) {
        this.headers.set(name, value);
    }
    async load() {
        return this.fulfillment.result;
    }
}
exports.RpcDirectRequest = RpcDirectRequest;
//# sourceMappingURL=RpcConfiguration.js.map