"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createMessagePipeline = void 0;
/**
 * Subscription message processing pipeline
 */
const protocol_1 = require("@streamr/protocol");
const decrypt_1 = require("../encryption/decrypt");
const PushPipeline_1 = require("../utils/PushPipeline");
const validateStreamMessage_1 = require("../utils/validateStreamMessage");
const MsgChainUtil_1 = require("./MsgChainUtil");
const OrderMessages_1 = require("./ordering/OrderMessages");
const createMessagePipeline = (opts) => {
    const logger = opts.loggerFactory.createLogger(module);
    /* eslint-enable object-curly-newline */
    const onError = (error, streamMessage) => {
        if (streamMessage) {
            ignoreMessages.add(streamMessage);
        }
        if (error && 'streamMessage' in error && error.streamMessage) {
            ignoreMessages.add(error.streamMessage);
        }
        throw error;
    };
    const messageStream = new PushPipeline_1.PushPipeline;
    const msgChainUtil = new MsgChainUtil_1.MsgChainUtil(async (msg) => {
        await (0, validateStreamMessage_1.validateStreamMessage)(msg, opts.streamRegistry);
        let decrypted;
        if (protocol_1.StreamMessage.isAESEncrypted(msg)) {
            try {
                decrypted = await (0, decrypt_1.decrypt)(msg, opts.groupKeyManager, opts.destroySignal);
            }
            catch (err) {
                // TODO log this in onError? if we want to log all errors?
                logger.debug('Failed to decrypt', { messageId: msg.messageId, err });
                // clear cached permissions if cannot decrypt, likely permissions need updating
                opts.streamRegistry.clearStreamCache(msg.getStreamId());
                throw err;
            }
        }
        else {
            decrypted = msg;
        }
        decrypted.getParsedContent(); // throws if content is not parsable (e.g. not valid JSON)
        return decrypted;
    }, messageStream.onError);
    // collect messages that fail validation/parsing, do not push out of pipeline
    // NOTE: we let failed messages be processed and only removed at end so they don't
    // end up acting as gaps that we repeatedly try to fill.
    const ignoreMessages = new WeakSet();
    messageStream.onError.listen(onError);
    if (opts.config.orderMessages) {
        // order messages and fill gaps
        const orderMessages = new OrderMessages_1.OrderMessages(opts.streamPartId, opts.getStorageNodes, () => { }, // TODO send some error to messageStream (NET-987)
        opts.resends, opts.config);
        messageStream.pipe(async function* (src) {
            setImmediate(() => {
                orderMessages.addMessages(src);
            });
            yield* orderMessages;
        });
        messageStream.onBeforeFinally.listen(() => {
            orderMessages.destroy();
        });
    }
    messageStream
        .pipe(async function* (src) {
        setImmediate(async () => {
            let err = undefined;
            try {
                for await (const msg of src) {
                    msgChainUtil.addMessage(msg);
                }
            }
            catch (e) {
                err = e;
            }
            await msgChainUtil.flush();
            msgChainUtil.stop(err);
        });
        yield* msgChainUtil;
    })
        // ignore any failed messages
        .filter((streamMessage) => {
        return !ignoreMessages.has(streamMessage);
    });
    return messageStream;
};
exports.createMessagePipeline = createMessagePipeline;
//# sourceMappingURL=messagePipeline.js.map