const require_rolldown_runtime = require('../../../_virtual/rolldown_runtime.js');
const require_decorateMetadata = require('../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorateMetadata.js');
const require_decorate = require('../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.js');
const require_TransportDecorator = require('./decorators/transport/TransportDecorator.js');
const require_MessageSendingError = require('./errors/MessageSendingError.js');
require('./errors/index.js');
const require_DidCommOutboundMessageContext = require('./models/DidCommOutboundMessageContext.js');
const require_DidCommOutboundMessageSendStatus = require('./models/DidCommOutboundMessageSendStatus.js');
require('./models/index.js');
const require_constants = require('./constants.js');
const require_DidCommEvents = require('./DidCommEvents.js');
const require_DidCommModuleConfig = require('./DidCommModuleConfig.js');
const require_DidCommTransportService = require('./DidCommTransportService.js');
const require_DidCommDocumentService = require('./services/DidCommDocumentService.js');
const require_DidCommEnvelopeService = require('./DidCommEnvelopeService.js');
let __credo_ts_core = require("@credo-ts/core");
__credo_ts_core = require_rolldown_runtime.__toESM(__credo_ts_core);

//#region ../didcomm/src/DidCommMessageSender.ts
var _ref, _ref2, _ref3, _ref4, _ref5;
let DidCommMessageSender = class DidCommMessageSender$1 {
	constructor(envelopeService, transportService, didCommModuleConfig, didCommDocumentService, eventEmitter) {
		this._outboundTransports = [];
		this.envelopeService = envelopeService;
		this.transportService = transportService;
		this.queueTransportRepository = didCommModuleConfig.queueTransportRepository;
		this.didCommDocumentService = didCommDocumentService;
		this.eventEmitter = eventEmitter;
		this._outboundTransports = [];
	}
	get outboundTransports() {
		return this._outboundTransports;
	}
	registerOutboundTransport(outboundTransport) {
		this._outboundTransports.push(outboundTransport);
	}
	async unregisterOutboundTransport(outboundTransport) {
		this._outboundTransports = this.outboundTransports.filter((transport) => transport !== outboundTransport);
		await outboundTransport.stop();
	}
	async packMessage(agentContext, { keys, message, endpoint }) {
		return {
			payload: await this.envelopeService.packMessage(agentContext, message, keys),
			responseRequested: message.hasAnyReturnRoute(),
			endpoint
		};
	}
	async sendMessageToSession(agentContext, session, message) {
		agentContext.config.logger.debug(`Packing message and sending it via existing session ${session.type}...`);
		if (!session.keys) throw new __credo_ts_core.CredoError(`There are no keys for the given ${session.type} transport session.`);
		const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, session.keys);
		agentContext.config.logger.debug("Sending message");
		await session.send(agentContext, encryptedMessage);
	}
	async sendPackage(agentContext, { connection, encryptedMessage, recipientKey, options }) {
		const errors = [];
		const session = this.transportService.findSessionByConnectionId(connection.id);
		if (session?.inboundMessage?.hasReturnRouting()) try {
			await session.send(agentContext, encryptedMessage);
			return;
		} catch (error) {
			errors.push(error);
			agentContext.config.logger.debug(`Sending packed message via session failed with error: ${error.message}.`, error);
		}
		const { services, queueService } = await this.retrieveServicesByConnection(agentContext, connection, options?.transportPriority);
		if (this.outboundTransports.length === 0 && !queueService) throw new __credo_ts_core.CredoError("Agent has no outbound transport!");
		for await (const service of services) {
			agentContext.config.logger.debug("Sending outbound message to service:", { service });
			try {
				const protocolScheme = __credo_ts_core.utils.getProtocolScheme(service.serviceEndpoint);
				for (const transport of this.outboundTransports) if (transport.supportedSchemes.includes(protocolScheme)) {
					await transport.sendMessage({
						payload: encryptedMessage,
						endpoint: service.serviceEndpoint,
						connectionId: connection.id
					});
					break;
				}
				return;
			} catch (error) {
				agentContext.config.logger.debug(`Sending outbound message to service with id ${service.id} failed with the following error:`, {
					message: error.message,
					error
				});
			}
		}
		if (queueService) {
			agentContext.config.logger.debug(`Queue packed message for connection ${connection.id} (${connection.theirLabel})`);
			await this.queueTransportRepository.addMessage(agentContext, {
				connectionId: connection.id,
				recipientDids: [(0, __credo_ts_core.verkeyToDidKey)(recipientKey)],
				payload: encryptedMessage
			});
			return;
		}
		agentContext.config.logger.error(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`, {
			message: encryptedMessage,
			errors,
			connection
		});
		throw new __credo_ts_core.CredoError(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`);
	}
	async sendMessage(outboundMessageContext, options) {
		const { agentContext, connection, outOfBand, message } = outboundMessageContext;
		const errors = [];
		if (outboundMessageContext.isOutboundServiceMessage()) return this.sendMessageToService(outboundMessageContext);
		if (!connection) {
			agentContext.config.logger.error("Outbound message has no associated connection");
			this.emitMessageSentEvent(outboundMessageContext, require_DidCommOutboundMessageSendStatus.OutboundMessageSendStatus.Undeliverable);
			throw new require_MessageSendingError.MessageSendingError("Outbound message has no associated connection", { outboundMessageContext });
		}
		agentContext.config.logger.debug("Send outbound message", {
			message,
			connectionId: connection.id
		});
		const session = this.findSessionForOutboundContext(outboundMessageContext);
		if (session) {
			agentContext.config.logger.debug(`Found session with return routing for message '${message.id}' (connection '${connection.id}'`);
			try {
				await this.sendMessageToSession(agentContext, session, message);
				this.emitMessageSentEvent(outboundMessageContext, require_DidCommOutboundMessageSendStatus.OutboundMessageSendStatus.SentToSession);
				return;
			} catch (error) {
				errors.push(error);
				agentContext.config.logger.debug(`Sending an outbound message via session failed with error: ${error.message}.`, error);
			}
		}
		let services = [];
		let queueService;
		try {
			({services, queueService} = await this.retrieveServicesByConnection(agentContext, connection, options?.transportPriority, outOfBand));
		} catch (error) {
			agentContext.config.logger.error(`Unable to retrieve services for connection '${connection.id}. ${error.message}`);
			this.emitMessageSentEvent(outboundMessageContext, require_DidCommOutboundMessageSendStatus.OutboundMessageSendStatus.Undeliverable);
			throw new require_MessageSendingError.MessageSendingError(`Unable to retrieve services for connection '${connection.id}`, {
				outboundMessageContext,
				cause: error
			});
		}
		if (!connection.did) {
			agentContext.config.logger.error(`Unable to send message using connection '${connection.id}' that doesn't have a did`);
			this.emitMessageSentEvent(outboundMessageContext, require_DidCommOutboundMessageSendStatus.OutboundMessageSendStatus.Undeliverable);
			throw new require_MessageSendingError.MessageSendingError(`Unable to send message using connection '${connection.id}' that doesn't have a did`, { outboundMessageContext });
		}
		const { didDocument, keys } = await agentContext.resolve(__credo_ts_core.DidsApi).resolveCreatedDidDocumentWithKeys(connection.did).catch((error) => {
			agentContext.config.logger.error(`Unable to send message using connection '${connection.id}', unable to resolve did`, { error });
			this.emitMessageSentEvent(outboundMessageContext, require_DidCommOutboundMessageSendStatus.OutboundMessageSendStatus.Undeliverable);
			throw new require_MessageSendingError.MessageSendingError(`Unable to send message using connection '${connection.id}'. Unble to resolve did`, {
				outboundMessageContext,
				cause: error
			});
		});
		const authentication = didDocument.authentication?.map((a) => {
			const verificationMethod = typeof a === "string" ? didDocument.dereferenceVerificationMethod(a) : a;
			const publicJwk = (0, __credo_ts_core.getPublicJwkFromVerificationMethod)(verificationMethod);
			const kmsKeyId = keys?.find((key) => verificationMethod.id.endsWith(key.didDocumentRelativeKeyId))?.kmsKeyId;
			publicJwk.keyId = kmsKeyId ?? publicJwk.legacyKeyId;
			return {
				verificationMethod,
				publicJwk,
				kmsKeyId
			};
		}).filter((v) => v.publicJwk.is(__credo_ts_core.Kms.Ed25519PublicJwk));
		const senderVerificationMethod = authentication?.find((a) => a.kmsKeyId !== void 0) ?? authentication?.[0];
		if (!senderVerificationMethod) throw new require_MessageSendingError.MessageSendingError(`Unable to determine sender key for did ${connection.did}, no available Ed25519 keys`, { outboundMessageContext });
		const shouldAddReturnRoute = message.transport?.returnRoute === void 0 && !this.transportService.hasInboundEndpoint(didDocument);
		for await (const service of services) try {
			await this.sendToService(new require_DidCommOutboundMessageContext.DidCommOutboundMessageContext(message, {
				agentContext,
				serviceParams: {
					service,
					senderKey: senderVerificationMethod.publicJwk,
					returnRoute: shouldAddReturnRoute
				},
				connection
			}));
			this.emitMessageSentEvent(outboundMessageContext, require_DidCommOutboundMessageSendStatus.OutboundMessageSendStatus.SentToTransport);
			return;
		} catch (error) {
			errors.push(error);
			agentContext.config.logger.debug(`Sending outbound message to service with id ${service.id} failed with the following error:`, {
				message: error.message,
				error
			});
		}
		if (queueService && message.allowQueueTransport) {
			agentContext.config.logger.debug(`Queue message for connection ${connection.id} (${connection.theirLabel})`);
			const keys$1 = {
				recipientKeys: queueService.recipientKeys,
				routingKeys: queueService.routingKeys,
				senderKey: senderVerificationMethod.publicJwk
			};
			const encryptedMessage = await this.envelopeService.packMessage(agentContext, message, keys$1);
			await this.queueTransportRepository.addMessage(agentContext, {
				connectionId: connection.id,
				recipientDids: keys$1.recipientKeys.map((item) => new __credo_ts_core.DidKey(item).did),
				payload: encryptedMessage
			});
			this.emitMessageSentEvent(outboundMessageContext, require_DidCommOutboundMessageSendStatus.OutboundMessageSendStatus.QueuedForPickup);
			return;
		}
		agentContext.config.logger.error(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel})`, {
			message,
			errors,
			connection
		});
		this.emitMessageSentEvent(outboundMessageContext, require_DidCommOutboundMessageSendStatus.OutboundMessageSendStatus.Undeliverable);
		throw new require_MessageSendingError.MessageSendingError(`Message is undeliverable to connection ${connection.id} (${connection.theirLabel}). \n\nReasons:\n\t- ${errors.map((e) => e.message).join("\n	-")}`, { outboundMessageContext });
	}
	async sendMessageToService(outboundMessageContext) {
		const session = this.findSessionForOutboundContext(outboundMessageContext);
		if (session) {
			outboundMessageContext.agentContext.config.logger.debug(`Found session with return routing for message '${outboundMessageContext.message.id}'`);
			try {
				await this.sendMessageToSession(outboundMessageContext.agentContext, session, outboundMessageContext.message);
				this.emitMessageSentEvent(outboundMessageContext, require_DidCommOutboundMessageSendStatus.OutboundMessageSendStatus.SentToSession);
				return;
			} catch (error) {
				outboundMessageContext.agentContext.config.logger.debug(`Sending an outbound message via session failed with error: ${error.message}.`, error);
			}
		}
		try {
			await this.sendToService(outboundMessageContext);
			this.emitMessageSentEvent(outboundMessageContext, require_DidCommOutboundMessageSendStatus.OutboundMessageSendStatus.SentToTransport);
		} catch (error) {
			outboundMessageContext.agentContext.config.logger.error(`Message is undeliverable to service with id ${outboundMessageContext.serviceParams?.service.id}: ${error.message}`, {
				message: outboundMessageContext.message,
				error
			});
			this.emitMessageSentEvent(outboundMessageContext, require_DidCommOutboundMessageSendStatus.OutboundMessageSendStatus.Undeliverable);
			throw new require_MessageSendingError.MessageSendingError(`Message is undeliverable to service with id ${outboundMessageContext.serviceParams?.service.id}: ${error.message}`, { outboundMessageContext });
		}
	}
	async sendToService(outboundMessageContext) {
		const { agentContext, message, serviceParams, connection } = outboundMessageContext;
		if (!serviceParams) throw new __credo_ts_core.CredoError("No service parameters found in outbound message context");
		const { service, senderKey, returnRoute } = serviceParams;
		if (this.outboundTransports.length === 0) throw new __credo_ts_core.CredoError("Agent has no outbound transport!");
		agentContext.config.logger.debug("Sending outbound message to service:", {
			messageId: message.id,
			service: {
				...service,
				recipientKeys: "omitted...",
				routingKeys: "omitted..."
			}
		});
		const keys = {
			recipientKeys: service.recipientKeys,
			routingKeys: service.routingKeys,
			senderKey
		};
		if (returnRoute) message.setReturnRouting(require_TransportDecorator.ReturnRouteTypes.all);
		try {
			__credo_ts_core.MessageValidator.validateSync(message);
		} catch (error) {
			agentContext.config.logger.error(`Aborting sending outbound message ${message.type} to ${service.serviceEndpoint}. Message validation failed`, {
				errors: error,
				message: message.toJSON()
			});
			throw error;
		}
		const outboundPackage = await this.packMessage(agentContext, {
			message,
			keys,
			endpoint: service.serviceEndpoint
		});
		outboundPackage.endpoint = service.serviceEndpoint;
		outboundPackage.connectionId = connection?.id;
		for (const transport of this.outboundTransports) {
			const protocolScheme = __credo_ts_core.utils.getProtocolScheme(service.serviceEndpoint);
			if (!protocolScheme) agentContext.config.logger.warn("Service does not have a protocol scheme.");
			else if (transport.supportedSchemes.includes(protocolScheme)) {
				await transport.sendMessage(outboundPackage);
				return;
			}
		}
		throw new require_MessageSendingError.MessageSendingError(`Unable to send message to service: ${service.serviceEndpoint}`, { outboundMessageContext });
	}
	findSessionForOutboundContext(outboundContext) {
		let session = void 0;
		const sessionId = outboundContext.sessionId ?? outboundContext.inboundMessageContext?.sessionId;
		if (sessionId) session = this.transportService.findSessionById(sessionId);
		if (!session && outboundContext.connection?.id) session = this.transportService.findSessionByConnectionId(outboundContext.connection.id);
		return session?.inboundMessage?.hasAnyReturnRoute() ? session : null;
	}
	async retrieveServicesByConnection(agentContext, connection, transportPriority, outOfBand) {
		agentContext.config.logger.debug(`Retrieving services for connection '${connection.id}' (${connection.theirLabel})`, {
			transportPriority,
			connection
		});
		let didCommServices = [];
		if (connection.theirDid) {
			agentContext.config.logger.debug(`Resolving services for connection theirDid ${connection.theirDid}.`);
			didCommServices = await this.didCommDocumentService.resolveServicesFromDid(agentContext, connection.theirDid);
		} else if (outOfBand) {
			agentContext.config.logger.debug(`Resolving services from out-of-band record ${outOfBand.id}.`);
			if (connection.isRequester) for (const service of outOfBand.outOfBandInvitation.getServices()) if (typeof service === "string") {
				agentContext.config.logger.debug(`Resolving services for did ${service}.`);
				didCommServices.push(...await this.didCommDocumentService.resolveServicesFromDid(agentContext, service));
			} else didCommServices.push({
				id: service.id,
				recipientKeys: service.recipientKeys.map(__credo_ts_core.didKeyToEd25519PublicJwk),
				routingKeys: service.routingKeys?.map(__credo_ts_core.didKeyToEd25519PublicJwk) || [],
				serviceEndpoint: service.serviceEndpoint
			});
		}
		let services = didCommServices.filter((s) => !isDidCommTransportQueue(s.serviceEndpoint));
		const queueService = didCommServices.find((s) => isDidCommTransportQueue(s.serviceEndpoint));
		if (transportPriority?.restrictive) services = services.filter((service) => {
			const serviceSchema = __credo_ts_core.utils.getProtocolScheme(service.serviceEndpoint);
			return transportPriority.schemes.includes(serviceSchema);
		});
		if (transportPriority?.schemes) services = services.sort((a, b) => {
			const aScheme = __credo_ts_core.utils.getProtocolScheme(a.serviceEndpoint);
			const bScheme = __credo_ts_core.utils.getProtocolScheme(b.serviceEndpoint);
			return transportPriority?.schemes.indexOf(aScheme) - transportPriority?.schemes.indexOf(bScheme);
		});
		agentContext.config.logger.debug(`Retrieved ${services.length} services for message to connection '${connection.id}'(${connection.theirLabel})'`, {
			hasQueueService: queueService !== void 0,
			transportPriority
		});
		return {
			services,
			queueService
		};
	}
	emitMessageSentEvent(outboundMessageContext, status) {
		const { agentContext } = outboundMessageContext;
		this.eventEmitter.emit(agentContext, {
			type: require_DidCommEvents.DidCommEventTypes.DidCommMessageSent,
			payload: {
				message: outboundMessageContext,
				status
			}
		});
	}
};
DidCommMessageSender = require_decorate.__decorate([(0, __credo_ts_core.injectable)(), require_decorateMetadata.__decorateMetadata("design:paramtypes", [
	typeof (_ref = typeof require_DidCommEnvelopeService.DidCommEnvelopeService !== "undefined" && require_DidCommEnvelopeService.DidCommEnvelopeService) === "function" ? _ref : Object,
	typeof (_ref2 = typeof require_DidCommTransportService.DidCommTransportService !== "undefined" && require_DidCommTransportService.DidCommTransportService) === "function" ? _ref2 : Object,
	typeof (_ref3 = typeof require_DidCommModuleConfig.DidCommModuleConfig !== "undefined" && require_DidCommModuleConfig.DidCommModuleConfig) === "function" ? _ref3 : Object,
	typeof (_ref4 = typeof require_DidCommDocumentService.DidCommDocumentService !== "undefined" && require_DidCommDocumentService.DidCommDocumentService) === "function" ? _ref4 : Object,
	typeof (_ref5 = typeof __credo_ts_core.EventEmitter !== "undefined" && __credo_ts_core.EventEmitter) === "function" ? _ref5 : Object
])], DidCommMessageSender);
function isDidCommTransportQueue(serviceEndpoint) {
	return serviceEndpoint === require_constants.DID_COMM_TRANSPORT_QUEUE;
}

//#endregion
Object.defineProperty(exports, 'DidCommMessageSender', {
  enumerable: true,
  get: function () {
    return DidCommMessageSender;
  }
});