/*---------------------------------------------------------------------------------------------
* 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 { Base64 } from "js-base64";
import { RpcInterface } from "../../RpcInterface";
import { RpcManager } from "../../RpcManager";
import { RpcConfiguration } from "./RpcConfiguration";
import { RpcOperation } from "./RpcOperation";
import { RpcRegistry } from "./RpcRegistry";
/** An RPC operation control response.
 * @public
 */
export class RpcControlResponse {
    message = "RpcControlResponse";
}
/** A pending RPC operation response.
 * @public
 */
export class RpcPendingResponse extends RpcControlResponse {
    /** Extended status regarding the pending operation. */
    message;
    /** Constructs a pending response. */
    constructor(message = "") {
        super();
        this.message = message;
    }
}
/** A RPC operation response.
 * @public
 */
export class RpcNotFoundResponse extends RpcControlResponse {
    message = "Not found";
}
/** Manages requests and responses for an RPC configuration.
 * @internal
 */
export class RpcControlChannel {
    /** @internal */
    static channels = [];
    static _obtainLock = 0;
    _configuration;
    _initialized = false;
    _clientActive = false;
    _describeEndpoints = undefined;
    /** @internal */
    static ensureInitialized() {
        this.channels.forEach((channel) => channel.initialize());
    }
    constructor(configuration) {
        this._configuration = configuration;
        RpcControlChannel.channels.push(this);
    }
    /** @internal */
    async describeEndpoints() {
        this.activateClient();
        if (!this._channelInterface.interfaceName) {
            return [];
        }
        return this._describeEndpoints();
    }
    /** @internal */
    static obtain(configuration) {
        if (RpcControlChannel._obtainLock)
            return undefined;
        ++RpcControlChannel._obtainLock;
        const channel = new RpcControlChannel(configuration);
        --RpcControlChannel._obtainLock;
        return channel;
    }
    _channelInterface = class extends RpcInterface {
        static interfaceVersion = "CONTROL";
        static interfaceName = "";
        async describeEndpoints() { return this.forward(arguments); }
    };
    _channelImpl = class extends RpcInterface {
        async describeEndpoints() {
            const endpoints = [];
            this.configuration.interfaces().forEach((definition) => {
                if (!RpcRegistry.instance.isRpcInterfaceInitialized(definition))
                    return;
                const description = { interfaceName: definition.interfaceName, interfaceVersion: definition.interfaceVersion, operationNames: [], compatible: true };
                RpcOperation.forEach(definition, (operation) => description.operationNames.push(operation.operationName));
                endpoints.push(description);
            });
            return endpoints;
        }
    };
    computeId() {
        const interfaces = [];
        this._configuration.interfaces().forEach((definition) => interfaces.push(`${definition.interfaceName}@${definition.interfaceVersion}`));
        const id = interfaces.sort().join(",");
        return Base64.encode(id);
    }
    activateClient() {
        if (this._clientActive)
            return;
        this.initialize();
        const token = { key: "none", iTwinId: "none", iModelId: "none", changeset: { id: "none" } };
        RpcOperation.forEach(this._channelInterface, (operation) => operation.policy.token = (_request) => RpcOperation.fallbackToken ?? token);
        const client = RpcManager.getClientForInterface(this._channelInterface);
        this._describeEndpoints = async () => client.describeEndpoints();
        this._clientActive = true;
    }
    /** @internal */
    initialize() {
        if (this._initialized) {
            return;
        }
        const id = this.computeId();
        Object.defineProperty(this._channelInterface, "interfaceName", { value: id });
        Object.defineProperty(this._channelImpl, "interfaceName", { value: id });
        RpcConfiguration.assign(this._channelInterface, () => this._configuration.constructor);
        RpcManager.registerImpl(this._channelInterface, this._channelImpl);
        RpcManager.initializeInterface(this._channelInterface);
        this._initialized = true;
    }
    /** @internal */
    handleUnknownOperation(invocation, _error) {
        this.initialize();
        const op = invocation.request.operation;
        if (op.interfaceVersion === "CONTROL" && op.operationName === "describeEndpoints") {
            if (this._channelInterface.interfaceName) {
                op.interfaceDefinition = this._channelInterface.interfaceName;
            }
            return true;
        }
        return false;
    }
}
//# sourceMappingURL=RpcControl.js.map