

import { CredoError } from "../../../error/CredoError.mjs";
import "../../../error/index.mjs";
import { Hasher } from "../../../crypto/hashes/Hasher.mjs";
import { ClaimFormat } from "../../vc/models/ClaimFormat.mjs";
import "../../../crypto/index.mjs";
import { W3cCredentialRecord } from "../../vc/repository/W3cCredentialRecord.mjs";
import "../../vc/index.mjs";
import { Mdoc } from "../../mdoc/Mdoc.mjs";
import { MdocDeviceResponse } from "../../mdoc/MdocDeviceResponse.mjs";
import { MdocRecord } from "../../mdoc/repository/MdocRecord.mjs";
import "../../mdoc/index.mjs";
import { SdJwtVcRecord } from "../../sd-jwt-vc/repository/SdJwtVcRecord.mjs";
import "../../sd-jwt-vc/index.mjs";
import { DifPresentationExchangeError } from "../DifPresentationExchangeError.mjs";
import { getSphereonOriginalVerifiableCredential } from "./transform.mjs";
import { decodeSdJwtSync, getClaimsSync } from "@sd-jwt/decode";
import { Status } from "@animo-id/pex";
import { SubmissionRequirementMatchType } from "@animo-id/pex/dist/main/lib/evaluation/core/index.js";
import { JSONPath } from "@astronautlabs/jsonpath";
import { Rules } from "@sphereon/pex-models";

//#region src/modules/dif-presentation-exchange/utils/credentialSelection.ts
async function getCredentialsForRequest(pex, presentationDefinition, credentialRecords) {
	const encodedCredentials = credentialRecords.map(getSphereonOriginalVerifiableCredential);
	const selectResultsRaw = pex.selectFrom(presentationDefinition, encodedCredentials);
	const selectResults = {
		...selectResultsRaw,
		matches: selectResultsRaw.matches ?? [],
		verifiableCredential: selectResultsRaw.verifiableCredential?.map((selectedEncoded, index) => {
			const credentialRecordIndex = selectResultsRaw.vcIndexes?.[index];
			if (credentialRecordIndex === void 0 || credentialRecordIndex === -1) throw new DifPresentationExchangeError("Unable to find credential in credential records.");
			const credentialRecord = credentialRecords[credentialRecordIndex];
			if (credentialRecord instanceof SdJwtVcRecord) {
				const { jwt, disclosures } = decodeSdJwtSync(selectedEncoded, Hasher.hash);
				const prettyClaims = getClaimsSync(jwt.payload, disclosures, Hasher.hash);
				return {
					claimFormat: ClaimFormat.SdJwtDc,
					credentialRecord,
					disclosedPayload: prettyClaims
				};
			}
			if (credentialRecord instanceof MdocRecord) return {
				claimFormat: ClaimFormat.MsoMdoc,
				credentialRecord,
				disclosedPayload: {}
			};
			if (credentialRecord instanceof W3cCredentialRecord) return {
				claimFormat: credentialRecord.credential.claimFormat,
				credentialRecord
			};
			throw new CredoError("Unrecognized credential record type");
		}) ?? []
	};
	const presentationSubmission = {
		requirements: [],
		areRequirementsSatisfied: false,
		name: presentationDefinition.name,
		purpose: presentationDefinition.purpose
	};
	if (!presentationDefinition.submission_requirements || presentationDefinition.submission_requirements.length === 0) presentationSubmission.requirements = getSubmissionRequirementsForAllInputDescriptors(presentationDefinition.input_descriptors, selectResults);
	else presentationSubmission.requirements = getSubmissionRequirements(presentationDefinition, selectResults);
	const allEntries = presentationSubmission.requirements.flatMap((requirement) => requirement.submissionEntry);
	const inputDescriptorsForMdocCredential = /* @__PURE__ */ new Map();
	for (const entry of allEntries) for (const verifiableCredential of entry.verifiableCredentials) {
		if (verifiableCredential.claimFormat !== ClaimFormat.MsoMdoc) continue;
		const set = inputDescriptorsForMdocCredential.get(verifiableCredential) ?? /* @__PURE__ */ new Set();
		set.add(entry.inputDescriptorId);
		inputDescriptorsForMdocCredential.set(verifiableCredential, set);
	}
	for (const [verifiableCredential, inputDescriptorIds] of inputDescriptorsForMdocCredential.entries()) {
		if (verifiableCredential.claimFormat !== ClaimFormat.MsoMdoc) continue;
		const inputDescriptorsForCredential = presentationDefinition.input_descriptors.filter(({ id }) => inputDescriptorIds.has(id));
		const mdoc = Mdoc.fromBase64Url(verifiableCredential.credentialRecord.base64Url);
		verifiableCredential.disclosedPayload = MdocDeviceResponse.limitDisclosureToInputDescriptor({
			inputDescriptor: {
				id: mdoc.docType,
				format: { mso_mdoc: { alg: [] } },
				constraints: {
					limit_disclosure: "required",
					fields: inputDescriptorsForCredential.flatMap((i) => i.constraints?.fields ?? [])
				}
			},
			mdoc: Mdoc.fromBase64Url(verifiableCredential.credentialRecord.base64Url)
		});
	}
	if (presentationSubmission.requirements.length === 0) throw new DifPresentationExchangeError("Presentation Definition does not require any credentials. Optional credentials are not included in the presentation submission.");
	if (selectResults.areRequiredCredentialsPresent === Status.ERROR) return presentationSubmission;
	return {
		...presentationSubmission,
		areRequirementsSatisfied: presentationSubmission.requirements.every((requirement) => requirement.isRequirementSatisfied)
	};
}
function getSubmissionRequirements(presentationDefinition, selectResults) {
	const submissionRequirements = [];
	const matches = selectResults.matches;
	if (!matches.every((match) => match.type === SubmissionRequirementMatchType.SubmissionRequirement && match.from)) throw new DifPresentationExchangeError(`Expected all matches to be of type '${SubmissionRequirementMatchType.SubmissionRequirement}' with 'from' key.`);
	presentationDefinition.submission_requirements?.forEach((submissionRequirement, submissionRequirementIndex) => {
		if (submissionRequirement.from_nested) throw new DifPresentationExchangeError("Presentation definition contains requirement using 'from_nested', which is not supported yet.");
		if (!submissionRequirement.from) throw new DifPresentationExchangeError("Missing 'from' in submission requirement match");
		const match = matches.find((match$1) => match$1.id === submissionRequirementIndex);
		if (!match) throw new Error(`Unable to find a match for submission requirement with index '${submissionRequirementIndex}'`);
		if (submissionRequirement.rule === Rules.All) {
			const selectedSubmission = getSubmissionRequirementRuleAll(submissionRequirement, presentationDefinition, selectResults.verifiableCredential, match);
			submissionRequirements.push(selectedSubmission);
		} else {
			const selectedSubmission = getSubmissionRequirementRulePick(submissionRequirement, presentationDefinition, selectResults.verifiableCredential, match);
			submissionRequirements.push(selectedSubmission);
		}
	});
	return submissionRequirements.filter((requirement) => requirement.needsCount > 0);
}
function getSubmissionRequirementsForAllInputDescriptors(inputDescriptors, selectResults) {
	const submissionRequirements = [];
	const matches = selectResults.matches;
	if (!matches.every((match) => match.type === SubmissionRequirementMatchType.InputDescriptor)) throw new DifPresentationExchangeError(`Expected all matches to be of type '${SubmissionRequirementMatchType.InputDescriptor}' when.`);
	for (const inputDescriptor of inputDescriptors) {
		const submission = getSubmissionForInputDescriptor(inputDescriptor, selectResults.verifiableCredential, matches);
		submissionRequirements.push({
			rule: Rules.Pick,
			needsCount: 1,
			submissionEntry: [submission],
			isRequirementSatisfied: submission.verifiableCredentials.length >= 1
		});
	}
	return submissionRequirements;
}
function getSubmissionRequirementRuleAll(submissionRequirement, presentationDefinition, verifiableCredentials, match) {
	if (!submissionRequirement.from) throw new DifPresentationExchangeError("Missing 'from' in submission requirement match.");
	const selectedSubmission = {
		rule: Rules.All,
		needsCount: 0,
		name: submissionRequirement.name,
		purpose: submissionRequirement.purpose,
		submissionEntry: [],
		isRequirementSatisfied: false
	};
	for (const inputDescriptor of presentationDefinition.input_descriptors) {
		if (!inputDescriptor.group?.includes(match.from)) continue;
		const submission = getSubmissionForInputDescriptor(inputDescriptor, verifiableCredentials, match.input_descriptors);
		selectedSubmission.needsCount += 1;
		selectedSubmission.submissionEntry.push(submission);
	}
	return {
		...selectedSubmission,
		isRequirementSatisfied: selectedSubmission.submissionEntry.every((submission) => submission.verifiableCredentials.length >= 1)
	};
}
function getSubmissionRequirementRulePick(submissionRequirement, presentationDefinition, verifiableCredentials, match) {
	if (!submissionRequirement.from) throw new DifPresentationExchangeError("Missing 'from' in submission requirement match.");
	const selectedSubmission = {
		rule: Rules.Pick,
		needsCount: submissionRequirement.count ?? submissionRequirement.min ?? 1,
		name: submissionRequirement.name,
		purpose: submissionRequirement.purpose,
		submissionEntry: [],
		isRequirementSatisfied: false
	};
	const satisfiedSubmissions = [];
	const unsatisfiedSubmissions = [];
	for (const inputDescriptor of presentationDefinition.input_descriptors) {
		if (!inputDescriptor.group?.includes(match.from)) continue;
		const submission = getSubmissionForInputDescriptor(inputDescriptor, verifiableCredentials, match.input_descriptors);
		if (submission.verifiableCredentials.length >= 1) satisfiedSubmissions.push(submission);
		else unsatisfiedSubmissions.push(submission);
	}
	return {
		...selectedSubmission,
		isRequirementSatisfied: satisfiedSubmissions.length >= selectedSubmission.needsCount,
		submissionEntry: satisfiedSubmissions.length >= selectedSubmission.needsCount ? satisfiedSubmissions : [...satisfiedSubmissions, ...unsatisfiedSubmissions]
	};
}
function getSubmissionForInputDescriptor(inputDescriptor, verifiableCredentials, matches) {
	const matchesForInputDescriptor = matches.filter((m) => m.id === inputDescriptor.id);
	const submissionEntry = {
		inputDescriptorId: inputDescriptor.id,
		name: inputDescriptor.name,
		purpose: inputDescriptor.purpose,
		verifiableCredentials: matchesForInputDescriptor.flatMap((matchForInputDescriptor) => extractCredentialsFromInputDescriptorMatch(matchForInputDescriptor, verifiableCredentials))
	};
	if (!matchesForInputDescriptor?.length) return submissionEntry;
	return submissionEntry;
}
function extractCredentialsFromInputDescriptorMatch(match, availableCredentials) {
	const verifiableCredentials = [];
	for (const vcPath of match.vc_path) {
		const [verifiableCredential] = JSONPath.query({ verifiableCredential: availableCredentials }, vcPath);
		verifiableCredentials.push(verifiableCredential);
	}
	return verifiableCredentials;
}

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