"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.retrieveFilteredSystemMessages = void 0;
const protocol_1 = require("@logsn/protocol");
const protocol_2 = require("@streamr/protocol");
const rxjs_1 = require("rxjs");
const Queries_1 = require("../../Queries");
const switchPartition_1 = require("../rxjs/switchPartition");
const SystemMessageObservable_1 = require("../SystemMessageObservable");
const filterSystemMessageOperations_1 = require("./filterSystemMessageOperations");
const utils_1 = require("./utils");
/**
 * This function retrieves the filtered system messages.
 * The filtering is necessary because we only want to process the relevant messages and ignore the rest.
 */
function retrieveFilteredSystemMessages(systemMessages$, queryNodeAddress$, httpResponseRequestId$, queryInput) {
    const impossibleRequestIds$ = new rxjs_1.BehaviorSubject(new Set());
    const { newQueryRequest$, newQueryResponse$, newQueryPropagate$ } = (0, switchPartition_1.switchPartition)(systemMessages$, {
        newQueryRequest$: (0, SystemMessageObservable_1.isSystemMessageOfType)(protocol_1.SystemMessageType.QueryRequest),
        newQueryResponse$: (0, SystemMessageObservable_1.isSystemMessageOfType)(protocol_1.SystemMessageType.QueryResponse),
        newQueryPropagate$: (0, SystemMessageObservable_1.isSystemMessageOfType)(protocol_1.SystemMessageType.QueryPropagate),
    });
    const filteredQueryRequest$ = (0, filterSystemMessageOperations_1.filterQueryRequestMessages)(newQueryRequest$, queryNodeAddress$, impossibleRequestIds$, httpResponseRequestId$).pipe((0, rxjs_1.map)((c) => c.message));
    const filteredQueryResponse$ = (0, filterSystemMessageOperations_1.filterQueryResponseMessages)(newQueryResponse$, queryNodeAddress$, impossibleRequestIds$, httpResponseRequestId$).pipe((0, rxjs_1.map)((c) => c.message));
    const filteredQueryPropagate$ = (0, filterSystemMessageOperations_1.filterQueryPropagateMessages)(newQueryPropagate$, queryNodeAddress$, impossibleRequestIds$, httpResponseRequestId$).pipe((0, rxjs_1.map)((c) => c.message));
    const resultingResponsesPool$ = new rxjs_1.BehaviorSubject([]);
    const resultingPropagatesPool$ = new rxjs_1.BehaviorSubject([]);
    // side effect that fills the pools with new responses and propagates
    const fillPools$ = (0, rxjs_1.merge)(fillPoolWithStream(filteredQueryResponse$, resultingResponsesPool$), fillPoolWithStream(filteredQueryPropagate$, resultingPropagatesPool$));
    // this stream will help us filtering new impossible requestIds, once we uncover them
    // this happens only to decrease workload and memory usage
    const fillImpossibleRequestIdsWithIncompatibleInputs$ = filteredQueryRequest$.pipe((0, rxjs_1.filter)(hasIncompatibleQueryInput(queryInput)), (0, rxjs_1.tap)(({ message }) => impossibleRequestIds$.next(impossibleRequestIds$.getValue().add(message.requestId))));
    // side effect that shrinks the pools when we get a new http response, removing unnecessary responses and propagates
    // also reducing workload and memory usage
    const shrinkResultsOnRequestIdDefinition$ = (0, rxjs_1.merge)(shrinkPoolOnHttpRequestIdDefinition$(httpResponseRequestId$, resultingResponsesPool$), shrinkPoolOnHttpRequestIdDefinition$(httpResponseRequestId$, resultingPropagatesPool$));
    // if we don't start this Observable, it won't fill pools
    const startCollecting$ = (0, rxjs_1.merge)(fillImpossibleRequestIdsWithIncompatibleInputs$, fillPools$, shrinkResultsOnRequestIdDefinition$).pipe(
    // even if used multiple times, we shouldn't run these side effects more than once, so we share the stream
    (0, rxjs_1.share)({
        resetOnRefCountZero: true,
    }));
    const request$ = (0, rxjs_1.combineLatest)({
        request: filteredQueryRequest$,
        requestId: httpResponseRequestId$,
    }).pipe((0, rxjs_1.filter)(({ requestId }) => requestId !== null), (0, rxjs_1.filter)(({ request, requestId }) => request.message.requestId === requestId), (0, rxjs_1.map)(({ request }) => request), 
    // there may be only one request with this requestId, so we can take the first
    (0, rxjs_1.first)(), (0, utils_1.rethrowErrorWithSourceActionName)(`getting request`), (0, rxjs_1.shareReplay)({
        refCount: true,
        bufferSize: 1,
    }));
    return {
        request$,
        responses$: resultingResponsesPool$,
        propagates$: resultingPropagatesPool$,
        startCollectingSystemMessages$: startCollecting$,
    };
}
exports.retrieveFilteredSystemMessages = retrieveFilteredSystemMessages;
const fillPoolWithStream = (src$, pool$) => src$.pipe((0, rxjs_1.tap)((newVal) => pool$.next(pool$.getValue().concat(newVal))));
/**
 * This mutates pool$, filtering out requestIds from it that shouldn't be here,
 * releasing from memory
 * @param requestId$
 * @param pool$
 */
const shrinkPoolOnHttpRequestIdDefinition$ = (requestId$, pool$) => requestId$.pipe((0, rxjs_1.distinctUntilChanged)(), (0, rxjs_1.filter)((requestId) => requestId !== null), (0, rxjs_1.tap)((requestId) => {
    pool$.next(pool$.getValue().filter((c) => c.message.requestId === requestId));
}));
function hasIncompatibleQueryInput(queryInput) {
    return (msgValue) => {
        const requestMessage = msgValue.message;
        if (queryInput.queryType !== requestMessage.queryType) {
            return true;
        }
        const getRequestPartitionId = (request) => {
            const { partition, streamId } = request;
            return (0, protocol_2.toStreamPartID)((0, protocol_2.toStreamID)(streamId), partition);
        };
        if (queryInput.streamPartId !== getRequestPartitionId(requestMessage)) {
            return true;
        }
        switch (queryInput.queryType) {
            case Queries_1.QueryType.Last:
                return (queryInput.query.count !==
                    requestMessage.queryOptions.last);
            case Queries_1.QueryType.From:
                return (queryInput.query.fromTimestamp !==
                    requestMessage.queryOptions.from.timestamp ||
                    queryInput.query.fromSequenceNumber !==
                        requestMessage.queryOptions.from
                            .sequenceNumber ||
                    queryInput.query.publisherId !==
                        requestMessage.queryOptions.publisherId);
            case Queries_1.QueryType.Range:
                return (queryInput.query.fromTimestamp !==
                    requestMessage.queryOptions.from.timestamp ||
                    queryInput.query.toTimestamp !==
                        requestMessage.queryOptions.to.timestamp ||
                    queryInput.query.fromSequenceNumber !==
                        requestMessage.queryOptions.from
                            .sequenceNumber ||
                    queryInput.query.toSequenceNumber !==
                        requestMessage.queryOptions.to
                            .sequenceNumber ||
                    queryInput.query.publisherId !==
                        requestMessage.queryOptions.publisherId ||
                    queryInput.query.msgChainId !==
                        requestMessage.queryOptions.msgChainId);
        }
    };
}
//# sourceMappingURL=retrieveFilteredSystemMessages.js.map