/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-console */
import { Exception } from '@api-client/core/exceptions/exception.js';
/**
 * The base IO thread class containing the communication logic with the clients.
 */
export class IoThread {
    /**
     * To be called by the IO thread implementation when receiving a message from the client.
     */
    handleMessage(event) {
        if (!event.kind) {
            console.warn('Invalid message received on the app-config-channel.', event);
            return;
        }
        if (event.kind === 'IO#Event') {
            // message sent by self.
            return;
        }
        const { args, fn, id } = event;
        if (typeof id !== 'number') {
            console.warn('Invalid message received on the app-config-channel. The id must be a number.');
            return;
        }
        if (typeof fn !== 'string' || !fn) {
            const err = new Exception('Invalid message received on the io-channel. The second argument must be a function name to call.', { status: 400 });
            this.notifyError(id, err);
            console.warn(err.message);
            return;
        }
        const callable = this.getFunction(fn);
        const isFunction = typeof callable === 'function';
        if (!isFunction) {
            const err = new Exception(`Invalid message received on the io-channel. The function "${fn}" is either not implemented or invalid.`, { status: 400 });
            this.notifyError(id, err);
            console.warn(err.message);
            return;
        }
        this.call(callable, id, args);
    }
    notifyError(id, error) {
        const event = {
            kind: 'IO#Event',
            id,
            type: 'error',
            result: error,
        };
        this.postMessage(event);
    }
    // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
    getFunction(fn) {
        const path = fn.trim().split('.');
        // eslint-disable-next-line @typescript-eslint/no-this-alias
        let current = this;
        path.forEach((key) => {
            if (!current) {
                return;
            }
            const scope = current;
            current = current[key];
            if (current && current.bind) {
                current = current.bind(scope);
            }
        });
        return current;
    }
    /**
     * Calls the function on self, creates a response event, and dispatches it.
     *
     * @param fnName The function name to call.
     * @param id The request id sent back to the client.
     * @param args The function arguments.
     */
    // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
    async call(callable, id, args) {
        const event = {
            kind: 'IO#Event',
            id,
            type: 'result',
        };
        try {
            const promise = callable(...args);
            event.result = await promise;
            this.postMessage(event);
        }
        catch (e) {
            event.type = 'error';
            event.message = e.message;
            if (e.status) {
                event.result = e;
            }
            this.postMessage(event);
        }
    }
    /**
     * Sends a message to the clients that is a request to send a client DOM event.
     *
     * @param eventPath The path in the `Events` prototype.
     * @param args The arguments to call the event with.
     */
    notifyClients(eventPath, ...args) {
        const info = {
            kind: 'IO#Notification',
            args,
            path: eventPath,
        };
        this.postMessage(info);
    }
}
//# sourceMappingURL=IoThread.js.map