"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createBroadcastChannel = createBroadcastChannel;
exports.createDistributedMethod = createDistributedMethod;
function __assignType(fn, args) {
    fn.__type = args;
    return fn;
}
/*@ts-ignore*/
var { __ΩLoggerInterface } = require('@deepkit/logger');
const logger_1 = require("@deepkit/logger");
/*@ts-ignore*/
var { __ΩReceiveType } = require('@deepkit/type');
const lodash_1 = require("lodash");
const os_1 = require("os");
const resolver_1 = require("../../app/resolver");
const redis_1 = require("./redis");
const getSharedBroadcastChannel = (0, lodash_1.memoize)(() => {
    const logger = (0, resolver_1.r)(logger_1.Logger).scoped('Broadcast');
    const { prefix, client: publishClient } = (0, redis_1.createRedis)('BROADCAST');
    const { client: subscribeClient } = (0, redis_1.createRedis)('BROADCAST');
    const channel = `${prefix}:broadcast`;
    const localInstanceKey = `${(0, os_1.hostname)()}/${process.pid}`;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const listeners = (Map.Ω = [['&'], ['message', '', 'P"2!$/"D']], new Map());
    subscribeClient.subscribe(channel);
    subscribeClient.on('message', __assignType((_, message) => {
        try {
            const { instanceKey, eventName, data } = JSON.parse(message);
            if (instanceKey === localInstanceKey)
                return;
            const listenersForEvent = listeners.get(eventName);
            if (!listenersForEvent)
                return;
            for (const listener of listenersForEvent) {
                try {
                    listener(data);
                }
                catch (err) {
                    logger.error(`Failed to handle broadcast message`, err, { eventName });
                }
            }
        }
        catch (err) {
            logger.error('Failed to parse broadcast message', err, message);
        }
    }, ['_', 'message', '', 'P"2!"2""/#']));
    return {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        subscribe: __assignType((eventName, fn) => {
            const listenersForEvent = listeners.get(eventName) ?? new Set();
            listenersForEvent.add(fn);
            listeners.set(eventName, listenersForEvent);
        }, ['eventName', 'data', '', 'fn', 'P&2!P"2"$/#2$"/#']),
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        publish: __assignType((eventName, data) => {
            publishClient.publish(channel, JSON.stringify({ instanceKey: localInstanceKey, eventName, data }));
        }, ['eventName', 'data', '', 'P&2!"2""/#'])
    };
});
function createBroadcastChannel(eventName, _type = createBroadcastChannel.Ω?.[0]) {
    createBroadcastChannel.Ω = undefined;
    const channel = getSharedBroadcastChannel();
    return {
        subscribe: __assignType((fn) => {
            channel.subscribe(eventName, __assignType(data => {
                // todo: figure out type validation
                // assert<T>(data, undefined, type);
                fn(data);
            }, ['data', '', 'P"2!"/"']));
        }, [_type, 'data', '', 'fn', 'PPn!2"$/#2$"/#']),
        publish: __assignType((data) => {
            channel.publish(eventName, data);
        }, [_type, 'data', '', 'Pn!2""/#'])
    };
}
createBroadcastChannel.__type = ['eventName', () => __ΩReceiveType, '_type', 'createBroadcastChannel', 'P&2!"o""2#8"/$'];
const __ΩIDistributedMethodOptions = ['name', () => __ΩLoggerInterface, '', 'logger', 'IDistributedMethodOptions', 'P&4!Pn"/#4$8Mw%y'];
function createDistributedMethod(options, fn, type = createDistributedMethod.Ω?.[0]) {
    createDistributedMethod.Ω = undefined;
    const getLogger = options.logger ?? (() => (0, resolver_1.r)(logger_1.Logger).scoped(`Distributed:${options.name}`));
    const channel = createBroadcastChannel(options.name, type);
    const wrappedFn = __assignType(async (data) => {
        try {
            await fn(data);
        }
        catch (err) {
            getLogger().error(`Error executing ${options.name} distributed method`, err);
        }
    }, [type, 'data', '', 'Pn!2""/#']);
    // invoke locally when remotely requested
    channel.subscribe(wrappedFn);
    // publish & invoke locally when locally executed
    return __assignType((data) => {
        channel.publish(data);
        return wrappedFn(data);
    }, [type, 'data', '', 'Pn!2""/#']);
}
createDistributedMethod.__type = [() => __ΩIDistributedMethodOptions, 'options', 'data', '', 'fn', () => __ΩReceiveType, 'type', 'createDistributedMethod', 'Pn!2"P"2#$`/$2%"o&"2\'8"/('];
//# sourceMappingURL=broadcast.js.map