import { __decorateMetadata } from "../../../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorateMetadata.mjs";
import { __decorate } from "../../../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.mjs";
import { DidCommOutboundMessageContext } from "../../models/DidCommOutboundMessageContext.mjs";
import "../../models/index.mjs";
import { Subject } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/Subject.mjs";
import { ReplaySubject } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/ReplaySubject.mjs";
import { firstValueFrom } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/firstValueFrom.mjs";
import { timeout } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/operators/timeout.mjs";
import { timer } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/observable/timer.mjs";
import { interval } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/observable/interval.mjs";
import { merge } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/observable/merge.mjs";
import { filter } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/operators/filter.mjs";
import { delayWhen } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/operators/delayWhen.mjs";
import { first } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/operators/first.mjs";
import { takeUntil } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/operators/takeUntil.mjs";
import { tap } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/operators/tap.mjs";
import { throttleTime } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/operators/throttleTime.mjs";
import { DidCommModuleConfig } from "../../DidCommModuleConfig.mjs";
import { DidCommTransportEventTypes } from "../../transport/DidCommTransportEventTypes.mjs";
import "../../transport/index.mjs";
import { __decorateParam } from "../../../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorateParam.mjs";
import { DidCommConnectionService } from "../connections/services/DidCommConnectionService.mjs";
import { DidCommMessageSender } from "../../DidCommMessageSender.mjs";
import { DidCommConnectionMetadataKeys } from "../connections/repository/DidCommConnectionMetadataTypes.mjs";
import { DidCommRoutingEventTypes } from "./DidCommRoutingEvents.mjs";
import { DidCommKeylistUpdate, DidCommKeylistUpdateAction, DidCommKeylistUpdateMessage } from "./messages/DidCommKeylistUpdateMessage.mjs";
import "./messages/index.mjs";
import { DidCommMediationState } from "./models/DidCommMediationState.mjs";
import { DidCommMediatorPickupStrategy } from "./DidCommMediatorPickupStrategy.mjs";
import { DidCommMediationRepository } from "./repository/DidCommMediationRepository.mjs";
import { DidCommMediationRecipientService } from "./services/DidCommMediationRecipientService.mjs";
import "../connections/services/index.mjs";
import { DidCommRoutingService } from "./services/DidCommRoutingService.mjs";
import { DidCommMessagePickupApi } from "../message-pickup/DidCommMessagePickupApi.mjs";
import { DidCommBatchPickupMessage } from "../message-pickup/protocol/v1/messages/DidCommBatchPickupMessage.mjs";
import "../message-pickup/protocol/v1/index.mjs";
import { DidCommStatusV2Message } from "../message-pickup/protocol/v2/messages/DidCommStatusV2Message.mjs";
import "../message-pickup/protocol/v2/index.mjs";
import "./repository/index.mjs";
import { DidCommDiscoverFeaturesApi } from "../discover-features/DidCommDiscoverFeaturesApi.mjs";
import "../discover-features/index.mjs";
import { DidCommMediationRecipientModuleConfig } from "./DidCommMediationRecipientModuleConfig.mjs";
import { AgentContext, CredoError, DidDocument, DidsApi, EventEmitter, InjectionSymbols, filterContextCorrelationId, inject, injectable, verkeyToDidKey } from "@credo-ts/core";

//#region ../didcomm/src/modules/routing/DidCommMediationRecipientApi.ts
var _ref, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9, _ref10, _ref11, _ref12;
let DidCommMediationRecipientApi = class DidCommMediationRecipientApi$1 {
	constructor(mediationRecipientService, connectionService, dids, messageSender, eventEmitter, discoverFeaturesApi, messagePickupApi, mediationRepository, routingService, logger, agentContext, stop$, mediationRecipientModuleConfig) {
		this.stopMessagePickup$ = new Subject();
		this.connectionService = connectionService;
		this.dids = dids;
		this.mediationRecipientService = mediationRecipientService;
		this.messageSender = messageSender;
		this.eventEmitter = eventEmitter;
		this.logger = logger;
		this.discoverFeaturesApi = discoverFeaturesApi;
		this.messagePickupApi = messagePickupApi;
		this.mediationRepository = mediationRepository;
		this.routingService = routingService;
		this.agentContext = agentContext;
		this.stop$ = stop$;
		this.config = mediationRecipientModuleConfig;
	}
	async sendMessage(outboundMessageContext, pickupStrategy) {
		const transportPriority = (pickupStrategy ?? this.config.mediatorPickupStrategy) === DidCommMediatorPickupStrategy.Implicit ? {
			schemes: ["wss", "ws"],
			restrictive: true
		} : void 0;
		await this.messageSender.sendMessage(outboundMessageContext, { transportPriority });
	}
	/**
	* Implicit mode consists simply on initiating a long-lived session to a mediator and wait for the
	* messages to arrive automatically.
	*
	* In order to do initiate this session, we open a suitable connection (using WebSocket transport) and
	* send a Trust Ping message.
	*
	* @param mediator
	*/
	async initiateImplicitMode(mediator) {
		const connection = await this.connectionService.getById(this.agentContext, mediator.connectionId);
		const { message, connectionRecord } = await this.connectionService.createTrustPing(this.agentContext, connection, { responseRequested: false });
		const websocketSchemes = ["ws", "wss"];
		if (!((connectionRecord.theirDid && await this.dids.resolveDidDocument(connectionRecord.theirDid))?.didCommServices || [])?.some((s) => websocketSchemes.includes(s.protocolScheme))) throw new CredoError("Cannot open websocket to connection without websocket service endpoint");
		await this.messageSender.sendMessage(new DidCommOutboundMessageContext(message, {
			agentContext: this.agentContext,
			connection: connectionRecord
		}), { transportPriority: {
			schemes: websocketSchemes,
			restrictive: true
		} });
	}
	/**
	* Keep track of a persistent transport session with a mediator, trying to reconnect to it as
	* soon as it is disconnected, using a recursive back-off strategy
	*
	* @param mediator mediation record
	* @param pickupStrategy chosen pick up strategy (should be Implicit or PickUp in Live Mode)
	*/
	async monitorMediatorWebSocketEvents(mediator, pickupStrategy) {
		const { baseMediatorReconnectionIntervalMs, maximumMediatorReconnectionIntervalMs } = this.config;
		let interval$1 = baseMediatorReconnectionIntervalMs;
		const stopConditions$ = merge(this.stop$, this.stopMessagePickup$).pipe();
		this.eventEmitter.observable(DidCommTransportEventTypes.DidCommOutboundWebSocketOpenedEvent).pipe(takeUntil(stopConditions$), filter((e) => e.payload.connectionId === mediator.connectionId)).subscribe(() => {
			interval$1 = baseMediatorReconnectionIntervalMs;
		});
		this.eventEmitter.observable(DidCommTransportEventTypes.DidCommOutboundWebSocketClosedEvent).pipe(takeUntil(stopConditions$), filter((e) => e.payload.connectionId === mediator.connectionId), throttleTime(interval$1), delayWhen(() => timer(interval$1)), tap(() => {
			interval$1 = Math.min(interval$1 * 2, maximumMediatorReconnectionIntervalMs);
		})).subscribe({
			next: async () => {
				this.logger.debug(`Websocket connection to mediator with connectionId '${mediator.connectionId}' is closed, attempting to reconnect...`);
				try {
					if (pickupStrategy === DidCommMediatorPickupStrategy.PickUpV2LiveMode) {
						await this.messagePickupApi.pickupMessages({
							connectionId: mediator.connectionId,
							protocolVersion: "v2",
							awaitCompletion: true
						});
						await this.messagePickupApi.setLiveDeliveryMode({
							connectionId: mediator.connectionId,
							liveDelivery: true,
							protocolVersion: "v2"
						});
					} else await this.initiateImplicitMode(mediator);
				} catch (error) {
					this.logger.warn("Unable to re-open websocket connection to mediator", { error });
				}
			},
			complete: () => this.logger.info(`Stopping pickup of messages from mediator '${mediator.id}'`)
		});
	}
	/**
	* Start a Message Pickup flow with a registered Mediator.
	*
	* @param mediator optional {MediationRecord} corresponding to the mediator to pick messages from. It will use
	* default mediator otherwise
	* @param pickupStrategy optional {DidCommMediatorPickupStrategy} to use in the loop. It will use Agent's default
	* strategy or attempt to find it by Discover Features otherwise
	* @returns
	*/
	async initiateMessagePickup(mediator, pickupStrategy) {
		const { mediatorPollingInterval } = this.config;
		const mediatorRecord = mediator ?? await this.findDefaultMediator();
		if (!mediatorRecord) throw new CredoError("There is no mediator to pickup messages from");
		const mediatorPickupStrategy = pickupStrategy ?? await this.getPickupStrategyForMediator(mediatorRecord);
		const mediatorConnection = await this.connectionService.getById(this.agentContext, mediatorRecord.connectionId);
		switch (mediatorPickupStrategy) {
			case DidCommMediatorPickupStrategy.PickUpV1:
			case DidCommMediatorPickupStrategy.PickUpV2: {
				const stopConditions$ = merge(this.stop$, this.stopMessagePickup$).pipe();
				const protocolVersion = mediatorPickupStrategy === DidCommMediatorPickupStrategy.PickUpV2 ? "v2" : "v1";
				this.logger.info(`Starting explicit pickup of messages from mediator '${mediatorRecord.id}' using ${protocolVersion}`);
				return interval(mediatorPollingInterval).pipe(takeUntil(stopConditions$)).subscribe({
					next: async () => {
						await this.messagePickupApi.pickupMessages({
							connectionId: mediatorConnection.id,
							batchSize: this.config.maximumMessagePickup,
							protocolVersion
						});
					},
					complete: () => this.logger.info(`Stopping pickup of messages from mediator '${mediatorRecord.id}'`)
				});
			}
			case DidCommMediatorPickupStrategy.PickUpV2LiveMode:
				this.logger.info(`Starting Live Mode pickup of messages from mediator '${mediatorRecord.id}'`);
				await this.monitorMediatorWebSocketEvents(mediatorRecord, mediatorPickupStrategy);
				await this.messagePickupApi.pickupMessages({
					connectionId: mediatorConnection.id,
					protocolVersion: "v2",
					awaitCompletion: true
				});
				await this.messagePickupApi.setLiveDeliveryMode({
					connectionId: mediatorConnection.id,
					liveDelivery: true,
					protocolVersion: "v2"
				});
				break;
			case DidCommMediatorPickupStrategy.Implicit:
				this.logger.info(`Starting implicit pickup of messages from mediator '${mediatorRecord.id}'`);
				await this.monitorMediatorWebSocketEvents(mediatorRecord, mediatorPickupStrategy);
				await this.initiateImplicitMode(mediatorRecord);
				break;
			default: this.logger.info(`Skipping pickup of messages from mediator '${mediatorRecord.id}' due to pickup strategy none`);
		}
	}
	/**
	* Terminate all ongoing Message Pickup loops
	*/
	async stopMessagePickup() {
		this.stopMessagePickup$.next(true);
	}
	async getPickupStrategyForMediator(mediator) {
		let mediatorPickupStrategy = mediator.pickupStrategy ?? this.config.mediatorPickupStrategy;
		if (!mediatorPickupStrategy) {
			if ((await this.discoverFeaturesApi.queryFeatures({
				connectionId: mediator.connectionId,
				protocolVersion: "v1",
				queries: [{
					featureType: "protocol",
					match: DidCommStatusV2Message.type.protocolUri
				}],
				awaitDisclosures: true
			})).features?.find((item) => item.id === DidCommStatusV2Message.type.protocolUri)) mediatorPickupStrategy = DidCommMediatorPickupStrategy.PickUpV2;
			else mediatorPickupStrategy = (await this.discoverFeaturesApi.queryFeatures({
				connectionId: mediator.connectionId,
				protocolVersion: "v1",
				queries: [{
					featureType: "protocol",
					match: DidCommBatchPickupMessage.type.protocolUri
				}],
				awaitDisclosures: true
			})).features?.find((item) => item.id === DidCommBatchPickupMessage.type.protocolUri) ? DidCommMediatorPickupStrategy.PickUpV1 : DidCommMediatorPickupStrategy.Implicit;
			mediator.pickupStrategy = mediatorPickupStrategy;
			await this.mediationRepository.update(this.agentContext, mediator);
		}
		return mediatorPickupStrategy;
	}
	async discoverMediation() {
		return this.mediationRecipientService.discoverMediation(this.agentContext);
	}
	async setDefaultMediator(mediatorRecord) {
		return this.mediationRecipientService.setDefaultMediator(this.agentContext, mediatorRecord);
	}
	async requestMediation(connection) {
		const { mediationRecord, message } = await this.mediationRecipientService.createRequest(this.agentContext, connection);
		const outboundMessage = new DidCommOutboundMessageContext(message, {
			agentContext: this.agentContext,
			connection
		});
		await this.sendMessage(outboundMessage);
		return mediationRecord;
	}
	async notifyKeylistUpdate(connection, verkey, action) {
		let useDidKey = this.agentContext.dependencyManager.resolve(DidCommModuleConfig).useDidKeyInProtocols;
		const useDidKeysConnectionMetadata = connection.metadata.get(DidCommConnectionMetadataKeys.UseDidKeysForProtocol);
		if (useDidKeysConnectionMetadata) useDidKey = useDidKeysConnectionMetadata[DidCommKeylistUpdateMessage.type.protocolUri] ?? useDidKey;
		const outboundMessageContext = new DidCommOutboundMessageContext(this.mediationRecipientService.createKeylistUpdateMessage([new DidCommKeylistUpdate({
			action: action ?? DidCommKeylistUpdateAction.add,
			recipientKey: useDidKey ? verkeyToDidKey(verkey) : verkey
		})]), {
			agentContext: this.agentContext,
			connection
		});
		await this.sendMessage(outboundMessageContext);
	}
	async findByConnectionId(connectionId) {
		return await this.mediationRecipientService.findByConnectionId(this.agentContext, connectionId);
	}
	async getMediators() {
		return await this.mediationRecipientService.getMediators(this.agentContext);
	}
	async findDefaultMediator() {
		return this.mediationRecipientService.findDefaultMediator(this.agentContext);
	}
	async findDefaultMediatorConnection() {
		const mediatorRecord = await this.findDefaultMediator();
		if (mediatorRecord) return this.connectionService.getById(this.agentContext, mediatorRecord.connectionId);
		return null;
	}
	async requestAndAwaitGrant(connection, timeoutMs = 1e4) {
		const { mediationRecord, message } = await this.mediationRecipientService.createRequest(this.agentContext, connection);
		const observable = this.eventEmitter.observable(DidCommRoutingEventTypes.MediationStateChanged);
		const subject = new ReplaySubject(1);
		observable.pipe(filterContextCorrelationId(this.agentContext.contextCorrelationId), filter((event) => event.payload.mediationRecord.id === mediationRecord.id), filter((event) => event.payload.previousState === DidCommMediationState.Requested), filter((event) => event.payload.mediationRecord.state === DidCommMediationState.Granted), first(), timeout({
			first: timeoutMs,
			meta: "MediationRecipientApi.requestAndAwaitGrant"
		})).subscribe(subject);
		const outboundMessageContext = new DidCommOutboundMessageContext(message, {
			agentContext: this.agentContext,
			connection,
			associatedRecord: mediationRecord
		});
		await this.sendMessage(outboundMessageContext);
		return (await firstValueFrom(subject)).payload.mediationRecord;
	}
	/**
	* Requests mediation for a given connection and sets that as default mediator.
	*
	* @param connection connection record which will be used for mediation
	* @returns mediation record
	*/
	async provision(connection) {
		this.logger.debug("Connection completed, requesting mediation");
		let mediation = await this.findByConnectionId(connection.id);
		if (!mediation) {
			this.logger.info(`Requesting mediation for connection ${connection.id}`);
			mediation = await this.requestAndAwaitGrant(connection, 6e4);
			this.logger.debug("Mediation granted, setting as default mediator");
			await this.setDefaultMediator(mediation);
			this.logger.debug("Default mediator set");
		} else this.logger.debug(`Mediator invitation has already been ${mediation.isReady ? "granted" : "requested"}`);
		return mediation;
	}
	async getRouting(options) {
		return this.routingService.getRouting(this.agentContext, options);
	}
};
DidCommMediationRecipientApi = __decorate([
	injectable(),
	__decorateParam(9, inject(InjectionSymbols.Logger)),
	__decorateParam(11, inject(InjectionSymbols.Stop$)),
	__decorateMetadata("design:paramtypes", [
		typeof (_ref = typeof DidCommMediationRecipientService !== "undefined" && DidCommMediationRecipientService) === "function" ? _ref : Object,
		typeof (_ref2 = typeof DidCommConnectionService !== "undefined" && DidCommConnectionService) === "function" ? _ref2 : Object,
		typeof (_ref3 = typeof DidsApi !== "undefined" && DidsApi) === "function" ? _ref3 : Object,
		typeof (_ref4 = typeof DidCommMessageSender !== "undefined" && DidCommMessageSender) === "function" ? _ref4 : Object,
		typeof (_ref5 = typeof EventEmitter !== "undefined" && EventEmitter) === "function" ? _ref5 : Object,
		typeof (_ref6 = typeof DidCommDiscoverFeaturesApi !== "undefined" && DidCommDiscoverFeaturesApi) === "function" ? _ref6 : Object,
		typeof (_ref7 = typeof DidCommMessagePickupApi !== "undefined" && DidCommMessagePickupApi) === "function" ? _ref7 : Object,
		typeof (_ref8 = typeof DidCommMediationRepository !== "undefined" && DidCommMediationRepository) === "function" ? _ref8 : Object,
		typeof (_ref9 = typeof DidCommRoutingService !== "undefined" && DidCommRoutingService) === "function" ? _ref9 : Object,
		Object,
		typeof (_ref10 = typeof AgentContext !== "undefined" && AgentContext) === "function" ? _ref10 : Object,
		typeof (_ref11 = typeof Subject !== "undefined" && Subject) === "function" ? _ref11 : Object,
		typeof (_ref12 = typeof DidCommMediationRecipientModuleConfig !== "undefined" && DidCommMediationRecipientModuleConfig) === "function" ? _ref12 : Object
	])
], DidCommMediationRecipientApi);

//#endregion
export { DidCommMediationRecipientApi };
//# sourceMappingURL=DidCommMediationRecipientApi.mjs.map