'use strict';

const typeGuard = require('./type-guard-c1195e50.js');
const number = require('./number-4816603d.js');
const internalFormFieldValidationRule_abstract = require('./internal-form-field-validation-rule.abstract-07cdff46.js');

/**
 * A rule that checks if it's a valid credit card number
 *
 * @augments {InternalFormFieldValidationRule<string>}
 */
class ValidCreditCardNumberRule extends internalFormFieldValidationRule_abstract.InternalFormFieldValidationRule {
  /**
   * It takes a string, maps it to an array of numbers, reverses the array, checks the length of the
   * array, maps the even digits to a new array, sums the new array, and returns true if the sum is
   * divisible by 10
   *
   * @param {string} value - string - The card number to validate
   * @returns {boolean} A function that takes a string and returns a boolean.
   */
  static validateCreditCardDigits(value) {
    const cardNumberArray = number.mapStringToNumberArray(value).reverse();
    if (cardNumberArray.length < 12 || cardNumberArray.length > 19) {
      return false;
    }
    const checkSum = number.mapParityDigits(cardNumberArray, (index) => index % 2 === 0).reduce(number.sum, 0);
    return checkSum % 10 === 0;
  }
  /**
   * It checks if the credit card number is valid.
   *
   * @param {string} input - The input value to be evaluated.
   * @returns {EvaluateResult} an object with two properties: isValid and message.
   */
  evaluate(input) {
    if (!this.isAcceptedInputType(input)) {
      throw new Error(`Unexpected type of input: ${typeof input}: ${input}`);
    }
    const formattedCardNumber = this.formatCreditCardNumber(input);
    const isValid = this.isValidVisaCreditCard(input) ||
      this.isValidAmexCreditCard(input) ||
      this.isValidDiscoverCreditCard(input) ||
      this.isValidMasterCardCreditCard(input) ||
      (!this.isSupportedCreditCardType(formattedCardNumber) &&
        ValidCreditCardNumberRule.validateCreditCardDigits(formattedCardNumber));
    return {
      isValid,
      message: isValid ? '' : 'Credit card number is invalid',
    };
  }
  /**
   * If the input is accepted and the status is not disabled and visible, then return true
   *
   * @param {unknown} input - The input to be evaluated.
   * @param {FormFieldStatusMeta} [status] - FormFieldStatusMeta
   * @returns {boolean} The return value is a boolean that indicates whether the input can be evaluated.
   */
  canEvaluate(input, status) {
    return status
      ? this.isAcceptedInputType(input) && !status.disabled && status.visible
      : this.isAcceptedInputType(input);
  }
  /**
   * If the field is not disabled, visible, and has been touched or modified, then show the error
   *
   * @param {FormFieldStatusMeta} status - FormFieldStatusMeta
   * @returns {boolean} A boolean value.
   */
  shouldShowError(status) {
    return !status.disabled && status.visible && (status.touched || status.modified);
  }
  /**
   * The function `isValidVisaCreditCard` checks if a given card number is a valid Visa credit card number.
   *
   * @param {string} cardNumber - The `cardNumber` parameter is a string representing a credit card
   * number that needs to be validated as a Visa credit card.
   * @returns {boolean} a boolean value indicating whether the provided card number is a valid Visa credit card.
   */
  isValidVisaCreditCard(cardNumber) {
    const formattedCardNumber = this.formatCreditCardNumber(cardNumber);
    return (typeGuard.VisaRegex.test(formattedCardNumber) &&
      (formattedCardNumber.length === 13 ||
        formattedCardNumber.length === 16 ||
        formattedCardNumber.length === 19) &&
      ValidCreditCardNumberRule.validateCreditCardDigits(formattedCardNumber));
  }
  /**
   * The function `isValidMasterCardCreditCard` checks if a given credit card number is a valid Mastercard number.
   *
   * @param {string} cardNumber - The `cardNumber` parameter is a string representing a credit card
   * number that needs to be validated to determine if it belongs to a Mastercard.
   * @returns {boolean} The function isValidMasterCardCreditCard is returning a boolean value, which indicates whether the
   * provided card number is a valid Mastercard.
   */
  isValidMasterCardCreditCard(cardNumber) {
    const formattedCardNumber = this.formatCreditCardNumber(cardNumber);
    return (typeGuard.MasterCardRegex.test(formattedCardNumber) &&
      formattedCardNumber.length === 16 &&
      ValidCreditCardNumberRule.validateCreditCardDigits(formattedCardNumber));
  }
  /**
   * The function `isValidAmexCreditCard` checks if a given credit card number is a valid American Express
   * card number.
   *
   * @param {string} cardNumber - The `cardNumber` parameter is a string representing a credit card
   * number that needs to be validated as an American Express credit card.
   * @returns {boolean} The `isValidAmexCreditCard` function is returning a boolean value, which indicates whether the
   * provided `cardNumber` is a valid American Express credit card.
   */
  isValidAmexCreditCard(cardNumber) {
    const formattedCardNumber = this.formatCreditCardNumber(cardNumber);
    return (typeGuard.AmexRegex.test(formattedCardNumber) &&
      formattedCardNumber.length === 15 &&
      ValidCreditCardNumberRule.validateCreditCardDigits(formattedCardNumber));
  }
  /**
   * The function `isValidDiscoverCreditCard` checks if a given credit card number is a valid Discover card
   * number.
   *
   * @param {string} cardNumber - The `cardNumber` parameter is a string representing a credit card
   * number that needs to be validated as a Discover credit card.
   * @returns {boolean} a boolean value indicating whether the provided card number is a valid Discover credit
   * card.
   */
  isValidDiscoverCreditCard(cardNumber) {
    const formattedCardNumber = this.formatCreditCardNumber(cardNumber);
    return (typeGuard.DiscoverRegex.test(formattedCardNumber) &&
      formattedCardNumber.length === 16 &&
      ValidCreditCardNumberRule.validateCreditCardDigits(formattedCardNumber));
  }
  /**
   * If the input is not a string, then return false, otherwise return true.
   *
   * @param {unknown} input - The input value to be validated.
   * @returns {boolean} a boolean value.
   */
  isAcceptedInputType(input) {
    return typeof input === 'string';
  }
  /**
   * The `formatCreditCardNumber` function removes all spaces from a given credit card number string.
   *
   * @param {string} cardNumber - The `cardNumber` parameter is a string representing a credit card
   * number. The `formatCreditCardNumber` function removes any white space characters from the input
   * `cardNumber` string and returns the formatted credit card number.
   * @returns {string} The `formatCreditCardNumber` function returns the `cardNumber` string with all spaces
   * removed.
   */
  formatCreditCardNumber(cardNumber) {
    return cardNumber.replace(/\s/g, '');
  }
  /**
   * The function checks if a given credit card number matches any of the supported card types (Visa,
   * MasterCard, Amex, Discover).
   *
   * @param {string} formattedCardNumber - The `formattedCardNumber` parameter is a string representing a
   * credit card number that has been formatted in a specific way. The function
   * `isSupportedCreditCardType` checks if the provided credit card number matches any of the regular
   * expressions for Visa, MasterCard, American Express (Amex), or Discover
   * @returns {boolean} The `isSupportedCreditCardType` function is returning a boolean value based on whether the
   * `formattedCardNumber` matches any of the regular expressions for Visa, MasterCard, American Express
   * (Amex), or Discover credit card types. If the `formattedCardNumber` matches any of these card types,
   * the function will return `true`, indicating that the credit card type is supported. Otherwise, it
   */
  isSupportedCreditCardType(formattedCardNumber) {
    return (typeGuard.VisaRegex.test(formattedCardNumber) ||
      typeGuard.MasterCardRegex.test(formattedCardNumber) ||
      typeGuard.AmexRegex.test(formattedCardNumber) ||
      typeGuard.DiscoverRegex.test(formattedCardNumber));
  }
}

exports.ValidCreditCardNumberRule = ValidCreditCardNumberRule;
