var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import BaseSessionHandler from './base-session-handler';
import { SessionTypes, SdkErrorTypes } from '../types/enums';
import { createAndEmitSdkError, getBareJid, isPeerConnectionDisconnected, isScreenRecordingJid, requestApiWithRetry } from '../utils';
import { filter, fromEvent, take, takeWhile } from 'rxjs';
export class ScreenRecordingSessionHandler extends BaseSessionHandler {
    constructor() {
        super(...arguments);
        this.requestedSessions = {};
        this.sessionType = SessionTypes.screenRecording;
    }
    shouldHandleSessionByJid(jid) {
        return isScreenRecordingJid(jid);
    }
    handleConversationUpdate() {
        /* no-op */
        return;
    }
    handlePropose(pendingSession) {
        const _super = Object.create(null, {
            handlePropose: { get: () => super.handlePropose }
        });
        return __awaiter(this, void 0, void 0, function* () {
            if (this.sdk._config.autoAcceptPendingScreenRecordingRequests) {
                return this.proceedWithSession(pendingSession);
            }
            // if not auto accepting sessions, emit this event for the consumer to accept
            yield _super.handlePropose.call(this, pendingSession);
        });
    }
    acceptSession(session, params) {
        const _super = Object.create(null, {
            acceptSession: { get: () => super.acceptSession }
        });
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            const mediaStream = params.mediaStream;
            if (!mediaStream) {
                throw createAndEmitSdkError.call(this.sdk, SdkErrorTypes.session, `Cannot accept screen recording session without providing a media stream`, { conversationId: session.conversationId, sessionType: this.sessionType });
            }
            if (!((_a = params.screenRecordingMetadatas) === null || _a === void 0 ? void 0 : _a.length)) {
                throw createAndEmitSdkError.call(this.sdk, SdkErrorTypes.not_supported, 'acceptSession must be called with a `screenRecordingMetadatas` property for screen recording sessions');
            }
            session._outboundStream = mediaStream;
            let addMediaPromise = Promise.resolve();
            mediaStream.getTracks().forEach((track) => {
                addMediaPromise = addMediaPromise.then(() => {
                    this.sdk.logger.info('Adding screen track to session', { trackId: track.id, label: track.label, conversationId: session.conversationId, sessionType: this.sessionType });
                    return session.pc.addTrack(track);
                });
            });
            yield addMediaPromise;
            this.sendMetadataWhenSessionConnects(session, params.screenRecordingMetadatas);
            yield _super.acceptSession.call(this, session, params);
        });
    }
    sendMetadataWhenSessionConnects(session, metadatas) {
        // We really only want to ignore the error handling (because that should never execute), but putting an ignore closer to that line doesn't properly ignore it
        /* istanbul ignore next */
        fromEvent(session.peerConnection, 'connectionstatechange')
            .pipe(takeWhile(() => {
            return !isPeerConnectionDisconnected(session.peerConnection.connectionState);
        }), filter(() => {
            return session.peerConnection.connectionState === 'connected';
        }), take(1)).subscribe({
            next: () => __awaiter(this, void 0, void 0, function* () { return yield this.updateScreenRecordingMetadatas(session, metadatas); }),
            error: (e) => this._logSubscriptionError(e)
        });
    }
    /* istanbul ignore next */
    _logSubscriptionError(_) {
        // This is for testing thrown exceptions with an RXJS subscription
    }
    endSession(conversationId, _session, _reason) {
        return __awaiter(this, void 0, void 0, function* () {
            throw createAndEmitSdkError.call(this.sdk, SdkErrorTypes.not_supported, `sessionType ${this.sessionType} must be ended remotely`, { conversationId });
        });
    }
    updateOutgoingMedia(session, _options) {
        this.log('warn', 'Cannot update outgoing media for screen recording sessions', { sessionId: session.id, sessionType: session.sessionType });
        throw createAndEmitSdkError.call(this.sdk, SdkErrorTypes.not_supported, 'Cannot update outgoing media for screen recording sessions');
    }
    updateScreenRecordingMetadatas(session, metadatas) {
        this.log('info', 'sending screen metadatas', { conversationId: session.conversationId, sessionId: session.id, metadatas });
        metadatas.forEach((meta) => {
            // adding _trackId because I don't want to add this to the public interface
            meta._trackId = meta.trackId;
            const transceiver = session.pc.getTransceivers()
                .find((transceiver) => { var _a; return ((_a = transceiver.sender.track) === null || _a === void 0 ? void 0 : _a.id) === meta.trackId; });
            if (!transceiver) {
                this.log('warn', 'Failed to find transceiver for screen recording track', { conversationId: session.conversationId, sessionId: session.id, trackId: meta.trackId });
                return;
            }
            meta.trackId = transceiver.mid;
        });
        const data = JSON.stringify({
            participantJid: getBareJid(this.sdk),
            metaData: metadatas,
            roomId: session.originalRoomJid,
            conversationId: session.conversationId
        });
        let url = '/recordings/screensessions/metadata';
        const { accessToken, jwt } = this.sdk._config;
        if (this.sdk.isJwtAuth) {
            url += '/backgroundassistant';
        }
        return requestApiWithRetry
            .call(this.sdk, url, {
            method: 'post',
            authToken: accessToken || jwt,
            data,
        })
            .promise.then(() => {
            this.log('info', 'Screen recording metadata sent.', {
                conversationId: session.conversationId,
                sessionId: session.id,
                metadatas,
            });
        })
            .catch((e) => {
            this.log('error', 'Failed to send screen recording metadata.', {
                error: e,
                conversationId: session.conversationId,
                sessionId: session.id,
                metadatas,
            });
            throw e;
        });
    }
}
export default ScreenRecordingSessionHandler;
//# sourceMappingURL=screen-recording-session-handler.js.map