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 { DidCommAttachment, DidCommAttachmentData } from "../../decorators/attachment/DidCommAttachment.mjs";
import "../../models/index.mjs";
import { DidCommDidExchangeState } from "./models/DidCommDidExchangeState.mjs";
import { DidCommDidExchangeRole } from "./models/DidCommDidExchangeRole.mjs";
import { DidCommHandshakeProtocol } from "./models/DidCommHandshakeProtocol.mjs";
import "./models/index.mjs";
import { DidCommDidExchangeRequestMessage } from "./messages/DidCommDidExchangeRequestMessage.mjs";
import { DidCommDidExchangeResponseMessage } from "./messages/DidCommDidExchangeResponseMessage.mjs";
import { DidCommDidExchangeCompleteMessage } from "./messages/DidCommDidExchangeCompleteMessage.mjs";
import "./messages/index.mjs";
import { DidExchangeProblemReportError } from "./errors/DidExchangeProblemReportError.mjs";
import { DidExchangeProblemReportReason } from "./errors/DidExchangeProblemReportReason.mjs";
import "./errors/index.mjs";
import { DidCommOutOfBandState } from "../oob/domain/DidCommOutOfBandState.mjs";
import { __decorateParam } from "../../../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorateParam.mjs";
import { DidCommDocumentService } from "../../services/DidCommDocumentService.mjs";
import "../../services/index.mjs";
import { createPeerDidFromServices, getResolvedDidcommServiceWithSigningKeyId, routingToServices } from "./services/helpers.mjs";
import { DidCommOutOfBandRole } from "../oob/domain/DidCommOutOfBandRole.mjs";
import { DidCommConnectionService } from "./services/DidCommConnectionService.mjs";
import { getMediationRecordForDidDocument } from "../routing/services/helpers.mjs";
import { DidCommConnectionsModuleConfig } from "./DidCommConnectionsModuleConfig.mjs";
import "./services/index.mjs";
import { DidExchangeStateMachine } from "./DidExchangeStateMachine.mjs";
import { Buffer, CredoError, DidDocument, DidKey, DidRepository, DidsApi, InjectionSymbols, JsonEncoder, JsonTransformer, JwsService, Kms, PeerDidNumAlgo, TypedArrayEncoder, base64ToBase64URL, getAlternativeDidsForPeerDid, getNumAlgoFromPeerDid, getPublicJwkFromVerificationMethod, inject, injectable, isDid, isValidPeerDid, parseDid, tryParseDid } from "@credo-ts/core";

//#region ../didcomm/src/modules/connections/DidExchangeProtocol.ts
var _ref, _ref2, _ref3, _ref4;
let DidExchangeProtocol = class DidExchangeProtocol$1 {
	constructor(connectionService, didRepository, jwsService, didcommDocumentService, logger) {
		this.connectionService = connectionService;
		this.didRepository = didRepository;
		this.jwsService = jwsService;
		this.didcommDocumentService = didcommDocumentService;
		this.logger = logger;
	}
	async createRequest(agentContext, outOfBandRecord, params) {
		this.logger.debug(`Create message ${DidCommDidExchangeRequestMessage.type.messageTypeUri} start`, {
			outOfBandRecord,
			params
		});
		const config = agentContext.dependencyManager.resolve(DidCommConnectionsModuleConfig);
		const { outOfBandInvitation } = outOfBandRecord;
		const { alias, goal, goalCode, routing, autoAcceptConnection, ourDid: did } = params;
		const [invitationDid] = outOfBandInvitation.invitationDids;
		const label = params.label;
		let didDocument;
		let keys;
		let mediatorId;
		if (did) {
			const resolved = await agentContext.resolve(DidsApi).resolveCreatedDidDocumentWithKeys(did);
			didDocument = resolved.didDocument;
			keys = resolved.keys;
			mediatorId = (await getMediationRecordForDidDocument(agentContext, didDocument))?.id;
		} else {
			if (!routing) throw new CredoError(`'routing' must be defined if 'ourDid' is not specified`);
			const resolved = await createPeerDidFromServices(agentContext, routingToServices(routing), config.peerNumAlgoForDidExchangeRequests);
			didDocument = resolved.didDocument;
			keys = resolved.keys;
			mediatorId = routing.mediatorId;
		}
		const parentThreadId = outOfBandRecord.outOfBandInvitation.id;
		const message = new DidCommDidExchangeRequestMessage({
			label,
			parentThreadId,
			did: didDocument.id,
			goal,
			goalCode
		});
		const signingKeys = didDocument.getRecipientKeysWithVerificationMethod({ mapX25519ToEd25519: true }).map(({ publicJwk, verificationMethod }) => {
			publicJwk.keyId = keys?.find(({ didDocumentRelativeKeyId }) => verificationMethod.id.endsWith(didDocumentRelativeKeyId))?.kmsKeyId ?? publicJwk.legacyKeyId;
			return publicJwk;
		});
		if (isValidPeerDid(didDocument.id) && getNumAlgoFromPeerDid(didDocument.id) === PeerDidNumAlgo.GenesisDoc) message.didDoc = await this.createSignedAttachment(agentContext, didDocument.toJSON(), signingKeys);
		const connectionRecord = await this.connectionService.createConnection(agentContext, {
			protocol: DidCommHandshakeProtocol.DidExchange,
			role: DidCommDidExchangeRole.Requester,
			alias,
			state: DidCommDidExchangeState.InvitationReceived,
			theirLabel: outOfBandInvitation.label,
			mediatorId,
			autoAcceptConnection: outOfBandRecord.autoAcceptConnection,
			outOfBandId: outOfBandRecord.id,
			invitationDid,
			imageUrl: outOfBandInvitation.imageUrl
		});
		DidExchangeStateMachine.assertCreateMessageState(DidCommDidExchangeRequestMessage.type, connectionRecord);
		connectionRecord.did = didDocument.id;
		connectionRecord.threadId = message.id;
		if (autoAcceptConnection !== void 0 || autoAcceptConnection !== null) connectionRecord.autoAcceptConnection = autoAcceptConnection;
		await this.updateState(agentContext, DidCommDidExchangeRequestMessage.type, connectionRecord);
		this.logger.debug(`Create message ${DidCommDidExchangeRequestMessage.type.messageTypeUri} end`, {
			connectionRecord,
			message
		});
		return {
			message,
			connectionRecord
		};
	}
	async processRequest(messageContext, outOfBandRecord) {
		this.logger.debug(`Process message ${messageContext.message.type} start`, { message: messageContext.message });
		outOfBandRecord.assertRole(DidCommOutOfBandRole.Sender);
		outOfBandRecord.assertState(DidCommOutOfBandState.AwaitResponse);
		const { message, agentContext } = messageContext;
		const parentThreadId = message.thread?.parentThreadId;
		if (!parentThreadId || !tryParseDid(parentThreadId) && parentThreadId !== outOfBandRecord.getTags().invitationId) throw new DidExchangeProblemReportError("Missing reference to invitation.", { problemCode: DidExchangeProblemReportReason.RequestNotAccepted });
		const didDocument = await this.resolveDidDocument(agentContext, message);
		const didRecord = await this.didRepository.storeReceivedDid(messageContext.agentContext, {
			did: didDocument.id,
			didDocument: !isValidPeerDid(didDocument.id) || getNumAlgoFromPeerDid(message.did) === PeerDidNumAlgo.GenesisDoc ? didDocument : void 0,
			tags: {
				recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint),
				alternativeDids: isValidPeerDid(didDocument.id) ? getAlternativeDidsForPeerDid(didDocument.id) : void 0
			}
		});
		this.logger.debug("Saved DID record", {
			id: didRecord.id,
			did: didRecord.did,
			role: didRecord.role,
			tags: didRecord.getTags(),
			didDocument: "omitted..."
		});
		const connectionRecord = await this.connectionService.createConnection(messageContext.agentContext, {
			protocol: DidCommHandshakeProtocol.DidExchange,
			role: DidCommDidExchangeRole.Responder,
			state: DidCommDidExchangeState.RequestReceived,
			alias: outOfBandRecord.alias,
			theirDid: message.did,
			theirLabel: message.label,
			threadId: message.threadId,
			mediatorId: outOfBandRecord.mediatorId,
			autoAcceptConnection: outOfBandRecord.autoAcceptConnection,
			outOfBandId: outOfBandRecord.id
		});
		await this.updateState(messageContext.agentContext, DidCommDidExchangeRequestMessage.type, connectionRecord);
		this.logger.debug(`Process message ${DidCommDidExchangeRequestMessage.type.messageTypeUri} end`, connectionRecord);
		return connectionRecord;
	}
	async createResponse(agentContext, connectionRecord, outOfBandRecord, routing) {
		this.logger.debug(`Create message ${DidCommDidExchangeResponseMessage.type.messageTypeUri} start`, connectionRecord);
		DidExchangeStateMachine.assertCreateMessageState(DidCommDidExchangeResponseMessage.type, connectionRecord);
		const { threadId, theirDid } = connectionRecord;
		const config = agentContext.dependencyManager.resolve(DidCommConnectionsModuleConfig);
		if (!threadId) throw new CredoError("Missing threadId on connection record.");
		if (!theirDid) throw new CredoError("Missing theirDid on connection record.");
		const inlineResolvedServices = outOfBandRecord.outOfBandInvitation.getInlineServices().map((service) => getResolvedDidcommServiceWithSigningKeyId(service, outOfBandRecord.invitationInlineServiceKeys));
		let services = [];
		if (routing) services = routingToServices(routing);
		else if (inlineResolvedServices.length > 0) services = inlineResolvedServices;
		else throw new CredoError("No routing provided, and no inline services found in out of band invitation. When using did services in out of band invitation, make sure to provide routing information for rotation.");
		const numAlgo = isValidPeerDid(theirDid) ? getNumAlgoFromPeerDid(theirDid) : config.peerNumAlgoForDidExchangeRequests;
		const { didDocument } = await createPeerDidFromServices(agentContext, services, numAlgo);
		const message = new DidCommDidExchangeResponseMessage({
			did: didDocument.id,
			threadId
		});
		const invitationRecipientKeys = inlineResolvedServices.flatMap((s) => s.recipientKeys);
		for (const did of outOfBandRecord.outOfBandInvitation.getDidServices()) {
			const resolved = await agentContext.resolve(DidsApi).resolveCreatedDidDocumentWithKeys(parseDid(did).did);
			invitationRecipientKeys.push(...resolved.didDocument.getRecipientKeysWithVerificationMethod({ mapX25519ToEd25519: true }).map(({ publicJwk, verificationMethod }) => {
				publicJwk.keyId = resolved.keys?.find(({ didDocumentRelativeKeyId }) => verificationMethod.id.endsWith(didDocumentRelativeKeyId))?.kmsKeyId ?? publicJwk.legacyKeyId;
				return publicJwk;
			}));
		}
		if (numAlgo === PeerDidNumAlgo.GenesisDoc) message.didDoc = await this.createSignedAttachment(agentContext, didDocument.toJSON(), invitationRecipientKeys);
		else message.didRotate = await this.createSignedAttachment(agentContext, didDocument.id, invitationRecipientKeys);
		connectionRecord.did = didDocument.id;
		await this.updateState(agentContext, DidCommDidExchangeResponseMessage.type, connectionRecord);
		this.logger.debug(`Create message ${DidCommDidExchangeResponseMessage.type.messageTypeUri} end`, {
			connectionRecord,
			message
		});
		return message;
	}
	async processResponse(messageContext, outOfBandRecord) {
		this.logger.debug(`Process message ${DidCommDidExchangeResponseMessage.type.messageTypeUri} start`, { message: messageContext.message });
		const { connection: connectionRecord, message, agentContext } = messageContext;
		if (!connectionRecord) throw new CredoError("No connection record in message context.");
		DidExchangeStateMachine.assertProcessMessageState(DidCommDidExchangeResponseMessage.type, connectionRecord);
		if (!message.thread?.threadId || message.thread?.threadId !== connectionRecord.threadId) throw new DidExchangeProblemReportError("Invalid or missing thread ID.", { problemCode: DidExchangeProblemReportReason.ResponseNotAccepted });
		const didDocument = await this.resolveDidDocument(agentContext, message, outOfBandRecord.getTags().recipientKeyFingerprints.map((fingerprint) => {
			const publicJwk = Kms.PublicJwk.fromFingerprint(fingerprint);
			if (!publicJwk.is(Kms.Ed25519PublicJwk)) throw new CredoError("Expected fingerprint to be of type Ed25519");
			return publicJwk;
		}));
		if (isValidPeerDid(didDocument.id)) {
			const didRecord = await this.didRepository.storeReceivedDid(messageContext.agentContext, {
				did: didDocument.id,
				didDocument: getNumAlgoFromPeerDid(message.did) === PeerDidNumAlgo.GenesisDoc ? didDocument : void 0,
				tags: {
					recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint),
					alternativeDids: getAlternativeDidsForPeerDid(didDocument.id)
				}
			});
			this.logger.debug("Saved DID record", {
				id: didRecord.id,
				did: didRecord.did,
				role: didRecord.role,
				tags: didRecord.getTags(),
				didDocument: "omitted..."
			});
		}
		connectionRecord.theirDid = message.did;
		await this.updateState(messageContext.agentContext, DidCommDidExchangeResponseMessage.type, connectionRecord);
		this.logger.debug(`Process message ${DidCommDidExchangeResponseMessage.type.messageTypeUri} end`, connectionRecord);
		return connectionRecord;
	}
	async createComplete(agentContext, connectionRecord, outOfBandRecord) {
		this.logger.debug(`Create message ${DidCommDidExchangeCompleteMessage.type.messageTypeUri} start`, connectionRecord);
		DidExchangeStateMachine.assertCreateMessageState(DidCommDidExchangeCompleteMessage.type, connectionRecord);
		const threadId = connectionRecord.threadId;
		const parentThreadId = outOfBandRecord.outOfBandInvitation.id;
		if (!threadId) throw new CredoError(`Connection record ${connectionRecord.id} does not have 'threadId' attribute.`);
		if (!parentThreadId) throw new CredoError(`Connection record ${connectionRecord.id} does not have 'parentThreadId' attribute.`);
		const message = new DidCommDidExchangeCompleteMessage({
			threadId,
			parentThreadId
		});
		await this.updateState(agentContext, DidCommDidExchangeCompleteMessage.type, connectionRecord);
		this.logger.debug(`Create message ${DidCommDidExchangeCompleteMessage.type.messageTypeUri} end`, {
			connectionRecord,
			message
		});
		return message;
	}
	async processComplete(messageContext, outOfBandRecord) {
		this.logger.debug(`Process message ${DidCommDidExchangeCompleteMessage.type.messageTypeUri} start`, { message: messageContext.message });
		const { connection: connectionRecord, message } = messageContext;
		if (!connectionRecord) throw new CredoError("No connection record in message context.");
		DidExchangeStateMachine.assertProcessMessageState(DidCommDidExchangeCompleteMessage.type, connectionRecord);
		if (message.threadId !== connectionRecord.threadId) throw new DidExchangeProblemReportError("Invalid or missing thread ID.", { problemCode: DidExchangeProblemReportReason.CompleteRejected });
		const pthid = message.thread?.parentThreadId;
		if (!pthid || pthid !== outOfBandRecord.outOfBandInvitation.id) throw new DidExchangeProblemReportError("Invalid or missing parent thread ID referencing to the invitation.", { problemCode: DidExchangeProblemReportReason.CompleteRejected });
		await this.updateState(messageContext.agentContext, DidCommDidExchangeCompleteMessage.type, connectionRecord);
		this.logger.debug(`Process message ${DidCommDidExchangeCompleteMessage.type.messageTypeUri} end`, { connectionRecord });
		return connectionRecord;
	}
	async updateState(agentContext, messageType, connectionRecord) {
		this.logger.debug("Updating state", { connectionRecord });
		const nextState = DidExchangeStateMachine.nextState(messageType, connectionRecord);
		return this.connectionService.updateState(agentContext, connectionRecord, nextState);
	}
	async createSignedAttachment(agentContext, data, signingKeys) {
		this.logger.debug("Creating signed attachment");
		const signedAttach = new DidCommAttachment({
			mimeType: typeof data === "string" ? void 0 : "application/json",
			data: new DidCommAttachmentData({ base64: typeof data === "string" ? TypedArrayEncoder.toBase64URL(Buffer.from(data)) : JsonEncoder.toBase64(data) })
		});
		await Promise.all(signingKeys.map(async (signingKey) => {
			const kid = new DidKey(signingKey).did;
			const payload = typeof data === "string" ? TypedArrayEncoder.fromString(data) : JsonEncoder.toBuffer(data);
			const jws = await this.jwsService.createJws(agentContext, {
				payload,
				keyId: signingKey.keyId,
				header: { kid },
				protectedHeaderOptions: {
					alg: Kms.KnownJwaSignatureAlgorithms.EdDSA,
					jwk: signingKey
				}
			});
			signedAttach.addJws(jws);
		}));
		return signedAttach;
	}
	/**
	* Resolves a did document from a given `request` or `response` message, verifying its signature or did rotate
	* signature in case it is taken from message attachment.
	*
	* @param message DID request or DID response message
	* @param invitationKeys array containing keys from connection invitation that could be used for signing of DID document
	* @returns verified DID document content from message attachment
	*/
	async resolveDidDocument(agentContext, message, invitationKeys = []) {
		return isDid(message.did, "peer") && getNumAlgoFromPeerDid(message.did) === PeerDidNumAlgo.GenesisDoc ? this.extractAttachedDidDocument(agentContext, message, invitationKeys) : this.extractResolvableDidDocument(agentContext, message, invitationKeys);
	}
	/**
	* Extracts DID document from message (resolving it externally if required) and verifies did-rotate attachment signature
	* if applicable
	*/
	async extractResolvableDidDocument(agentContext, message, invitationKeys) {
		if (message instanceof DidCommDidExchangeResponseMessage) {
			const didRotateAttachment = message.didRotate;
			if (!didRotateAttachment) throw new DidExchangeProblemReportError("DID Rotate attachment is missing.", { problemCode: DidExchangeProblemReportReason.ResponseNotAccepted });
			const jws = didRotateAttachment.data.jws;
			if (!jws) throw new DidExchangeProblemReportError("DID Rotate signature is missing.", { problemCode: DidExchangeProblemReportReason.ResponseNotAccepted });
			if (!didRotateAttachment.data.base64) throw new CredoError("DID Rotate attachment is missing base64 property for signed did.");
			const base64UrlPayload = base64ToBase64URL(didRotateAttachment.data.base64);
			if (TypedArrayEncoder.fromBase64(base64UrlPayload).toString() !== message.did) throw new CredoError(`DID Rotate attachment's did ${message.did} does not correspond to message did ${message.did}`);
			const { isValid, jwsSigners } = await this.jwsService.verifyJws(agentContext, {
				jws: {
					...jws,
					payload: base64UrlPayload
				},
				allowedJwsSignerMethods: ["did"],
				resolveJwsSigner: ({ jws: { header } }) => {
					if (typeof header.kid !== "string" || !isDid(header.kid, "key")) throw new CredoError("JWS header kid must be a did:key DID.");
					const didKey = DidKey.fromDid(header.kid);
					return {
						method: "did",
						didUrl: `${didKey.did}#${didKey.publicJwk.fingerprint}`,
						jwk: didKey.publicJwk
					};
				}
			});
			const jwsSignerKeys = jwsSigners.map((signer) => signer.jwk);
			if (!jwsSignerKeys.every((key) => key.is(Kms.Ed25519PublicJwk))) throw new DidExchangeProblemReportError("Expected DID Rotate signature to be signed with Ed25519 key.", { problemCode: DidExchangeProblemReportReason.ResponseNotAccepted });
			if (!isValid || !jwsSignerKeys.every((key) => invitationKeys?.some((invitationKey) => invitationKey.equals(key)))) throw new DidExchangeProblemReportError(`DID Rotate signature is invalid. isValid: ${isValid} signerKeys: ${JSON.stringify(jwsSignerKeys.map((key) => key.fingerprint))} invitationKeys:${JSON.stringify(invitationKeys?.map((key) => key.fingerprint))}`, { problemCode: DidExchangeProblemReportReason.ResponseNotAccepted });
		}
		try {
			return await agentContext.dependencyManager.resolve(DidsApi).resolveDidDocument(message.did);
		} catch (error) {
			throw new DidExchangeProblemReportError(error, { problemCode: message instanceof DidCommDidExchangeRequestMessage ? DidExchangeProblemReportReason.RequestNotAccepted : DidExchangeProblemReportReason.ResponseNotAccepted });
		}
	}
	/**
	* Extracts DID document as is from request or response message attachment and verifies its signature.
	*
	* @param message DID request or DID response message
	* @param invitationKeys array containing keys from connection invitation that could be used for signing of DID document
	* @returns verified DID document content from message attachment
	*/
	async extractAttachedDidDocument(agentContext, message, invitationKeys = []) {
		if (!message.didDoc) throw new DidExchangeProblemReportError("DID Document attachment is missing.", { problemCode: message instanceof DidCommDidExchangeRequestMessage ? DidExchangeProblemReportReason.RequestNotAccepted : DidExchangeProblemReportReason.ResponseNotAccepted });
		const didDocumentAttachment = message.didDoc;
		const jws = didDocumentAttachment.data.jws;
		if (!jws) throw new DidExchangeProblemReportError("DID Document signature is missing.", { problemCode: message instanceof DidCommDidExchangeRequestMessage ? DidExchangeProblemReportReason.RequestNotAccepted : DidExchangeProblemReportReason.ResponseNotAccepted });
		if (!didDocumentAttachment.data.base64) throw new CredoError("DID Document attachment is missing base64 property for signed did document.");
		const base64UrlPayload = base64ToBase64URL(didDocumentAttachment.data.base64);
		const { isValid, jwsSigners } = await this.jwsService.verifyJws(agentContext, {
			jws: {
				...jws,
				payload: base64UrlPayload
			},
			allowedJwsSignerMethods: ["did"],
			resolveJwsSigner: ({ jws: { header } }) => {
				if (typeof header.kid !== "string" || !isDid(header.kid, "key")) throw new CredoError("JWS header kid must be a did:key DID.");
				const didKey = DidKey.fromDid(header.kid);
				return {
					method: "did",
					didUrl: `${didKey.did}#${didKey.publicJwk.fingerprint}`,
					jwk: didKey.publicJwk
				};
			}
		});
		const json = JsonEncoder.fromBase64(didDocumentAttachment.data.base64);
		const didDocument = JsonTransformer.fromJSON(json, DidDocument);
		const didDocumentKeys = didDocument.authentication?.map((authentication) => {
			return getPublicJwkFromVerificationMethod(typeof authentication === "string" ? didDocument.dereferenceVerificationMethod(authentication) : authentication);
		}).concat(invitationKeys);
		this.logger.trace("JWS verification result", {
			isValid,
			jwsSigners
		});
		if (!isValid || !jwsSigners.every((jwsSigner) => didDocumentKeys?.some((key) => key.equals(jwsSigner.jwk)))) throw new DidExchangeProblemReportError("DID Document signature is invalid.", { problemCode: message instanceof DidCommDidExchangeRequestMessage ? DidExchangeProblemReportReason.RequestNotAccepted : DidExchangeProblemReportReason.ResponseNotAccepted });
		return didDocument;
	}
};
DidExchangeProtocol = __decorate([
	injectable(),
	__decorateParam(4, inject(InjectionSymbols.Logger)),
	__decorateMetadata("design:paramtypes", [
		typeof (_ref = typeof DidCommConnectionService !== "undefined" && DidCommConnectionService) === "function" ? _ref : Object,
		typeof (_ref2 = typeof DidRepository !== "undefined" && DidRepository) === "function" ? _ref2 : Object,
		typeof (_ref3 = typeof JwsService !== "undefined" && JwsService) === "function" ? _ref3 : Object,
		typeof (_ref4 = typeof DidCommDocumentService !== "undefined" && DidCommDocumentService) === "function" ? _ref4 : Object,
		Object
	])
], DidExchangeProtocol);

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