"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SseTransport = void 0;
const eventsource_1 = __importDefault(require("eventsource"));
const cache_1 = require("../cache");
const util_1 = require("../util");
const streaming_1 = require("./abstract/streaming");
const logger = (0, util_1.makeLogger)('SSETransport');
/**
 * Transport implementation that establishes a long lived connection to a server using the SSE protocol and subcribes to updates.
 *
 * @typeParam T - Helper struct type that will be used to pass types to the generic parameters (check [[SSETransportGenerics]])
 */
class SseTransport extends streaming_1.StreamingTransport {
    constructor(config) {
        super();
        this.config = config;
        this.EventSource = eventsource_1.default;
        this.timeOfLastReq = 0;
    }
    getSubscriptionTtlFromConfig(adapterSettings) {
        return adapterSettings.SSE_SUBSCRIPTION_TTL;
    }
    async initialize(dependencies, adapterSettings, endpointName, transportName) {
        super.initialize(dependencies, adapterSettings, endpointName, transportName);
        this.requester = dependencies.requester;
        if (dependencies.eventSource) {
            this.EventSource = dependencies.eventSource;
        }
    }
    async streamHandler(context, subscriptions) {
        if ((subscriptions.new.length || subscriptions.stale.length) &&
            (!this.sseConnection || this.sseConnection.readyState !== this.sseConnection.OPEN)) {
            logger.debug('No established connection and new subscriptions available, connecting to SSE');
            const sseConfig = this.config.prepareSSEConnectionConfig(subscriptions.new, context);
            this.providerDataStreamEstablished = Date.now();
            this.sseConnection = new this.EventSource(sseConfig.url, sseConfig.eventSourceInitDict);
            const eventHandlerGenerator = (listener) => {
                return (e) => {
                    const providerDataReceived = Date.now();
                    const results = listener.parseResponse(e).map((r) => {
                        const partialResponse = r.response;
                        const result = r;
                        result.response.timestamps = {
                            providerDataStreamEstablishedUnixMs: this.providerDataStreamEstablished,
                            providerDataReceivedUnixMs: providerDataReceived,
                            providerIndicatedTimeUnixMs: partialResponse.timestamps?.providerIndicatedTimeUnixMs,
                        };
                        return result;
                    });
                    this.responseCache.write(this.name, results);
                };
            };
            this.config.eventListeners.forEach((listener) => {
                this.sseConnection?.addEventListener(listener.type, eventHandlerGenerator(listener));
            });
        }
        const makeRequest = async (key, req) => {
            await this.requester.request(key, req);
            this.timeOfLastReq = Date.now();
        };
        if (subscriptions.new.length) {
            const subscribeRequest = this.config.prepareSubscriptionRequest(subscriptions.new, context);
            makeRequest((0, cache_1.calculateHttpRequestKey)({
                context,
                transportName: this.name,
                data: subscriptions.new,
            }), subscribeRequest);
        }
        if (subscriptions.stale.length) {
            const unsubscribeRequest = this.config.prepareUnsubscriptionRequest(subscriptions.stale, context);
            makeRequest((0, cache_1.calculateHttpRequestKey)({
                context,
                transportName: this.name,
                data: subscriptions.stale,
            }), unsubscribeRequest);
        }
        if (this.config.prepareKeepAliveRequest &&
            subscriptions.desired.length &&
            Date.now() - this.timeOfLastReq > context.adapterSettings.SSE_KEEPALIVE_SLEEP) {
            const prepareKeepAliveRequest = this.config.prepareKeepAliveRequest(context);
            makeRequest((0, cache_1.calculateHttpRequestKey)({
                context,
                transportName: this.name,
                data: subscriptions.desired,
            }), prepareKeepAliveRequest);
        }
        // The background execute loop no longer sleeps between executions, so we have to do it here
        logger.trace(`SSE handler complete, sleeping for ${context.adapterSettings.BACKGROUND_EXECUTE_MS_SSE}ms...`);
        await (0, util_1.sleep)(context.adapterSettings.BACKGROUND_EXECUTE_MS_SSE);
        return;
    }
}
exports.SseTransport = SseTransport;
//# sourceMappingURL=sse.js.map