text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.Predicate = exports.validatorSymbol = void 0;
  const is_1 = require("@sindresorhus/is");
  const argument_error_1 = require("../argument-error");
  const not_1 = require("../operators/not");
  const base_predicate_1 = require("./base-predicate");
  const generate_argument_error_message_1 = require("../utils/generate-argument-error-message");
  /**
  @hidden
  */
  exports.validatorSymbol = Symbol('validators');
  /**
  @hidden
  */
  class Predicate {
      constructor(type, options = {}) {
          Object.defineProperty(this, "type", {
              enumerable: true,
              configurable: true,
              writable: true,
              value: type
          });
          Object.defineProperty(this, "options", {
              enumerable: true,
              configurable: true,
              writable: true,
              value: options
          });
          Object.defineProperty(this, "context", {
              enumerable: true,
              configurable: true,
              writable: true,
              value: {
                  validators: []
              }
          });
          this.context = {
              ...this.context,
              ...this.options
          };
          const typeString = this.type.charAt(0).toLowerCase() + this.type.slice(1);
          this.addValidator({
              message: (value, label) => {
                  // We do not include type in this label as we do for other messages, because it would be redundant.
                  const label_ = label === null || label === void 0 ? void 0 : label.slice(this.type.length + 1);
                  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
                  return `Expected ${label_ || 'argument'} to be of type \`${this.type}\` but received type \`${is_1.default(value)}\``;
              },
              validator: value => is_1.default[typeString](value)
          });
      }
      /**
      @hidden
      */
      [base_predicate_1.testSymbol](value, main, label, idLabel) {
          // Create a map of labels -> received errors.
          const errors = new Map();
          for (const { validator, message } of this.context.validators) {
              if (this.options.optional === true && value === undefined) {
                  continue;
              }
              let result;
              try {
                  result = validator(value);
              }
              catch (error) {
                  // Any errors caught means validators couldn't process the input.
                  result = error;
              }
              if (result === true) {
                  continue;
              }
              const label2 = is_1.default.function_(label) ? label() : label;
              const labelWithTick = (label2 && idLabel) ? `\`${label2}\`` : label2;
              const label_ = labelWithTick ?
                  `${this.type} ${labelWithTick}` :
                  this.type;
              const mapKey = label2 || this.type;
              // Get the current errors encountered for this label.
              const currentErrors = errors.get(mapKey);
              // Pre-generate the error message that will be reported to the user.
              const errorMessage = message(value, label_, result);
              // If we already have any errors for this label.
              if (currentErrors) {
                  // If we don't already have this error logged, add it.
                  currentErrors.add(errorMessage);
              }
              else {
                  // Set this label and error in the full map.
                  errors.set(mapKey, new Set([errorMessage]));
              }
          }
          // If we have any errors to report, throw.
          if (errors.size > 0) {
              // Generate the `error.message` property.
              const message = generate_argument_error_message_1.generateArgumentErrorMessage(errors);
              throw new argument_error_1.ArgumentError(message, main, errors);
          }
      }
      /**
      @hidden
      */
      get [exports.validatorSymbol]() {
          return this.context.validators;
      }
      /**
      Invert the following validators.
      */
      get not() {
          return not_1.not(this);
      }
      /**
      Test if the value matches a custom validation function. The validation function should return an object containing a `validator` and `message`. If the `validator` is `false`, the validation fails and the `message` will be used as error message. If the `message` is a function, the function is invoked with the `label` as argument to let you further customize the error message.
  
      @param customValidator - Custom validation function.
      */
      validate(customValidator) {
          return this.addValidator({
              message: (_, label, error) => typeof error === 'string' ?
                  `(${label}) ${error}` :
                  error(label),
              validator: value => {
                  const { message, validator } = customValidator(value);
                  if (validator) {
                      return true;
                  }
                  return message;
              }
          });
      }
      /**
      Test if the value matches a custom validation function. The validation function should return `true` if the value passes the function. If the function either returns `false` or a string, the function fails and the string will be used as error message.
  
      @param validator - Validation function.
      */
      is(validator) {
          return this.addValidator({
              message: (value, label, error) => (error ?
                  `(${label}) ${error}` :
                  `Expected ${label} \`${value}\` to pass custom validation function`),
              validator
          });
      }
      /**
      Provide a new error message to be thrown when the validation fails.
  
      @param newMessage - Either a string containing the new message or a function returning the new message.
  
      @example
      ```
      ow('🌈', 'unicorn', ow.string.equals('🦄').message('Expected unicorn, got rainbow'));
      //=> ArgumentError: Expected unicorn, got rainbow
      ```
  
      @example
      ```
      ow('🌈', ow.string.minLength(5).message((value, label) => `Expected ${label}, to have a minimum length of 5, got \`${value}\``));
      //=> ArgumentError: Expected string, to be have a minimum length of 5, got `🌈`
      ```
      */
      message(newMessage) {
          const { validators } = this.context;
          validators[validators.length - 1].message = (value, label) => {
              if (typeof newMessage === 'function') {
                  return newMessage(value, label);
              }
              return newMessage;
          };
          return this;
      }
      /**
      Register a new validator.
  
      @param validator - Validator to register.
      */
      addValidator(validator) {
          this.context.validators.push(validator);
          return this;
      }
  }
  exports.Predicate = Predicate;
  