'use strict';

var jsonld = require('jsonld');
var jsonschema = require('jsonschema');
var vc_documentLoader = require('./document-loader.cjs');
var vc_constants = require('./constants.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');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var jsonld__default = /*#__PURE__*/_interopDefault(jsonld);

/**
 * The function uses `jsonschema` package to verify that the expanded `credential`'s subject `credentialSubject` has the JSON
 * schema `schema`
 * @param {object} credential - The credential to use, must be expanded JSON-LD
 * @param {object} schema - The schema to use
 * @param context
 * @param documentLoader
 * @returns {Promise<Boolean>} - Returns promise to a boolean or throws error
 */
async function validateCredentialSchema(
  credential,
  schema,
  context,
  documentLoader,
) {
  const requiresID = schema.required && schema.required.indexOf('id') > -1;
  const credentialSubject = credential[vc_constants.expandedSubjectProperty] || [];
  const subjects = credentialSubject.length
    ? credentialSubject
    : [credentialSubject];
  for (let i = 0; i < subjects.length; i++) {
    const subject = { ...subjects[i] };

    // eslint-disable-next-line
    const compacted = await jsonld__default.default.compact(subject, context, {
      documentLoader: documentLoader || vc_documentLoader(),
    });
    delete compacted[vc_constants.credentialContextField];

    // Check emptyness/JSON-LD errors before removing ID to avoid edge case of credential with ID field only
    if (Object.keys(compacted).length === 0) {
      throw new Error('Compacted subject is empty, likely invalid');
    }

    if (!requiresID) {
      // The id will not be part of schema. The spec mentioned that id will be popped off from subject
      delete compacted.id;
    }

    const schemaObj = schema.schema || schema;
    const subjectSchema = (schemaObj.properties && schemaObj.properties.credentialSubject)
      || schemaObj;

    jsonschema.validate(compacted, subjectSchema, {
      throwError: true,
    });
  }
  return true;
}

/**
 * Get schema and run validation on credential if it contains both a credentialSubject and credentialSchema
 * @param {object} credential - a verifiable credential JSON object
 * @param {object} context - the context
 * @param {object} documentLoader - the document loader
 * @returns {Promise<void>}
 */
// eslint-disable-next-line sonarjs/cognitive-complexity
async function getAndValidateSchemaIfPresent(
  credential,
  context,
  documentLoader,
) {
  const schemaList = credential[vc_constants.expandedSchemaProperty];
  if (schemaList) {
    const schema = schemaList[0];
    if (credential[vc_constants.expandedSubjectProperty] && schema) {
      const schemaUri = schema[vc_constants.credentialIDField];
      let schemaObj;

      const { document } = await documentLoader(schemaUri);
      if (Array.isArray(document) && document.length === 2) {
        const [author, data] = document;

        if (typeof data !== 'object' || data instanceof Uint8Array) {
          throw new Error('Incorrect schema format');
        }

        schemaObj = {
          ...data,
          id: schemaUri,
          author: String(author),
        };
      } else {
        schemaObj = document;
      }

      try {
        await validateCredentialSchema(
          credential,
          schemaObj,
          context,
          documentLoader,
        );
      } catch (e) {
        throw new Error(`Schema validation failed: ${e}`);
      }
    }
  }
}

exports.getAndValidateSchemaIfPresent = getAndValidateSchemaIfPresent;
exports.validateCredentialSchema = validateCredentialSchema;
//# sourceMappingURL=schema.cjs.map
