/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module RpcInterface
 */
import { expectDefined } from "@itwin/core-bentley";
import { RpcManager } from "../../RpcManager";
import { RpcControlChannel } from "./RpcControl";
import { RpcProtocol } from "./RpcProtocol";
import { INSTANCE } from "./RpcRegistry";
import { RpcRequest } from "./RpcRequest";
import { RpcRoutingToken } from "./RpcRoutingToken";
/** @internal */
export var RpcRoutingMap;
(function (RpcRoutingMap) {
    function create() {
        const configurations = new Map();
        return Object.assign((routing) => configurations.get(expectDefined(routing).id)(), { configurations });
    }
    RpcRoutingMap.create = create;
})(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
 */
export class RpcConfiguration {
    /** Whether development mode is enabled.
     * @note This parameter determines whether developer convenience features like backend stack traces are available.
     */
    static developmentMode = false;
    /** Whether frontend checks that are relevant in a cloud-hosted routing scenario are disabled. */
    static 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).
     */
    static 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).
     */
    static throwOnTokenMismatch = false;
    /** @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.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) {
            RpcConfiguration.assign(definition, RpcRoutingMap.create());
        }
        let map = definition.prototype.configurationSupplier;
        if (typeof (map.configurations) === "undefined") {
            const existing = map;
            map = RpcRoutingMap.create();
            RpcConfiguration.assign(definition, map);
            map.configurations.set(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[INSTANCE];
        if (!instance)
            instance = configurationConstructor[INSTANCE] = new configurationConstructor();
        return instance;
    }
    /** @internal Enables passing of application-specific context with each RPC request. */
    static requestContext = {
        getId: (_request) => "",
        serialize: async (request) => ({
            id: request.id,
            applicationId: "",
            applicationVersion: "",
            sessionId: "",
            authorization: "",
        }),
    };
    /** @internal */
    attached = [];
    /** @internal */
    allowAttachedInterfaces = true;
    /** @internal */
    get attachedInterfaces() { return this.attached; }
    /** The target interval (in milliseconds) between connection attempts for pending RPC operation requests. */
    pendingOperationRetryInterval = 10000;
    /** The maximum number of transient faults permitted before request failure. */
    transientFaultLimit = 4;
    /** @internal */
    routing = RpcRoutingToken.default;
    /** The control channel for the configuration.
     * @internal
     */
    controlChannel = RpcControlChannel.obtain(this);
    /** @internal */
    attach(definition) {
        if (!this.allowAttachedInterfaces) {
            return;
        }
        if (this.interfaces().indexOf(definition) !== -1 || this.attached.indexOf(definition) !== -1) {
            return;
        }
        this.attached.push(definition);
        RpcConfiguration.assign(definition, () => this.constructor);
        RpcManager.initializeInterface(definition);
    }
    /** Initializes the RPC interfaces managed by the configuration. */
    static initializeInterfaces(configuration) {
        configuration.interfaces().forEach((definition) => RpcManager.initializeInterface(definition));
    }
    /** @internal */
    static supply(definition) {
        return RpcConfiguration.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);
    }
}
/** A default configuration that can be used for basic testing within a library.
 * @internal
 */
export class RpcDefaultConfiguration extends RpcConfiguration {
    interfaces = () => [];
    protocol = new RpcDirectProtocol(this);
}
/** A default protocol that can be used for basic testing within a library.
 * @internal
 */
export class RpcDirectProtocol extends RpcProtocol {
    requestType = RpcDirectRequest;
}
/** A default request type that can be used for basic testing within a library.
 * @internal
 */
export class RpcDirectRequest extends RpcRequest {
    headers = new Map();
    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 expectDefined(this.fulfillment).result;
    }
}
//# sourceMappingURL=RpcConfiguration.js.map