import { generateUUID } from '../../../utils/misc';
import { FormFieldValidationRuleHandler } from './form-field-validation-rule-handler';
/** The `FormFieldValidationRuleHandlerChain` class is responsible for handling a chain of form field validation rule handlers and processing the validation rules for a given input. */
export class FormFieldValidationRuleHandlerChain {
  constructor() {
    this.handlers = [];
    this.processId = '';
  }
  /**
   * The function checks if there is an async rule handler that can be evaluated for a given input and form field status.
   *
   * @param {unknown} input - The input parameter represents the value that needs to be validated.
   * @param {FormFieldStatusMeta} status - It represents the current status of a form field.
   * @param {(keyof FormFieldStatusMeta)[]} triggerStatus - It represents the status values that can trigger the validation rule handler.
   * @returns {boolean} a boolean value. It returns true if there is an async rule handler found in the `handlers` array, and false otherwise.
   */
  checkIncludesAsyncRuleHandler(input, status, triggerStatus) {
    const asyncRuleHandler = this.handlers.find((handles) => {
      const handlesStatus = handles.getStatus(input, status, triggerStatus);
      return handlesStatus.canEvaluate && handlesStatus.isAsync;
    });
    return asyncRuleHandler ? true : false;
  }
  /**
   * The function initializes form field validation rule handlers based on the provided rules.
   *
   * @param {FormFieldValidationRule<unknown>[]} rules - An array of FormFieldValidationRule objects.
   */
  setHandlersByRules(rules) {
    if (rules && rules.length) {
      this.handlers = rules.map((rule) => {
        return new FormFieldValidationRuleHandler(rule);
      });
    }
    else {
      this.handlers = [];
    }
  }
  /**
   * iterates through a list of handlers to evaluate and handle the input, returning a validation result and call the callback method
   *
   * @param {unknown} input - The input parameter represents the data that needs to be processed.
   * @param {FormFieldStatusMeta} status - It represents the current status of a form field.
   * @param {(keyof FormFieldStatusMeta)[]} triggerStatus - These keys represent the fields that have triggered a change in status.
   * @param {boolean | 'rule'} showError - A boolean or string rule form method call, It's a param of callback.
   * @param {ValidationCallBack} callback - The method should be call when validation result is not valid.
   * @returns {Promise<EvaluateResult>} validate result.
   */
  async process(input, status, triggerStatus, showError, callback) {
    const UUID = this.preProcess();
    let result;
    for (const ruleHandler of this.handlers) {
      if (UUID !== this.processId)
        break;
      const handlersStatus = ruleHandler.getStatus(input, status, triggerStatus);
      if (!handlersStatus.canEvaluate)
        continue;
      let evaluateResult;
      if (handlersStatus.isAsync) {
        try {
          evaluateResult = await ruleHandler.handle(input);
          // eslint-disable-next-line no-empty
        }
        catch (e) { }
      }
      else {
        evaluateResult = ruleHandler.handle(input);
      }
      if (evaluateResult && !evaluateResult.isValid) {
        const shouldShowError = ruleHandler.shouldShowError(status, triggerStatus);
        result = Object.assign(Object.assign({}, evaluateResult), { shouldShowError });
        break;
      }
    }
    this.postProcess(UUID, showError, result, callback);
    if (result) {
      return { isValid: false, message: result.message };
    }
    else {
      return { isValid: true, message: '' };
    }
  }
  /**
   * sets the processId to the UUID
   *
   * @returns {string} the UUID (string)
   */
  preProcess() {
    const UUID = generateUUID();
    this.processId = UUID;
    return UUID;
  }
  /**
   * call the method after get the validation result, such as send validation result to form, show or hide field error message
   *
   * @param {string} UUID the validate id
   * @param {boolean | 'rule'} showError boolean value to show or not the error message
   * @param {Nullable<ValidationResult>} validationResult the validation result
   * @param {ValidationCallBack} callback the method need to call to handle the result
   */
  postProcess(UUID, showError, validationResult, callback) {
    if (this.processId === UUID) {
      callback(showError, validationResult);
    }
  }
}
