function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

import { useState, useRef } from 'react';
import { HOOK_KEY } from './context';

var validateRules = function validateRules(name, value, rules) {
  for (var j = 0; j < rules.length; j += 1) {
    var rule = rules[j] || {};
    var required = rule.required,
        pattern = rule.pattern,
        validator = rule.validator,
        _rule$message = rule.message,
        message = _rule$message === void 0 ? "".concat(name, " is required") : _rule$message;

    if (required) {
      if (!value && value !== 0 && value !== false) {
        return [false, value, {
          error: true,
          message: message
        }];
      }
    }

    if (pattern) {
      var result = pattern.test(value);

      if (!result) {
        return [false, value, {
          error: true,
          message: message
        }];
      }
    }

    if (validator && typeof validator === 'function') {
      var _result = validator(value);

      if (!_result) {
        return [false, value, {
          error: true,
          message: message
        }];
      }
    }
  }

  return [true];
};

export var FormStore = function FormStore(forceRootUpdate) {
  var _this = this;

  _classCallCheck(this, FormStore);

  this.store = {};
  this.errors = {};
  this.fieldsProps = [];
  this.initialValues = {};
  this.callbacks = {};

  this.getForm = function () {
    return {
      getFieldError: _this.getFieldError,
      getFieldsError: _this.getFieldsError,
      getFieldValue: _this.getFieldValue,
      getFieldsValue: _this.getFieldsValue,
      setFieldsValue: _this.setFieldsValue,
      resetFields: _this.resetFields,
      validateFields: _this.validateFields,
      submit: _this.submit,
      getInternalHooks: _this.getInternalHooks
    };
  };

  this.setInitialValues = function (initialValues) {
    _this.initialValues = initialValues;
  };

  this.getFieldValue = function (name) {
    var _a;

    return (_a = _this.store) === null || _a === void 0 ? void 0 : _a[name];
  };

  this.getFieldsValue = function () {
    return _this.store;
  };

  this.resetFields = function (value) {
    var val = value && _typeof(value) === 'object' ? value : null;

    _this.initStore(val || _this.initialValues);

    _this.forceRootUpdate();
  };

  this.setFieldsValue = function (values) {
    _this.store = Object.assign({}, _this.store, values);

    _this.forceRootUpdate();
  };

  this.validateFields = function () {
    var _this$getFieldRuleObj = _this.getFieldRuleObjects(),
        _this$getFieldRuleObj2 = _slicedToArray(_this$getFieldRuleObj, 2),
        fieldRulesObj = _this$getFieldRuleObj2[0],
        nullValuesObj = _this$getFieldRuleObj2[1];

    var isPassed = true;
    var errorState = {};
    var errorFields = {};
    var names = Object.keys(_this.store);

    for (var i = 0; i < names.length; i += 1) {
      var name = names[i];
      var value = _this.store[name];
      var rules = fieldRulesObj[name];

      if (rules) {
        var _validateRules = validateRules(name, value, rules),
            _validateRules2 = _slicedToArray(_validateRules, 3),
            isvalidateRulesPassed = _validateRules2[0],
            fieldValue = _validateRules2[1],
            fieldState = _validateRules2[2];

        if (!isvalidateRulesPassed) {
          errorFields[name] = fieldValue;
          errorState[name] = fieldState;
          isPassed = false;
        }
      }
    }

    return [isPassed, errorState, errorFields, nullValuesObj];
  };

  this.getFieldError = function (name) {
    var _a;

    return (_a = _this.errors) === null || _a === void 0 ? void 0 : _a[name];
  };

  this.getFieldsError = function () {
    return _this.errors;
  };

  this.submit = function () {
    var _this$callbacks = _this.callbacks,
        onFinish = _this$callbacks.onFinish,
        onFinishFailed = _this$callbacks.onFinishFailed;

    var _this$validateFields = _this.validateFields(),
        _this$validateFields2 = _slicedToArray(_this$validateFields, 4),
        isPassed = _this$validateFields2[0],
        errorState = _this$validateFields2[1],
        errorFields = _this$validateFields2[2],
        nullValuesObj = _this$validateFields2[3];

    if (!isPassed) {
      _this.setErrors(Object.assign({}, errorState));

      _this.forceRootUpdate();

      onFinishFailed === null || onFinishFailed === void 0 ? void 0 : onFinishFailed(_this.store, errorFields);
      return;
    }

    if (isPassed) {
      _this.setErrors(nullValuesObj);

      _this.forceRootUpdate();
    }

    onFinish === null || onFinish === void 0 ? void 0 : onFinish(_this.store);
  };

  this.getInternalHooks = function (key) {
    if (key === HOOK_KEY) {
      return {
        initStore: _this.initStore,
        setCallbacks: _this.setCallbacks,
        setErrors: _this.setErrors,
        registerField: _this.registerField
      };
    }
  };

  this.setCallbacks = function (callbacks) {
    _this.callbacks = Object.assign({}, _this.callbacks, callbacks);
  };

  this.initStore = function (initialValues) {
    _this.setInitialValues(initialValues);

    var _this$getFieldRuleObj3 = _this.getFieldRuleObjects(),
        _this$getFieldRuleObj4 = _slicedToArray(_this$getFieldRuleObj3, 2),
        nullValuesObj = _this$getFieldRuleObj4[1]; // 不能用下面这句，因为第一个参数是{}, 这就会导致返回的是一个新的对象（引用变了）
    // return Object.assign({}, nullValuesObj, this.initialValues);


    var newStore = Object.assign(_this.store, nullValuesObj, _this.initialValues);
    var newError = Object.assign(_this.errors, nullValuesObj);
    _this.store = newStore;
    _this.errors = newError;
  };

  this.setErrors = function (errors) {
    _this.errors = Object.assign({}, _this.errors, errors);

    _this.forceRootUpdate();
  };

  this.getFieldRuleObjects = function () {
    var rules = _this.fieldsProps.map(function (i) {
      return {
        name: i === null || i === void 0 ? void 0 : i.name,
        rules: i === null || i === void 0 ? void 0 : i.rules
      };
    });

    var fieldRulesObj = {};
    var nullValuesObj = {};
    rules.forEach(function (i) {
      if (i.name && i.rules && Array.isArray(i.rules)) {
        fieldRulesObj[i.name] = i.rules;
        nullValuesObj[i.name] = undefined;
      }
    });
    return [fieldRulesObj, nullValuesObj];
  };

  this.registerField = function (fieldProps) {
    _this.fieldsProps.push(fieldProps);
  };

  this.forceRootUpdate = forceRootUpdate;
};

function useForm(form) {
  var formRef = useRef();

  var _useState = useState({}),
      _useState2 = _slicedToArray(_useState, 2),
      forceUpdate = _useState2[1];

  if (!formRef.current) {
    if (form) {
      formRef.current = form;
    } else {
      var forceReRender = function forceReRender() {
        forceUpdate({});
      };

      var formStore = new FormStore(forceReRender);
      formRef.current = formStore.getForm();
    }
  }

  return [formRef.current];
}

export default useForm;