const require_rolldown_runtime = require('../../../_virtual/rolldown_runtime.js');
const require_ServiceDecorator = require('./decorators/service/ServiceDecorator.js');
const require_DidCommOutboundMessageContext = require('./models/DidCommOutboundMessageContext.js');
require('./models/index.js');
const require_DidCommOutOfBandRole = require('./modules/oob/domain/DidCommOutOfBandRole.js');
const require_DidCommOutOfBandInvitation = require('./modules/oob/messages/DidCommOutOfBandInvitation.js');
const require_DidCommOutOfBandRepository = require('./modules/oob/repository/DidCommOutOfBandRepository.js');
const require_DidCommOutOfBandService = require('./modules/oob/DidCommOutOfBandService.js');
const require_outOfBandRecordMetadataTypes = require('./modules/oob/repository/outOfBandRecordMetadataTypes.js');
const require_DidCommRoutingService = require('./modules/routing/services/DidCommRoutingService.js');
require('./modules/oob/index.js');
require('./modules/routing/index.js');
const require_DidCommMessageRole = require('./repository/DidCommMessageRole.js');
const require_DidCommMessageRepository = require('./repository/DidCommMessageRepository.js');
require('./repository/index.js');
let __credo_ts_core = require("@credo-ts/core");
__credo_ts_core = require_rolldown_runtime.__toESM(__credo_ts_core);

//#region ../didcomm/src/getDidCommOutboundMessageContext.ts
/**
* Maybe these methods should be moved to a service, but that would require
* extra injection in the sender functions, and I'm not 100% sure what would
* be the best place to host these.
*/
/**
* Get the outbound message context for a message. Will use the connection record if available,
* and otherwise try to create a connectionless message context.
*/
async function getOutboundDidCommMessageContext(agentContext, { message, connectionRecord, associatedRecord, lastReceivedMessage, lastSentMessage }) {
	if (connectionRecord) {
		agentContext.config.logger.debug(`Creating outbound message context for message ${message.id} with connection ${connectionRecord.id}`);
		return new require_DidCommOutboundMessageContext.DidCommOutboundMessageContext(message, {
			agentContext,
			associatedRecord,
			connection: connectionRecord
		});
	}
	if (!lastReceivedMessage) throw new __credo_ts_core.CredoError("No connection record and no lastReceivedMessage was supplied. For connection-less exchanges the lastReceivedMessage is required.");
	if (!associatedRecord) throw new __credo_ts_core.CredoError("No associated record was supplied. This is required for connection-less exchanges to store the associated ~service decorator on the message.");
	return getConnectionlessOutboundMessageContext(agentContext, {
		message,
		associatedRecord,
		lastReceivedMessage,
		lastSentMessage
	});
}
async function getConnectionlessOutboundMessageContext(agentContext, { message, lastReceivedMessage, lastSentMessage, associatedRecord }) {
	agentContext.config.logger.debug(`Creating outbound message context for message ${message.id} using connection-less exchange`);
	const outOfBandRecord = await getOutOfBandRecordForMessage(agentContext, message);
	let { recipientService, ourService } = await getServicesForMessage(agentContext, {
		lastReceivedMessage,
		lastSentMessage,
		message,
		outOfBandRecord
	});
	if (!lastSentMessage) ourService = await createOurService(agentContext, {
		outOfBandRecord,
		message
	});
	if (!ourService) throw new __credo_ts_core.CredoError(`Could not determine our service for connection-less exchange for message ${message.id}.`);
	if (!recipientService) throw new __credo_ts_core.CredoError(`Could not determine recipient service for connection-less exchange for message ${message.id}.`);
	await addExchangeDataToMessage(agentContext, {
		message,
		ourService,
		outOfBandRecord,
		associatedRecord
	});
	return new require_DidCommOutboundMessageContext.DidCommOutboundMessageContext(message, {
		agentContext,
		associatedRecord,
		serviceParams: {
			service: recipientService,
			senderKey: ourService.recipientKeys[0],
			returnRoute: true
		}
	});
}
/**
* Retrieves the out of band record associated with the message based on the thread id of the message.
*/
async function getOutOfBandRecordForMessage(agentContext, message) {
	agentContext.config.logger.debug(`Looking for out-of-band record for message ${message.id} with thread id ${message.threadId} and type ${message.type}`);
	return await agentContext.dependencyManager.resolve(require_DidCommOutOfBandRepository.DidCommOutOfBandRepository).findSingleByQuery(agentContext, { invitationRequestsThreadIds: [message.threadId] }) ?? void 0;
}
/**
* Returns the services to use for the message. When available it will extract the services from the
* lastSentMessage and lastReceivedMessage. If not available it will try to extract the services from
* the out of band record.
*
* If the required services and fields are not available, an error will be thrown.
*/
async function getServicesForMessage(agentContext, { lastSentMessage, lastReceivedMessage, message, outOfBandRecord }) {
	let ourService = lastSentMessage?.service?.resolvedDidCommService;
	let recipientService = lastReceivedMessage.service?.resolvedDidCommService;
	const outOfBandService = agentContext.dependencyManager.resolve(require_DidCommOutOfBandService.DidCommOutOfBandService);
	if (outOfBandRecord?.role === require_DidCommOutOfBandRole.DidCommOutOfBandRole.Sender) {
		if (!ourService) ourService = await outOfBandService.getResolvedServiceForOutOfBandServices(agentContext, outOfBandRecord.outOfBandInvitation.getServices(), outOfBandRecord.invitationInlineServiceKeys);
		if (!recipientService) throw new __credo_ts_core.CredoError("Could not find a service to send the message to. Please make sure the connection has a service or provide a service to send the message to.");
		if (!lastSentMessage) throw new __credo_ts_core.CredoError("Must have lastSentMessage when out of band record has role Sender");
	} else if (outOfBandRecord?.role === require_DidCommOutOfBandRole.DidCommOutOfBandRole.Receiver) {
		if (!recipientService) recipientService = await outOfBandService.getResolvedServiceForOutOfBandServices(agentContext, outOfBandRecord.outOfBandInvitation.getServices());
		if (lastSentMessage && !ourService) throw new __credo_ts_core.CredoError("Could not find a service to send the message to. Please make sure the connection has a service or provide a service to send the message to.");
		const oobRecordRecipientRouting = outOfBandRecord?.metadata.get(require_outOfBandRecordMetadataTypes.DidCommOutOfBandRecordMetadataKeys.RecipientRouting);
		if (oobRecordRecipientRouting && ourService) ourService.recipientKeys[0].keyId = oobRecordRecipientRouting.recipientKeyId ?? ourService.recipientKeys[0].legacyKeyId;
	} else {
		if (lastSentMessage && !ourService) {
			agentContext.config.logger.error(`No out of band record associated and missing our service for connection-less exchange for message ${message.id}, while previous message has already been sent.`);
			throw new __credo_ts_core.CredoError(`No out of band record associated and missing our service for connection-less exchange for message ${message.id}, while previous message has already been sent.`);
		}
		if (!recipientService) {
			agentContext.config.logger.error(`No out of band record associated and missing recipient service for connection-less exchange for message ${message.id}.`);
			throw new __credo_ts_core.CredoError(`No out of band record associated and missing recipient service for connection-less exchange for message ${message.id}.`);
		}
	}
	return {
		ourService,
		recipientService
	};
}
/**
* Creates a new service for us as the sender to be used in a connection-less exchange.
*
* Will creating routing, which takes into account mediators, and will optionally extract
* routing configuration from the out of band record if available.
*/
async function createOurService(agentContext, { outOfBandRecord, message }) {
	agentContext.config.logger.debug(`No previous sent message in thread for outbound message ${message.id} with type ${message.type}, setting up routing`);
	let routing = void 0;
	const oobRecordRecipientRouting = outOfBandRecord?.metadata.get(require_outOfBandRecordMetadataTypes.DidCommOutOfBandRecordMetadataKeys.RecipientRouting);
	if (oobRecordRecipientRouting) {
		const recipientPublicJwk = __credo_ts_core.Kms.PublicJwk.fromFingerprint(oobRecordRecipientRouting.recipientKeyFingerprint);
		recipientPublicJwk.keyId = oobRecordRecipientRouting.recipientKeyId ?? recipientPublicJwk.legacyKeyId;
		routing = {
			recipientKey: recipientPublicJwk,
			routingKeys: oobRecordRecipientRouting.routingKeyFingerprints.map((fingerprint) => __credo_ts_core.Kms.PublicJwk.fromFingerprint(fingerprint)),
			endpoints: oobRecordRecipientRouting.endpoints,
			mediatorId: oobRecordRecipientRouting.mediatorId
		};
	}
	if (!routing) {
		routing = await agentContext.dependencyManager.resolve(require_DidCommRoutingService.DidCommRoutingService).getRouting(agentContext, { mediatorId: outOfBandRecord?.mediatorId });
		if (outOfBandRecord) {
			agentContext.config.logger.debug("Storing routing for out of band invitation.");
			outOfBandRecord.metadata.set(require_outOfBandRecordMetadataTypes.DidCommOutOfBandRecordMetadataKeys.RecipientRouting, {
				recipientKeyFingerprint: routing.recipientKey.fingerprint,
				recipientKeyId: routing.recipientKey.keyId,
				routingKeyFingerprints: routing.routingKeys.map((key) => key.fingerprint),
				endpoints: routing.endpoints,
				mediatorId: routing.mediatorId
			});
			outOfBandRecord.setTags({ recipientRoutingKeyFingerprint: routing.recipientKey.fingerprint });
			await agentContext.resolve(require_DidCommOutOfBandRepository.DidCommOutOfBandRepository).update(agentContext, outOfBandRecord);
		}
	}
	return {
		id: __credo_ts_core.utils.uuid(),
		serviceEndpoint: routing.endpoints[0],
		recipientKeys: [routing.recipientKey],
		routingKeys: routing.routingKeys
	};
}
async function addExchangeDataToMessage(agentContext, { message, ourService, outOfBandRecord, associatedRecord }) {
	const legacyInvitationMetadata = outOfBandRecord?.metadata.get(require_outOfBandRecordMetadataTypes.DidCommOutOfBandRecordMetadataKeys.LegacyInvitation);
	if (outOfBandRecord && legacyInvitationMetadata?.legacyInvitationType !== require_DidCommOutOfBandInvitation.DidCommInvitationType.Connectionless) if (!message.thread) message.setThread({ parentThreadId: outOfBandRecord.outOfBandInvitation.id });
	else message.thread.parentThreadId = outOfBandRecord.outOfBandInvitation.id;
	message.service = require_ServiceDecorator.ServiceDecorator.fromResolvedDidCommService(ourService);
	await agentContext.dependencyManager.resolve(require_DidCommMessageRepository.DidCommMessageRepository).saveOrUpdateAgentMessage(agentContext, {
		agentMessage: message,
		role: require_DidCommMessageRole.DidCommMessageRole.Sender,
		associatedRecordId: associatedRecord.id
	});
}

//#endregion
exports.getOutboundDidCommMessageContext = getOutboundDidCommMessageContext;