var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _ExtensionProvider_coder, _ExtensionProvider_eventemitter, _ExtensionProvider_handlers, _ExtensionProvider_subscriptions, _ExtensionProvider_waitingForId, _ExtensionProvider_isConnected, _ExtensionProvider_appName, _ExtensionProvider_chainName, _ExtensionProvider_handleMessage, _ExtensionProvider_onMessageResult, _ExtensionProvider_onMessageSubscribe;
import { RpcCoder } from '@polkadot/rpc-provider/coder';
import { logger } from '@polkadot/util';
import EventEmitter from 'eventemitter3';
import { isUndefined } from '../utils/index.js';
import { provider } from '@substrate/connect-extension-protocol';
const CONTENT_SCRIPT_ORIGIN = 'content-script';
const EXTENSION_PROVIDER_ORIGIN = 'extension-provider';
const l = logger(EXTENSION_PROVIDER_ORIGIN);
const ANGLICISMS = {
    chain_finalisedHead: 'chain_finalizedHead',
    chain_subscribeFinalisedHeads: 'chain_subscribeFinalizedHeads',
    chain_unsubscribeFinalisedHeads: 'chain_unsubscribeFinalizedHeads'
};
/**
 * The ExtensionProvider allows interacting with a smoldot-based WASM light
 * client running in a browser extension.  It is not designed to be used
 * directly.  You should use the `\@substrate/connect` package.
 */
export class ExtensionProvider {
    constructor(appName, chainName) {
        _ExtensionProvider_coder.set(this, new RpcCoder());
        _ExtensionProvider_eventemitter.set(this, new EventEmitter());
        _ExtensionProvider_handlers.set(this, {});
        _ExtensionProvider_subscriptions.set(this, {});
        _ExtensionProvider_waitingForId.set(this, {});
        _ExtensionProvider_isConnected.set(this, false);
        _ExtensionProvider_appName.set(this, void 0);
        _ExtensionProvider_chainName.set(this, void 0);
        _ExtensionProvider_handleMessage.set(this, (data) => {
            if (data.disconnect && data.disconnect === true) {
                __classPrivateFieldSet(this, _ExtensionProvider_isConnected, false, "f");
                this.emit('disconnected');
                return;
            }
            const message = data.message;
            if (message.type === 'error') {
                return this.emit('error', new Error(message.payload));
            }
            if (message.type === 'rpc') {
                const rpcString = message.payload;
                l.debug(() => ['received', rpcString]);
                const response = JSON.parse(rpcString);
                return isUndefined(response.method)
                    ? __classPrivateFieldGet(this, _ExtensionProvider_onMessageResult, "f").call(this, response)
                    : __classPrivateFieldGet(this, _ExtensionProvider_onMessageSubscribe, "f").call(this, response);
            }
            const errorMessage = `Unrecognised message type from extension ${message.type}`;
            return this.emit('error', new Error(errorMessage));
        });
        _ExtensionProvider_onMessageResult.set(this, (response) => {
            const handler = __classPrivateFieldGet(this, _ExtensionProvider_handlers, "f")[response.id];
            if (!handler) {
                l.debug(() => `Unable to find handler for id=${response.id}`);
                return;
            }
            try {
                const { method, subscription } = handler;
                const result = __classPrivateFieldGet(this, _ExtensionProvider_coder, "f").decodeResponse(response);
                // first send the result - in case of subs, we may have an update
                // immediately if we have some queued results already
                handler.callback(null, result);
                if (subscription) {
                    const subId = `${subscription.type}::${result}`;
                    __classPrivateFieldGet(this, _ExtensionProvider_subscriptions, "f")[subId] = {
                        ...subscription,
                        method,
                    };
                    // if we have a result waiting for this subscription already
                    if (__classPrivateFieldGet(this, _ExtensionProvider_waitingForId, "f")[subId]) {
                        __classPrivateFieldGet(this, _ExtensionProvider_onMessageSubscribe, "f").call(this, __classPrivateFieldGet(this, _ExtensionProvider_waitingForId, "f")[subId]);
                    }
                }
            }
            catch (error) {
                handler.callback(error, undefined);
            }
            delete __classPrivateFieldGet(this, _ExtensionProvider_handlers, "f")[response.id];
        });
        _ExtensionProvider_onMessageSubscribe.set(this, (response) => {
            const method = ANGLICISMS[response.method] || response.method || 'invalid';
            const subId = `${method}::${response.params.subscription}`;
            const handler = __classPrivateFieldGet(this, _ExtensionProvider_subscriptions, "f")[subId];
            if (!handler) {
                // store the response, we could have out-of-order subid coming in
                __classPrivateFieldGet(this, _ExtensionProvider_waitingForId, "f")[subId] = response;
                l.debug(() => `Unable to find handler for subscription=${subId} responseId=${response.id}`);
                return;
            }
            // housekeeping
            delete __classPrivateFieldGet(this, _ExtensionProvider_waitingForId, "f")[subId];
            try {
                const result = __classPrivateFieldGet(this, _ExtensionProvider_coder, "f").decodeResponse(response);
                handler.callback(null, result);
            }
            catch (error) {
                handler.callback(error, undefined);
            }
        }
        /**
         * "Connect" to the extension - sends a message to the `ExtensionMessageRouter`
         * asking it to connect to the extension background.
         *
         * @returns a resolved Promise
         * @remarks this is async to fulfill the interface with PolkadotJS
         */
        );
        __classPrivateFieldSet(this, _ExtensionProvider_appName, appName, "f");
        __classPrivateFieldSet(this, _ExtensionProvider_chainName, chainName, "f");
    }
    /**
     * name
     *
     * @returns the name of this app to be used by the extension for display
     * purposes.
     *
     * @remarks Apps are expected to make efforts to make this name reasonably
     * unique.
     */
    get name() {
        return __classPrivateFieldGet(this, _ExtensionProvider_appName, "f");
    }
    /**
     * chainName
     *
     * @returns the name of the chain this `ExtensionProvider` is talking to.
     */
    get chainName() {
        return __classPrivateFieldGet(this, _ExtensionProvider_chainName, "f");
    }
    /**
     * Lets polkadot-js know we support subscriptions
     *
     * @remarks Always returns `true` - this provider supports subscriptions.
     * PolkadotJS uses this internally.
     */
    get hasSubscriptions() {
        return true;
    }
    /**
     * clone
     *
     * @remarks This method is not supported
     * @throws {@link Error}
     */
    clone() {
        throw new Error('clone() is not supported.');
    }
    /**
     * "Connect" to the extension - sends a message to the `ExtensionMessageRouter`
     * asking it to connect to the extension background.
     *
     * @returns a resolved Promise
     * @remarks this is async to fulfill the interface with PolkadotJS
     */
    connect() {
        const connectMsg = {
            appName: __classPrivateFieldGet(this, _ExtensionProvider_appName, "f"),
            chainName: __classPrivateFieldGet(this, _ExtensionProvider_chainName, "f"),
            action: 'connect',
            origin: EXTENSION_PROVIDER_ORIGIN
        };
        provider.send(connectMsg);
        provider.listen(({ data }) => {
            if (data.origin && data.origin === CONTENT_SCRIPT_ORIGIN) {
                // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
                __classPrivateFieldGet(this, _ExtensionProvider_handleMessage, "f").call(this, data);
            }
        });
        __classPrivateFieldSet(this, _ExtensionProvider_isConnected, true, "f");
        this.emit('connected');
        return Promise.resolve();
    }
    /**
     * Manually "disconnect" - sends a message to the `ExtensionMessageRouter`
     * telling it to disconnect the port with the background manager.
     */
    // eslint-disable-next-line @typescript-eslint/require-await
    async disconnect() {
        const disconnectMsg = {
            appName: __classPrivateFieldGet(this, _ExtensionProvider_appName, "f"),
            chainName: __classPrivateFieldGet(this, _ExtensionProvider_chainName, "f"),
            action: 'disconnect',
            origin: EXTENSION_PROVIDER_ORIGIN
        };
        provider.send(disconnectMsg);
        __classPrivateFieldSet(this, _ExtensionProvider_isConnected, false, "f");
        this.emit('disconnected');
    }
    /**
     * Whether the node is connected or not.
     *
     * @returns true - if connected otherwise false
     */
    get isConnected() {
        return __classPrivateFieldGet(this, _ExtensionProvider_isConnected, "f");
    }
    /**
     * Listen to provider events - in practice the smoldot provider only
     * emits a `connected` event after successfully starting the smoldot client
     * and `disconnected` after `disconnect` is called.
     * @param type - Event
     * @param sub - Callback
     */
    on(type, sub) {
        __classPrivateFieldGet(this, _ExtensionProvider_eventemitter, "f").on(type, sub);
        return () => {
            __classPrivateFieldGet(this, _ExtensionProvider_eventemitter, "f").removeListener(type, sub);
        };
    }
    /**
     * Send an RPC request  the wasm client
     *
     * @param method - The RPC methods to execute
     * @param params - Encoded paramaters as applicable for the method
     * @param subscription - Subscription details (internally used by `subscribe`)
     */
    async send(method, 
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    params, subscription
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    ) {
        return new Promise((resolve, reject) => {
            const json = __classPrivateFieldGet(this, _ExtensionProvider_coder, "f").encodeJson(method, params);
            const id = __classPrivateFieldGet(this, _ExtensionProvider_coder, "f").getId();
            const callback = (error, result) => {
                error
                    ? reject(error)
                    : resolve(result);
            };
            l.debug(() => ['calling', method, json]);
            __classPrivateFieldGet(this, _ExtensionProvider_handlers, "f")[id] = {
                callback,
                method,
                subscription
            };
            const rpcMsg = {
                appName: __classPrivateFieldGet(this, _ExtensionProvider_appName, "f"),
                chainName: __classPrivateFieldGet(this, _ExtensionProvider_chainName, "f"),
                action: 'forward',
                message: {
                    type: 'rpc',
                    payload: json,
                    subscription: !!subscription
                },
                origin: EXTENSION_PROVIDER_ORIGIN
            };
            provider.send(rpcMsg);
        });
    }
    /**
     * Allows subscribing to a specific event.
     *
     * @param type     - Subscription type
     * @param method   - Subscription method
     * @param params   - Parameters
     * @param callback - Callback
     * @returns Promise  - resolves to the id of the subscription you can use with [[unsubscribe]].
     *
     * @example
     * <BR>
     *
     * ```javascript
     * const provider = new ExtensionProvider(client);
     * const rpc = new Rpc(provider);
     *
     * rpc.state.subscribeStorage([[storage.balances.freeBalance, <Address>]], (_, values) => {
     *   console.log(values)
     * }).then((subscriptionId) => {
     *   console.log('balance changes subscription id: ', subscriptionId)
     * })
     * ```
     */
    async subscribe(
    // the "method" property of the JSON response to this subscription
    type, 
    // the "method" property of the JSON request to register the subscription
    method, 
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    params, callback) {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-return
        return await this.send(method, params, { callback, type });
    }
    /**
     * Allows unsubscribing to subscriptions made with [[subscribe]].
     *
     * @param type   - Subscription type
     * @param method - Subscription method
     * @param id     - Id passed for send parameter
     * @returns Promise resolving to whether the unsunscribe request was successful.
     */
    async unsubscribe(type, method, id) {
        const subscription = `${type}::${id}`;
        if (isUndefined(__classPrivateFieldGet(this, _ExtensionProvider_subscriptions, "f")[subscription])) {
            l.debug(() => `Unable to find active subscription=${subscription}`);
            return false;
        }
        delete __classPrivateFieldGet(this, _ExtensionProvider_subscriptions, "f")[subscription];
        return await this.send(method, [id]);
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    emit(type, ...args) {
        __classPrivateFieldGet(this, _ExtensionProvider_eventemitter, "f").emit(type, ...args);
    }
}
_ExtensionProvider_coder = new WeakMap(), _ExtensionProvider_eventemitter = new WeakMap(), _ExtensionProvider_handlers = new WeakMap(), _ExtensionProvider_subscriptions = new WeakMap(), _ExtensionProvider_waitingForId = new WeakMap(), _ExtensionProvider_isConnected = new WeakMap(), _ExtensionProvider_appName = new WeakMap(), _ExtensionProvider_chainName = new WeakMap(), _ExtensionProvider_handleMessage = new WeakMap(), _ExtensionProvider_onMessageResult = new WeakMap(), _ExtensionProvider_onMessageSubscribe = new WeakMap();
//# sourceMappingURL=ExtensionProvider.js.map