import { isDateStr, compareIsAfter, compareIsBefore } from '../../../utils/date';
import { InternalFormFieldValidationRule } from './internal-form-field-validation-rule.abstract';
/**
 * A rule that checks if date is in minDate and maxDate range
 */
export class MinAndMaxDateRule extends InternalFormFieldValidationRule {
  /**
   * It creates a new instance of the class with the given config.
   *
   * @param {{ minDate: string; maxDate: string }} config - config including the minDate and the maxDate
   * @param {string} config.minDate - The minDate
   * @param {string} config.maxDate - The maxDate
   */
  constructor(config) {
    super();
    /**
     *  Evaluate if single date is within the min and max date range
     *
     * @param {string} input A single date string
     * @returns  {EvaluateResultTranslation}An object with two properties: isValid and message.
     */
    this.evaluateSingleDate = (input) => {
      const singleDate = new Date(input);
      if (this.minDate && compareIsBefore(singleDate, this.minDate)) {
        return this.invalidResult({
          key: 'The date should be later than {0}',
          args: [this.config.minDate],
        });
      }
      if (this.maxDate && compareIsAfter(singleDate, this.maxDate)) {
        return this.invalidResult({
          key: 'The date should be earlier than {0}',
          args: [this.config.maxDate],
        });
      }
      return this.validResult();
    };
    /**
     * Evaluate if start date or end date are within the min and max date range
     *
     * @param {string[]} input includes start date or end date
     * @returns  {EvaluateResultTranslation}An object with two properties: isValid and message.
     */
    this.evaluateRangeDates = (input) => {
      const startDate = new Date(input[0]);
      const endDate = new Date(input[1]);
      if (this.minDate && compareIsBefore(startDate, this.minDate)) {
        return this.invalidResult({
          key: 'The start date should be later than {0}',
          args: [this.config.minDate],
        });
      }
      if (this.maxDate && compareIsAfter(startDate, this.maxDate)) {
        return this.invalidResult({
          key: 'The start date should be earlier than {0}',
          args: [this.config.maxDate],
        });
      }
      if (endDate && this.maxDate && compareIsAfter(endDate, this.maxDate)) {
        return this.invalidResult({
          key: 'The end date should be earlier than {0}',
          args: [this.config.maxDate],
        });
      }
      return this.validResult();
    };
    /**
     * Generate an invalid result
     *
     * @param {string} message Error message
     * @returns {EvaluateResultTranslation} invalid validation result
     */
    this.invalidResult = (message) => ({
      isValid: false,
      message,
    });
    /**
     * Generate a valid result
     *
     * @returns {EvaluateResultTranslation} invalid validation result
     */
    this.validResult = () => ({
      isValid: true,
      message: '',
    });
    this.config = config;
    this.minDate = config.minDate ? new Date(config.minDate) : null;
    this.maxDate = config.maxDate ? new Date(config.maxDate) : null;
  }
  /**
   * 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} A boolean value.
   */
  canEvaluate(input, status) {
    return status
      ? this.isAcceptedInputType(input) && !status.disabled && status.visible
      : this.isAcceptedInputType(input);
  }
  /**
   * If the input is a string, check if it's within the min and max date range.
   * If the input is an array, check if the start date is within the min and max date range, and if the end date is within the
   * max date range
   *
   * @param {string | string[]} input - The value of the date picker.
   * @returns  {EvaluateResultTranslation} - The result of the evaluation.
   */
  evaluate(input) {
    if (!this.isAcceptedInputType(input)) {
      throw new Error(`Unexpected type of input: ${typeof input}: ${input}`);
    }
    if (typeof input === 'string') {
      return this.evaluateSingleDate(input);
    }
    else {
      return this.evaluateRangeDates(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);
  }
  /**
   * It returns true if the input is a string or an array of two strings, and each string is a valid date string
   *
   * @param {unknown} input - The input value to be validated.
   * @returns {boolean} A boolean value.
   */
  isAcceptedInputType(input) {
    return ((typeof input === 'string' && isDateStr(input)) ||
      (Array.isArray(input) && input.length === 2 && input.every(isDateStr)));
  }
}
