'use strict';

var blakejs = require('blakejs');
var jsonld = require('jsonld');
var cryptoWasmTs = require('@docknetwork/crypto-wasm-ts');
var utils_types_bytes = require('../utils/types/bytes.cjs');
var vc_constants = require('./constants.cjs');
require('crypto');
require('../utils/interfaces.cjs');
require('../utils/assert.cjs');
require('../utils/error.cjs');
require('../utils/types/string.cjs');
require('../utils/types/iterable.cjs');
require('../utils/types/object.cjs');
require('../utils/inheritance.cjs');
require('../utils/types/set.cjs');

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

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

// The revocation registry has id with the byte size `RevRegIdByteSize`
const RevRegIdByteSize = 32;
// Each entry in revocation registry has byte size `RevEntryByteSize`
const RevEntryByteSize = 32;

const LD_SEC_TERM = 'https://ld.truvera.io/security#';
const LD_SEC_TERM_LEGACY = 'https://ld.dock.io/security#';

/**
 * Return `credentialStatus` according to W3C spec when the revocation status is checked on Dock
 * @param registryId - Revocation registry id
 * @returns {{id: string, type: string}}
 */
function buildDockCredentialStatus(registryId) {
  return { id: `${vc_constants.DockRevRegQualifier}${registryId}`, type: vc_constants.RevRegType };
}

/**
 * Generate the revocation id of a credential usable by Dock. It hashes the credential id to get the
 * revocation id
 * @param credential
 * @returns {*}
 */
function getDockRevIdFromCredential(credential) {
  // The hash outputs the same number of bytes as required by Dock
  return utils_types_bytes.u8aToHex(blakejs.blake2b(credential[vc_constants.credentialIDField], RevEntryByteSize * 8));
}

/**
 * Generate a random revocation registry id.
 * @returns {string} The id as a hex string
 */
function createRandomRegistryId() {
  return utils_types_bytes.randomAsHex(RevRegIdByteSize);
}

/**
 * Retrieves a value under the `credentialStatus` property and ensures it has the expected properties.
 * Returns `null` if no value is found.
 * @param expanded
 */
function getCredentialStatus(expanded) {
  const statusValues = jsonld__default.default.getValues(expanded, vc_constants.expandedStatusProperty);
  if (!statusValues.length) {
    return null;
  } else if (statusValues.length > 1) {
    throw new Error(
      `\`statusPurpose\` must be an array containing up to one item, received: \`${expanded[vc_constants.expandedStatusProperty]}\``,
    );
  }
  const [status] = statusValues;

  if (!status[vc_constants.credentialIDField]) {
    throw new Error('"credentialStatus" must include an id.');
  }
  if (!status[vc_constants.credentialTypeField]) {
    throw new Error('"credentialStatus" must include a type.');
  }

  return status;
}

const ldTypeGen = (typ) => [
  typ,
  `${LD_SEC_TERM}${typ}`,
  `${LD_SEC_TERM_LEGACY}${typ}`,
  `/${typ}`,
];
const REV_REG_LD_TYPES = [vc_constants.RevRegType].flatMap(ldTypeGen);
const ACCUMULATOR_LD_TYPES = [cryptoWasmTs.VB_ACCUMULATOR_22, cryptoWasmTs.KB_UNI_ACCUMULATOR_24].flatMap(
  ldTypeGen,
);

/**
 * Returns `true` if supplied status is a registry revocation status.
 * @param status
 * @returns {boolean}
 */
const isRegistryRevocationStatus = ({ [vc_constants.credentialTypeField]: type }) => REV_REG_LD_TYPES.some((t) => type.includes(t));

/**
 * Returns `true` if supplied status is a accumulator revocation status.
 * @param status
 * @returns {boolean}
 */
const isAccumulatorRevocationStatus = ({
  [vc_constants.credentialTypeField]: type,
}) => ACCUMULATOR_LD_TYPES.some((t) => type.includes(t));

/**
 * Checks if a credential status has a registry revocation.
 * @param expanded
 * @returns {boolean}
 */
function hasRegistryRevocationStatus(expanded) {
  const status = getCredentialStatus(expanded);

  return !!status && isRegistryRevocationStatus(status);
}

/**
 * Check if the credential is revoked or not according to the revocation registry mechanism.
 * @param credential
 * @param documentLoader
 * @returns {Promise<{verified: boolean}|{verified: boolean, error: string}>} The returned object will have a key `verified`
 * which is true if the credential is not revoked and false otherwise. The `error` will describe the error if any.
 */
async function checkRevocationRegistryStatus(
  credential,
  documentLoader,
) {
  const status = getCredentialStatus(credential);
  const revId = getDockRevIdFromCredential(credential);
  if (!status) {
    throw new Error('Missing `credentialStatus`');
  }

  if (!isRegistryRevocationStatus(status)) {
    throw new Error(`Expected registry revocation status, got \`${status}\``);
  }

  const regId = status[vc_constants.credentialIDField];
  const fullId = `${regId}#${revId}`;

  // Hash credential id to get revocation id
  const { document: revocationStatus } = await documentLoader(fullId);
  if (revocationStatus) {
    return { verified: false, error: 'Revocation check failed' };
  }

  return { verified: true };
}

exports.RevEntryByteSize = RevEntryByteSize;
exports.RevRegIdByteSize = RevRegIdByteSize;
exports.buildDockCredentialStatus = buildDockCredentialStatus;
exports.checkRevocationRegistryStatus = checkRevocationRegistryStatus;
exports.createRandomRegistryId = createRandomRegistryId;
exports.getCredentialStatus = getCredentialStatus;
exports.getDockRevIdFromCredential = getDockRevIdFromCredential;
exports.hasRegistryRevocationStatus = hasRegistryRevocationStatus;
exports.isAccumulatorRevocationStatus = isAccumulatorRevocationStatus;
exports.isRegistryRevocationStatus = isRegistryRevocationStatus;
//# sourceMappingURL=revocation.cjs.map
