import '../../utils/types/array.js';
import { ensureNumber } from '../../utils/types/number.js';
import 'crypto';
import { allObjectPropertiesIncludingPrototypes, withExtendedStaticProperties } from '../../utils/inheritance.js';
import 'bs58';
import 'multiformats/bases/base58';
import 'varint';
import '../../utils/encoding/ss-58.js';
import '../../utils/types/object.js';
import '../../utils/types/iterable.js';
import '../../utils/types/set.js';
import 'blake2b';
import '../../utils/types/bytes.js';
import '../../utils/interfaces.js';
import '../../utils/assert.js';
import '../../utils/error.js';
import '../../utils/types/string.js';

/**
 * 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 = [
    ...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 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 withExtendedStaticProperties(['Size'], obj[name]);
}

export { sized as default };
//# sourceMappingURL=sized.js.map
