'use strict';

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

/**
 * @typedef {object} VerifiableCredentialVerificationResult The credential verification result
 * @property {Boolean} verified Is this credential verified or not
 * @property {array} results Verification results
 * @property {any} [error] Optional error
 */

/**
 * Representation of a Verifiable Credential.
 */
class VerifiableCredential {
  /**
   * Create a new Verifiable Credential instance.
   * @param {string} id - id of the credential
   */
  constructor(id) {
    if (id) {
      this.setId(id);
    }

    this.context = [vc_constants.DEFAULT_CONTEXT];
    this.type = [vc_constants.DEFAULT_TYPE];
    this.credentialSubject = [];
    this.setIssuanceDate(new Date().toISOString());
  }

  /**
   * Sets the credential's ID
   * @param {string} id - Signed credential's ID
   * @returns {VerifiableCredential}
   */
  setId(id) {
    this.constructor.verifyID(id);
    this.id = id;
    return this;
  }

  /**
   * Fail if the given verifiable credential id isn't a valid URI.
   * @param {*} id
   */
  static verifyID(id) {
    utils_ident_ensureIdent.ensureURI(id);
  }

  /**
   * Sets the credential's issuer DID
   * @param {string} issuer - the issuer's did
   * @returns {VerifiableCredential}
   */
  setIssuer(issuer) {
    this.issuer = issuer;
    return this;
  }

  /**
   * Sets the credential's proof
   * @param {object} proof - Signed credential proof
   * @returns {VerifiableCredential}
   */
  setProof(proof) {
    this.proof = proof;
    return this;
  }

  /**
   * Sets the `credentialSchema` field of the credential with the given id and type as specified in the RFC.
   * @param {string} id - schema ID URI
   * @param {string} type - type of the credential schema
   */
  setSchema(id, type) {
    this.constructor.verifyID(id);
    this.credentialSchema = {
      id,
      type,
    };
  }

  /**
   * Check that the credential is compliant with given JSON schema, meaning `credentialSubject` has the
   * structure specified by the given JSON schema. Use `validateCredentialSchema` but exclude subject's id.
   * Allows issuer to validate schema before adding it.
   * @param {object} schema - The schema to validate with
   * @returns {Promise<Boolean>}
   */
  async validateSchema(schema) {
    if (!this.credentialSubject) {
      throw new Error('No credential subject defined');
    }

    const expanded = await vc_helpers.expandJSONLD(this.toJSON());
    return vc_schema.validateCredentialSchema(expanded, schema, this.context);
  }

  /**
   * Sets the context to the given value, overrding all others
   * @param {string|object} context - Context to assign
   * @returns {VerifiableCredential}
   */
  setContext(context) {
    if (!utils_types_object.isObject(context) && !Array.isArray(context)) {
      this.constructor.verifyID(context);
    }
    this.context = context;
    return this;
  }

  /**
   * Add a context to this Credential's context array. Duplicates are omitted.
   * @param {string|object} context - Context to add to the credential context array
   * @returns {VerifiableCredential}
   */
  addContext(context) {
    if (!utils_types_object.isObject(context)) {
      this.constructor.verifyID(context);
    }
    this.context = utils_types_array.getUniqueElementsFromArray(
      [...this.context, context],
      JSON.stringify,
    );
    return this;
  }

  /**
   * Add a type to this Credential's type array. Duplicates are omitted.
   * @param {string} type - Type to add to the credential type array
   * @returns {VerifiableCredential}
   */
  addType(type) {
    utils_types_string.ensureString(type);
    this.type = [...new Set([...this.type, type])];
    return this;
  }

  /**
   * Add a subject to this Credential. Duplicates are omitted.
   * @param {object} subject -  Subject of the credential
   * @returns {VerifiableCredential}
   */
  addSubject(subject) {
    if (!this.credentialSubject || this.credentialSubject.length === 0) {
      this.credentialSubject = [subject];
    }

    const subjects = this.credentialSubject.length
      ? this.credentialSubject
      : [this.credentialSubject];
    this.credentialSubject = utils_types_array.getUniqueElementsFromArray(
      [...subjects, subject],
      JSON.stringify,
    );
    return this;
  }

  /**
   * Set the subject for this Credential
   * @param {object|array} subject - Subject of the credential as object or array
   * @returns {VerifiableCredential}
   */
  setSubject(subject) {
    if (!utils_types_object.isObject(subject) && !Array.isArray(subject)) {
      throw new Error('credentialSubject must be either an object or array');
    }
    this.credentialSubject = subject;
    return this;
  }

  /**
   * Set a status for this Credential
   * @param {object} status -  Status of the credential
   * @returns {VerifiableCredential}
   */
  setStatus(status) {
    utils_types_object.ensureObjectWithId(status, 'credentialStatus');
    if (!status.type) {
      throw new Error('"credentialStatus" must include a type.');
    }
    this.status = status;
    return this;
  }

  /**
   * Set a issuance date for this Credential
   * @param {string} issuanceDate -  issuanceDate of the credential
   * @returns {VerifiableCredential}
   */
  setIssuanceDate(issuanceDate) {
    utils_ident_ensureIdent.ensureValidDatetime(issuanceDate);
    this.issuanceDate = issuanceDate;
    return this;
  }

  /**
   * Set a expiration date for this Credential
   * @param {object} expirationDate -  expirationDate of the credential
   * @returns {VerifiableCredential}
   */
  setExpirationDate(expirationDate) {
    utils_ident_ensureIdent.ensureValidDatetime(expirationDate);
    this.expirationDate = expirationDate;
    return this;
  }

  /**
   * Define the JSON representation of a Verifiable Credential.
   * @returns {object}
   */
  toJSON() {
    const { context, status, ...rest } = this;
    const credJson = {
      '@context': context,
    };
    if (status) {
      credJson.credentialStatus = status;
    }
    return {
      ...credJson,
      ...rest,
    };
  }

  /**
   * Sign a Verifiable Credential using the provided keyDoc
   * @param {object} keyDoc - key document containing `id`, `controller`, `type`, `privateKeyBase58` and `publicKeyBase58`
   * @param {Boolean} [compactProof] - Whether to compact the JSON-LD or not.
   * @param {object} [issuerObject] - Optional issuer object to assign
   * @param {Boolean} [addSuiteContext] - Toggles the default
   *   behavior of each signature suite enforcing the presence of its own
   *   `@context` (if it is not present, it's added to the context list).
   * @param {(jsonld|jwt|proofValue)} [type] - Optional format/type of the credential (JSON-LD, JWT, proofValue)
   * @returns {Promise<VerifiableCredential>}
   */
  async sign(
    keyDoc,
    compactProof = true,
    issuerObject = null,
    addSuiteContext = true,
    documentLoader = null,
    type = null,
  ) {
    const signedVC = await vc_credentials.issueCredential(
      keyDoc,
      this.toJSON(),
      compactProof,
      documentLoader,
      null,
      void 0,
      issuerObject,
      addSuiteContext,
      type,
    );
    this.setProof(signedVC.proof);
    this.issuer = signedVC.issuer;
    this.context = signedVC['@context'];
    return this;
  }

  /**
   * Verify a Verifiable Credential
   * @param {object} [params] Verify parameters (TODO: add type info for this object)
   * @returns {Promise<VerifiableCredentialVerificationResult>}
   */
  async verify({
    resolver = null,
    compactProof = true,
    skipRevocationCheck = false,
    skipSchemaCheck = false,
    suite = [],
  } = {}) {
    if (!this.proof) {
      throw new Error('The current Verifiable Credential has no proof.');
    }

    return vc_credentials.verifyCredential(this.toJSON(), {
      resolver,
      compactProof,
      skipRevocationCheck,
      skipSchemaCheck,
      suite,
    });
  }

  /**
   * Sets this credential's properties based on a JSON object
   * @param {object} json - VC JSON
   * @returns {VerifiableCredential}
   */
  setFromJSON(json) {
    const subject = json.credentialSubject || json.subject;
    if (subject) {
      const subjects = subject.length ? subject : [subject];
      subjects.forEach((value) => {
        this.addSubject(value);
      });
    }

    if (json.proof) {
      this.setProof(json.proof);
    }

    if (json.issuer) {
      this.setIssuer(json.issuer);
    }

    const status = json.credentialStatus || json.status;
    if (status) {
      this.setStatus(status);
    }

    if (json.issuanceDate) {
      this.setIssuanceDate(json.issuanceDate);
    }

    if (json.expirationDate) {
      this.setExpirationDate(json.expirationDate);
    }

    Object.assign(this, json);
    return this;
  }

  /**
   * Creates a new VerifiableCredential instance from a JSON object
   * @param {object} json - VC JSON
   * @returns {VerifiableCredential}
   */
  static fromJSON(json) {
    const cert = new this(json.id);
    const contexts = json['@context'];
    if (contexts) {
      cert.setContext(contexts);
    } else {
      throw new Error(
        'No context found in JSON object, verifiable credentials must have a @context field.',
      );
    }

    const types = json.type;
    if (types) {
      cert.type = [];
      if (types.length !== undefined) {
        types.forEach((typeVal) => {
          cert.addType(typeVal);
        });
      } else {
        cert.addType(types);
      }
    } else {
      throw new Error(
        'No type found in JSON object, verifiable credentials must have a type field.',
      );
    }

    return cert.setFromJSON(json);
  }
}

module.exports = VerifiableCredential;
//# sourceMappingURL=verifiable-credential.cjs.map
