'use strict';

require('../../utils/types/array.cjs');
var utils_types_number = require('../../utils/types/number.cjs');
require('crypto');
var utils_inheritance = require('../../utils/inheritance.cjs');
require('bs58');
require('multiformats/bases/base58');
require('varint');
require('../../utils/encoding/ss-58.cjs');
require('../../utils/types/object.cjs');
require('../../utils/types/iterable.cjs');
require('../../utils/types/set.cjs');
require('blake2b');
require('../../utils/types/bytes.cjs');
require('../../utils/interfaces.cjs');
require('../../utils/assert.cjs');
require('../../utils/error.cjs');
require('../../utils/types/string.cjs');

/**
 * Extends supplied class to check that its itstance lenght is always equal to the `static Size` property
 * defined on the extended class.
 *
 * @template C
 * @param {C} klass
 * @param {function(*): number} getSize
 * @returns {C}
 */
function sized(klass) {
  const name = `Sized<${klass.name}>`;
  const methods = [
    ...utils_inheritance.allObjectPropertiesIncludingPrototypes(klass.prototype),
  ].filter(
    (prop) => typeof klass.prototype[prop] === 'function' && prop !== 'constructor',
  );

  const obj = {
    [name]: class extends klass {
      /**
       * @static
       * @type {number}
       * Size of the underlying data.
       */
      static Size;

      constructor(...args) {
        super(...args);

        this.ensureValidSize();
      }

      getSize() {
        return utils_types_number.ensureNumber(this.length);
      }

      ensureValidSize() {
        const { Size } = this.constructor;

        if (typeof Size !== 'number') {
          throw new Error(
            `Size must be a number, received \`${Size?.toString()}\``,
          );
        }

        const size = this.getSize();

        if (size !== Size) {
          throw new Error(
            `Received \`${size}\` items while expected \`${Size}\` by \`${this.constructor.name}\``,
          );
        }
      }

      static random() {
        return super.random(this.Size);
      }
    },
  };

  for (const method of methods) {
    // eslint-disable-next-line func-names
    obj[name].prototype[method] = function (...args) {
      const res = klass.prototype[method].apply(this, args);
      this.ensureValidSize();

      return res;
    };
  }

  return utils_inheritance.withExtendedStaticProperties(['Size'], obj[name]);
}

module.exports = sized;
//# sourceMappingURL=sized.cjs.map
