import { PEX, Status, PresentationSubmissionLocation } from '@sphereon/pex';
import { JSONPath } from '@astronautlabs/jsonpath';
import { v4 } from 'uuid';
import VerifiablePresentation from '../vc/verifiable-presentation.js';
import Presentation from '../vc/presentation.js';
import { applyEnforceBounds } from './pex-bounds.js';
import '../vc/presentations.js';
import 'jsonld';
import 'jsonld-signatures';
import '@docknetwork/crypto-wasm-ts';
import 'bs58';
import '@docknetwork/vc-delegation-engine';
import '../vc/credentials.js';
import '@digitalcredentials/vc-status-list';
import 'base64url';
import '../vc/CredentialIssuancePurpose.js';
import '../vc/helpers.js';
import '@transmute/json-web-signature';
import '../vc/document-loader.js';
import '../vc/contexts.js';
import '../vc/contexts/vc-examples-v1.js';
import '../vc/contexts/security_context.js';
import '../vc/contexts/status-list-21.js';
import '../vc/contexts/private-status-list-21.js';
import '../utils/json-fetch.js';
import '../vc/crypto/Bls12381BBDT16KeyPairDock2024.js';
import '../vc/crypto/constants.js';
import '../vc/crypto/common/DockCryptoKeyPair.js';
import '../utils/types/bytes.js';
import 'crypto';
import '../utils/interfaces.js';
import '../utils/assert.js';
import '../utils/error.js';
import '../utils/types/string.js';
import '../utils/types/iterable.js';
import '../utils/types/object.js';
import '../utils/inheritance.js';
import '../utils/types/set.js';
import '../vc/crypto/Bls12381BBDT16MACDock2024.js';
import '@docknetwork/crypto-wasm-ts/lib/index.js';
import '../vc/crypto/common/DockCryptoSignature.js';
import 'json-stringify-deterministic';
import 'semver/preload.js';
import '../vc/crypto/common/CustomLinkedDataSignature.js';
import '../types/did/document/verification-method-ref.js';
import '../types/generic/typed-enum.js';
import '../utils/types/array.js';
import 'multiformats/bases/base58';
import 'varint';
import '../utils/encoding/ss-58.js';
import 'blake2b';
import '../types/generic/with-base.js';
import '../types/generic/with-catch-null.js';
import '../types/generic/with-eq.js';
import '../types/generic/any-of.js';
import '../types/generic/with-from.js';
import '../types/generic/with-from-json-bytes.js';
import '../types/generic/typed-null.js';
import '../types/generic/create-placeholder.js';
import '../types/generic/typed-bytes.js';
import '../types/generic/typed-string.js';
import '../types/generic/typed-array.js';
import '../types/generic/typed-tuple.js';
import '../types/generic/typed-number.js';
import '../types/generic/typed-set.js';
import '../types/generic/typed-map.js';
import '../types/generic/typed-struct.js';
import 'js-sha256';
import '../types/generic/with-qualifier.js';
import '../types/generic/utils.js';
import '../types/generic/typed-bytes-array.js';
import '../types/did/onchain/typed-did/index.js';
import '../types/did/onchain/typed-did/did-method-key/did-method-key-public-key.js';
import '../utils/encoding/base-58-btc.js';
import '../types/generic/sized.js';
import '../utils/types/number.js';
import '../types/public-keys/public-key.js';
import '../types/public-keys/public-key-x25519-value.js';
import '../types/public-keys/public-key-ed25519-value.js';
import '../types/public-keys/public-key-secp256k1-value.js';
import '../types/public-keys/public-key-sr25519-value.js';
import '../types/did/onchain/constants.js';
import '../types/did/onchain/typed-did/signature.js';
import '../types/did/onchain/typed-did/did-method-key/did-method-key-signature.js';
import '../types/signatures/signature-value.js';
import '../types/signatures/signature-ed25519-value.js';
import '../types/signatures/signature-secp256k1-value.js';
import '../types/signatures/signature.js';
import '../types/signatures/signature-sr25519-value.js';
import '../types/did/onchain/typed-did/dock-did-value.js';
import '../types/generic/typed-uuid.js';
import '../types/did/onchain/typed-did/cheqd-did.js';
import '../vc/jws.js';
import '../vc/crypto/Bls12381BBDT16MACProofDock2024.js';
import '../vc/crypto/common/DockCryptoSignatureProof.js';
import '../vc/crypto/Bls12381BBSKeyPairDock2023.js';
import '../vc/crypto/Bls12381BBSSignatureDock2022.js';
import '../vc/crypto/Bls12381G2KeyPairDock2022.js';
import '../vc/crypto/Bls12381BBSSignatureDock2023.js';
import '../vc/crypto/Bls12381BBSSignatureProofDock2022.js';
import '../vc/crypto/Bls12381BBSSignatureProofDock2023.js';
import '../vc/crypto/Bls12381BDDT16KeyPairDock2024.js';
import '../vc/crypto/Bls12381BDDT16MACDock2024.js';
import '../vc/crypto/Bls12381BDDT16MACProofDock2024.js';
import '../vc/crypto/Bls12381PSKeyPairDock2023.js';
import '../vc/crypto/Bls12381PSSignatureDock2023.js';
import '../vc/crypto/Bls12381PSSignatureProofDock2023.js';
import '../vc/crypto/EcdsaSecp256k1Signature2019.js';
import '../vc/crypto/EcdsaSecp256k1VerificationKey2019.js';
import '@juanelas/base64';
import '../keypairs/keypair-secp256k1.js';
import 'elliptic';
import '../keypairs/dock-keypair.js';
import '../utils/encoding/multibase.js';
import '../vc/crypto/Ed25519Signature2018.js';
import '../vc/crypto/Ed25519VerificationKey2018.js';
import '../keypairs/keypair-ed25519.js';
import '@stablelib/ed25519';
import '../vc/crypto/Ed25519Signature2020.js';
import '../vc/crypto/Ed25519VerificationKey2020.js';
import '../vc/crypto/JsonWebSignature2020.js';
import '../keypairs/did-keypair.js';
import '../types/did/onchain/did-key.js';
import '../types/offchain-signatures/params/index.js';
import '../types/offchain-signatures/params/value.js';
import '../types/generic/option.js';
import '../types/offchain-signatures/curve-type.js';
import '../types/offchain-signatures/params/id.js';
import '../types/generic/with-from-dock-id.js';
import '../types/offchain-signatures/public-keys/index.js';
import '../types/generic/with-prop.js';
import '../types/generic/with-props.js';
import '../types/offchain-signatures/public-keys/value.js';
import '../types/offchain-signatures/public-keys/ref.js';
import '../types/offchain-signatures/public-keys/id.js';
import '../types/did/onchain/verification-relationship.js';
import '../types/did/document/verification-method-ref-or-ident-ref.js';
import '../types/did/document/ident-ref.js';
import '../vc/schema.js';
import 'jsonschema';
import '../vc/constants.js';
import '../vc/revocation.js';
import 'blakejs';
import '../utils/ident/ensure-ident.js';
import '../utils/ident/matchers.js';
import '../vc/verifiable-credential.js';

const DEFAULT_LIMIT_DISCLOSURE_SUITES = [
  'Bls12381BBS+SignatureDock2022',
  'Bls12381BBSSignatureDock2023',
  'Bls12381PSSignatureDock2023',
  'Bls12381BBDT16MACDock2024',
];

const SELECTIVE_DISCLOSURE_SIGNATURE_TYPES = new Set(DEFAULT_LIMIT_DISCLOSURE_SUITES);

const pex = new PEX();

const GeneratePresentationStatus = Object.freeze({
  SUCCESS: 'success',
  REQUIREMENTS_NOT_MET: 'requirements_not_met',
  SELECTIVE_DISCLOSURE_REQUIRED: 'selective_disclosure_required',
});

function ensurePresentationDefinition(pexRequest) {
  if (!pexRequest) {
    throw new Error('pexRequest is required');
  }

  if (pexRequest.presentationDefinition) {
    return pexRequest.presentationDefinition;
  }

  if (pexRequest.presentation_definition) {
    return pexRequest.presentation_definition;
  }

  return pexRequest;
}

function flattenAttributes(attributes) {
  if (!Array.isArray(attributes)) {
    return [];
  }
  return attributes.flatMap((attribute) => (Array.isArray(attribute) ? attribute : [attribute]));
}

function normalizeHolderDids({ explicitHolderDids = [], holderDid, holderKeyDoc }) {
  const dids = [...explicitHolderDids];
  const inferred = holderDid || holderKeyDoc?.controller;
  if (inferred && !dids.includes(inferred)) {
    dids.push(inferred);
  }
  return dids;
}

function requiresSelectiveDisclosure(presentationDefinition) {
  return presentationDefinition?.input_descriptors?.some(
    (descriptor) => descriptor?.constraints?.limit_disclosure === 'required',
  );
}

function dedupeAttributes(attributes = []) {
  return [...new Set(attributes)];
}

function isSelectiveDisclosureCredential(credential) {
  const proofType = credential?.proof?.type;
  return typeof proofType === 'string'
    && SELECTIVE_DISCLOSURE_SIGNATURE_TYPES.has(proofType);
}

function normalizeSelectiveDisclosureConfigs(configs = [], expectedLength) {
  if (!Array.isArray(configs)) {
    return Array(expectedLength).fill(undefined);
  }
  const normalized = [...configs];
  while (normalized.length < expectedLength) {
    normalized.push(undefined);
  }
  return normalized;
}

function normalizeAttributesConfig(attributes) {
  if (!attributes) {
    return undefined;
  }
  if (typeof attributes === 'string') {
    return [attributes];
  }
  if (Array.isArray(attributes?.[0])) {
    return flattenAttributes(attributes);
  }
  return Array.isArray(attributes) ? attributes : undefined;
}

function buildSelectiveDisclosureConfigs(selectiveDisclosure = {}, credentialCount) {
  if (!selectiveDisclosure) {
    return Array(credentialCount).fill(undefined);
  }

  let configs = selectiveDisclosure.credentials?.map((config) => {
    if (!config) {
      return undefined;
    }
    return {
      ...config,
      attributes: normalizeAttributesConfig(config.attributes),
    };
  });

  if (
    (!configs || configs.length === 0)
    && (
      selectiveDisclosure.attributes
      || selectiveDisclosure.witness
    )
  ) {
    configs = [{
      attributes: normalizeAttributesConfig(selectiveDisclosure.attributes),
      witness: selectiveDisclosure.witness,
    }];
  }

  return normalizeSelectiveDisclosureConfigs(configs, credentialCount);
}

function filterCredentialsByPexRequest({
  credentials = [],
  pexRequest,
  holderDids = [],
  holderDid,
  holderKeyDoc,
  limitDisclosureSignatureSuites = DEFAULT_LIMIT_DISCLOSURE_SUITES,
  restrictToFormats,
  restrictToDIDMethods,
} = {}) {
  const presentationDefinition = ensurePresentationDefinition(pexRequest);
  const normalizedDids = normalizeHolderDids({ explicitHolderDids: holderDids, holderDid, holderKeyDoc });

  return pex.selectFrom(presentationDefinition, credentials, {
    holderDIDs: normalizedDids.length ? normalizedDids : undefined,
    limitDisclosureSignatureSuites,
    restrictToFormats,
    restrictToDIDMethods,
  });
}

function evaluatePresentationAgainstDefinition({
  presentation,
  pexRequest,
  presentationSubmission,
  limitDisclosureSignatureSuites = DEFAULT_LIMIT_DISCLOSURE_SUITES,
  restrictToFormats,
  restrictToDIDMethods,
} = {}) {
  if (!presentation) {
    throw new Error('presentation is required');
  }

  const presentationDefinition = ensurePresentationDefinition(pexRequest);

  return pex.evaluatePresentation(presentationDefinition, presentation, {
    presentationSubmission,
    limitDisclosureSignatureSuites,
    restrictToFormats,
    restrictToDIDMethods,
  });
}

const attributesToSkip = [
  /^type/,
  /^issuer/,
  /^@context/,
  /^proof/,
  /^credentialSchema/,
  /^issuanceDate/,
];

const shouldSkipAttribute = (attributeName) => attributesToSkip.some((regex) => regex.test(attributeName));

function correctFieldPath(path) {
  return path.replace('$.', '');
}

function getAttributeName({ field, selectedCredentials, index }) {
  let attributeName;
  if (Array.isArray(field.path) && field.path.length > 1) {
    const selectedCredential = selectedCredentials[index];
    if (!selectedCredential) {
      return undefined;
    }

    for (let i = 0; i < field.path.length; i += 1) {
      const path = field.path[i];
      const paths = JSONPath.paths(selectedCredential, path);
      if (paths.length) {
        attributeName = correctFieldPath(JSONPath.stringify(paths[0]));
        break;
      }
    }
  } else {
    attributeName = correctFieldPath(Array.isArray(field.path) ? field.path[0] : field.path);
  }

  return attributeName;
}

function getPexRequiredAttributes(pexRequest, selectedCredentials = []) {
  const presentationDefinition = ensurePresentationDefinition(pexRequest);
  const descriptors = presentationDefinition.input_descriptors || [];
  return descriptors
    .map((inputDescriptor, index) => {
      if (!inputDescriptor?.constraints?.fields?.length) {
        return [];
      }

      return inputDescriptor.constraints.fields
        .filter((field) => {
          if (field.filter || field.optional) {
            return false;
          }

          try {
            const credential = selectedCredentials[index];
            if (!credential) {
              return false;
            }

            const paths = Array.isArray(field.path)
              ? field.path.flatMap((singlePath) => JSONPath.paths(credential, singlePath))
              : JSONPath.paths(credential, field.path);
            return paths.length !== 0;
          } catch (error) {
            console.error(`Error in field ${field.path}: ${error.message}`);
            return false;
          }
        })
        .map((field) => getAttributeName({ field, selectedCredentials, index }))
        .filter((attributeName) => attributeName && !shouldSkipAttribute(attributeName));
    });
}

const PEX_SUBMISSION_CONTEXT = {
  '@version': 1.1,
  presentation_submission: {
    '@id': 'https://identity.foundation/presentation-exchange/#presentation-submission',
    '@type': '@json',
  },
};

/**
 * Generates a (potentially selective-disclosure) presentation that satisfies a Presentation Exchange definition.
 *
 * @param {object} params - Options for presentation derivation.
 * @param {Array<object|string>} params.credentials - The verifiable credentials (JSON objects or serialized JSON strings) to evaluate.
 * @param {object} params.pexRequest - Presentation Definition or a wrapper that contains one (`presentationDefinition` or `presentation_definition`).
 * @param {object} [params.holderKeyDoc] - Holder key material used to sign the presentation when `skipSigning` is false.
 * @param {string} [params.holderDid] - DID that should be set as the presentation holder (falls back to `holderKeyDoc.controller`).
 * @param {Array<string>} [params.holderDids] - Explicit holder DID list passed through to PEX selection.
 * @param {string} [params.challenge] - Challenge used when signing or verifying derived presentations.
 * @param {string} [params.domain] - Domain used when signing selective disclosure presentations.
 * @param {string} [params.presentationId] - Optional identifier for the generated presentation.
 * @param {PresentationSubmissionLocation} [params.presentationSubmissionLocation=PresentationSubmissionLocation.PRESENTATION] - Location for the presentation submission descriptor.
 * @param {object} [params.resolver] - DID resolver used by the Presentation builder and range-proof witness lookups.
 * @param {boolean} [params.compactProof=true] - Whether holder signatures should omit canonical proof fields.
 * @param {Array<string>} [params.limitDisclosureSignatureSuites=DEFAULT_LIMIT_DISCLOSURE_SUITES] - Signature suites that are considered selective-disclosure capable by PEX.
 * @param {Array<string>} [params.restrictToFormats] - Optional PEX filter restricting acceptable VC proof formats.
 * @param {Array<string>} [params.restrictToDIDMethods] - Optional PEX filter restricting acceptable DID methods.
 * @param {boolean} [params.skipSigning=false] - When true, the generated presentation is returned unsigned (useful for SD proofs or tests).
 * @param {Function} [params.loadProvingKey] - Async loader invoked with `{ credentialIndex, presentationDefinition }` and returning `{ provingKey, provingKeyId }` for bound proofs.
 * @param {object} [params.selectiveDisclosure] - Configuration for attribute revelation (global `attributes`), per-credential configs (`credentials[index]`), accumulator witnesses, and presentation builder options.
 * @returns {Promise<object>} Result object containing status, presentation (or derived credentials) and any warnings/errors from PEX.
 */
/* eslint-disable-next-line sonarjs/cognitive-complexity */
async function generatePresentationFromPexRequest({
  credentials = [],
  pexRequest,
  holderKeyDoc,
  holderDid,
  holderDids = [],
  challenge,
  domain,
  presentationId,
  presentationSubmissionLocation = PresentationSubmissionLocation.PRESENTATION,
  resolver,
  compactProof = true,
  limitDisclosureSignatureSuites = DEFAULT_LIMIT_DISCLOSURE_SUITES,
  restrictToFormats,
  restrictToDIDMethods,
  skipSigning = false,
  loadProvingKey,
  selectiveDisclosure,
} = {}) {
  const presentationDefinition = ensurePresentationDefinition(pexRequest);
  const selectResults = filterCredentialsByPexRequest({
    credentials,
    pexRequest: presentationDefinition,
    holderDids,
    holderDid,
    holderKeyDoc,
    limitDisclosureSignatureSuites,
    restrictToFormats,
    restrictToDIDMethods,
  });
  const selectedCredentials = selectResults.verifiableCredential || [];

  if (
    selectResults.areRequiredCredentialsPresent === Status.ERROR
    || selectedCredentials.length === 0
  ) {
    return {
      status: GeneratePresentationStatus.REQUIREMENTS_NOT_MET,
      error: new Error('Provided credentials do not satisfy the presentation definition'),
      details: selectResults,
    };
  }

  const pexRequiredAttributes = getPexRequiredAttributes(
    presentationDefinition,
    selectedCredentials,
  );
  const selectiveDisclosureRequired = requiresSelectiveDisclosure(presentationDefinition);
  const selectiveConfigs = buildSelectiveDisclosureConfigs(
    selectiveDisclosure,
    selectedCredentials.length,
  );

  if (selectiveDisclosureRequired) {
    const missingAttributes = pexRequiredAttributes.some((required, index) => {
      if (!required.length) {
        return false;
      }
      const provided = new Set(selectiveConfigs[index]?.attributes || []);
      return required.some((attribute) => !provided.has(attribute));
    });
    if (missingAttributes) {
      return {
        status: GeneratePresentationStatus.SELECTIVE_DISCLOSURE_REQUIRED,
        error: new Error('Presentation definition requires selective disclosure'),
        details: selectResults,
        selectiveDisclosure: {
          requiredAttributes: pexRequiredAttributes,
        },
      };
    }
  }

  const needsSelectiveDisclosure = selectiveConfigs.some((config, index) => {
    const required = pexRequiredAttributes[index] || [];
    const provided = config?.attributes || [];
    return required.length > 0
      || provided.length > 0
      || config?.witness;
  }) || Boolean(loadProvingKey);

  const boundOptions = {
    loadProvingKey,
    presentationOptions: selectiveDisclosure?.presentationOptions,
  };

  const shouldRunSelectiveFlow = needsSelectiveDisclosure || Boolean(loadProvingKey);

  let updatedCredentials = selectedCredentials;

  if (shouldRunSelectiveFlow) {
    updatedCredentials = await deriveSelectiveDisclosureCredentials({
      selectedCredentials,
      resolver,
      presentationDefinition,
      pexRequiredAttributes,
      selectiveConfigs,
      boundOptions,
    });
  }

  if (!skipSigning && !holderKeyDoc) {
    throw new Error('holderKeyDoc is required when skipSigning is false');
  }

  if (!skipSigning && !challenge) {
    throw new Error('challenge is required when skipSigning is false');
  }

  const presentationSubmission = pex.presentationSubmissionFrom(
    presentationDefinition,
    updatedCredentials,
    {
      presentationSubmissionLocation,
    },
  );

  const vpId = presentationId || `urn:uuid:${v4()}`;
  const vp = new VerifiablePresentation(vpId);
  updatedCredentials.forEach((credential) => {
    vp.addCredential(credential);
  });
  const normalizedHolder = holderDid || holderKeyDoc?.controller;
  if (normalizedHolder) {
    vp.setHolder(normalizedHolder);
  }
  if (presentationSubmissionLocation === PresentationSubmissionLocation.PRESENTATION) {
    vp.addContext(PEX_SUBMISSION_CONTEXT);
    vp.presentation_submission = presentationSubmission;
  }

  if (skipSigning) {
    return {
      status: GeneratePresentationStatus.SUCCESS,
      presentation: vp.toJSON(),
      presentationSubmission,
      warnings: selectResults.warnings || [],
      matches: selectResults.matches || [],
    };
  }

  const signedPresentation = await vp.sign(
    holderKeyDoc,
    challenge,
    domain,
    resolver,
    compactProof,
  );

  return {
    status: GeneratePresentationStatus.SUCCESS,
    presentation: signedPresentation.toJSON(),
    presentationSubmission,
    warnings: selectResults.warnings || [],
    matches: selectResults.matches || [],
  };
}

/* eslint-disable-next-line sonarjs/cognitive-complexity */
async function deriveSelectiveDisclosureCredentials({
  selectedCredentials,
  resolver,
  presentationDefinition,
  pexRequiredAttributes,
  selectiveConfigs,
  boundOptions = {},
}) {
  const globalBoundsEnabled = Boolean(boundOptions.loadProvingKey);
  const derivationPlan = selectedCredentials.map((credential, index) => {
    const config = selectiveConfigs[index] || {};
    const requiredAttributes = pexRequiredAttributes[index] || [];
    const customAttributes = config.attributes || [];
    const attributesToReveal = dedupeAttributes([
      ...customAttributes,
      ...requiredAttributes,
    ]);
    const shouldDerive = globalBoundsEnabled
      || attributesToReveal.length > 0
      || !!config.witness;
    return {
      credential,
      attributesToReveal,
      witness: config.witness,
      shouldDerive,
    };
  });

  if (!derivationPlan.some((item) => item.shouldDerive)) {
    return selectedCredentials;
  }

  derivationPlan.forEach((plan, index) => {
    if (plan.shouldDerive && !isSelectiveDisclosureCredential(plan.credential)) {
      throw new Error(`Selective disclosure is not supported for credential at index ${index}`);
    }
  });

  const presentation = new Presentation();
  const sourceToBuilderMap = [];

  for (let index = 0; index < derivationPlan.length; index += 1) {
    const plan = derivationPlan[index];
    if (plan.shouldDerive) {
      const { credential } = plan;
      const clonedCredential = typeof credential === 'string'
        ? JSON.parse(credential)
        : JSON.parse(JSON.stringify(credential));
      // eslint-disable-next-line no-await-in-loop
      const builderIndex = await presentation.addCredentialToPresent(clonedCredential, {
        resolver,
      });
      sourceToBuilderMap.push({ sourceIndex: index, builderIndex });
    }
  }

  const descriptorBoundsMap = {};

  for (const { sourceIndex, builderIndex } of sourceToBuilderMap) {
    const plan = derivationPlan[sourceIndex];
    if (globalBoundsEnabled) {
      // eslint-disable-next-line no-await-in-loop
      const proofConfig = await resolveProvingKeyConfig({
        loadProvingKey: boundOptions.loadProvingKey,
        credentialIndex: sourceIndex,
        presentationDefinition,
      });
      if (proofConfig?.provingKey) {
        const scopedBounds = applyEnforceBounds({
          builder: presentation.presBuilder,
          presentationDefinition,
          provingKeyId: proofConfig.provingKeyId || 'key0',
          provingKey: proofConfig.provingKey,
          selectedCredentials: [plan.credential],
          credentialIdx: builderIndex,
        });
        if (scopedBounds[builderIndex]) {
          descriptorBoundsMap[sourceIndex] = scopedBounds[builderIndex];
        }
      }
    }
    const boundsForCredential = descriptorBoundsMap[sourceIndex] || [];
    const boundAttributes = boundsForCredential.map((bound) => bound.attributeName);
    const filteredAttributes = (plan.attributesToReveal || []).filter(
      (attribute) => attribute
        && !boundAttributes.includes(attribute)
        && !shouldSkipAttribute(attribute),
    );
    const requiredAttributes = pexRequiredAttributes[sourceIndex] || [];
    requiredAttributes.forEach((attribute) => {
      if (attribute && !filteredAttributes.includes(attribute)) {
        filteredAttributes.push(attribute);
      }
    });

    if (filteredAttributes.length) {
      presentation.addAttributeToReveal(builderIndex, filteredAttributes);
    }
    if (plan.witness) {
      const {
        membershipWitness,
        accumulated,
        pk,
        params,
      } = plan.witness;
      presentation.presBuilder.addAccumInfoForCredStatus(
        builderIndex,
        membershipWitness,
        accumulated,
        pk,
        params,
      );
    }
  }

  const derivedCredentials = await presentation.deriveCredentials(
    boundOptions.presentationOptions,
  );

  const updatedCredentials = [...selectedCredentials];
  let derivedIndex = 0;
  sourceToBuilderMap.forEach(({ sourceIndex }) => {
    updatedCredentials[sourceIndex] = derivedCredentials[derivedIndex];
    derivedIndex += 1;
  });

  return updatedCredentials;
}

async function resolveProvingKeyConfig({
  loadProvingKey,
  credentialIndex,
  presentationDefinition,
}) {
  if (typeof loadProvingKey !== 'function') {
    return null;
  }
  const loaded = await loadProvingKey({
    credentialIndex,
    presentationDefinition,
  });
  if (!loaded?.provingKey) {
    return null;
  }
  return {
    provingKey: loaded.provingKey,
    provingKeyId: loaded.provingKeyId || 'key0',
  };
}

export { GeneratePresentationStatus, evaluatePresentationAgainstDefinition, filterCredentialsByPexRequest, generatePresentationFromPexRequest, getPexRequiredAttributes, shouldSkipAttribute };
//# sourceMappingURL=pex-utils.js.map
