import _defineProperty from 'babel-runtime/helpers/defineProperty';
import _extends from 'babel-runtime/helpers/extends';
import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
import _createClass from 'babel-runtime/helpers/createClass';
import get from 'lodash/get';
import has from 'lodash/has';
import set from 'lodash/set';
import { flatFieldNames, getErrorStrs, getNameIfNested, getVirtualPaths } from './utils';

var atom = {};

var FieldsStore = function () {
  function FieldsStore(fields) {
    _classCallCheck(this, FieldsStore);

    _initialiseProps.call(this);

    this.fields = fields;
    this.fieldsMeta = {};
  }

  _createClass(FieldsStore, [{
    key: 'updateFields',
    value: function updateFields(fields) {
      _extends(this.fields, fields);
    }
  }, {
    key: 'setFields',
    value: function setFields(fields) {
      var _this = this;

      var fieldsMeta = this.fieldsMeta;
      var nowFields = _extends({}, this.fields, fields);
      var nowValues = {};
      Object.keys(fieldsMeta).forEach(function (f) {
        var _getNameIfNested = getNameIfNested(f),
            name = _getNameIfNested.name,
            isNested = _getNameIfNested.isNested;

        if (isNested && fieldsMeta[name].exclusive) {
          return;
        }
        nowValues[f] = _this.getValueFromFields(f, nowFields);
      });
      Object.keys(nowValues).forEach(function (f) {
        var value = nowValues[f];
        var fieldMeta = fieldsMeta[f];
        if (fieldMeta && fieldMeta.normalize) {
          var nowValue = fieldMeta.normalize(value, _this.getValueFromFields(f, _this.fields), nowValues);
          if (nowValue !== value) {
            nowFields[f] = _extends({}, nowFields[f], {
              value: nowValue
            });
          }
        }
      });
      this.fields = nowFields;
    }
  }, {
    key: 'resetFields',
    value: function resetFields(ns) {
      var newFields = {};
      var fields = this.fields;

      var names = ns || Object.keys(fields);
      names.forEach(function (name) {
        var field = fields[name];
        if (field && 'value' in field) {
          newFields[name] = {};
        }
      });
      return newFields;
    }
  }, {
    key: 'getValueFromFieldsInternal',
    value: function getValueFromFieldsInternal(name, fields) {
      var field = fields[name];
      if (field && 'value' in field) {
        return field.value;
      }
      var fieldMeta = this.fieldsMeta[name];
      return fieldMeta && fieldMeta.initialValue;
    }
  }, {
    key: 'getValueFromFields',
    value: function getValueFromFields(name, fields) {
      var _this2 = this;

      var fieldsMeta = this.fieldsMeta;

      if (fieldsMeta[name] && fieldsMeta[name].virtual) {
        var ret = {};
        Object.keys(fieldsMeta).forEach(function (fieldKey) {
          var nameIfNested = getNameIfNested(fieldKey);
          if (nameIfNested.name === name && nameIfNested.isNested) {
            set(ret, fieldKey, _this2.getValueFromFieldsInternal(fieldKey, fields));
          }
        });
        return ret[name];
      }
      return this.getValueFromFieldsInternal(name, fields);
    }
  }, {
    key: 'getValidFieldsName',
    value: function getValidFieldsName() {
      var fieldsMeta = this.fieldsMeta;
      return fieldsMeta ? Object.keys(fieldsMeta).filter(function (name) {
        return !fieldsMeta[name].hidden;
      }) : [];
    }
  }, {
    key: 'getFieldValuePropValue',
    value: function getFieldValuePropValue(fieldMeta) {
      var exclusive = fieldMeta.exclusive,
          leadingName = fieldMeta.leadingName,
          name = fieldMeta.name,
          getValueProps = fieldMeta.getValueProps,
          valuePropName = fieldMeta.valuePropName;
      var fieldsMeta = this.fieldsMeta;

      var field = exclusive ? this.getField(leadingName) : this.getField(name);
      var fieldValue = atom;
      if (field && 'value' in field) {
        fieldValue = field.value;
      }
      if (fieldValue === atom) {
        fieldValue = exclusive ? fieldsMeta[leadingName].initialValue : fieldMeta.initialValue;
      }
      if (getValueProps) {
        return getValueProps(fieldValue);
      }
      return _defineProperty({}, valuePropName, fieldValue);
    }
  }, {
    key: 'getField',
    value: function getField(name) {
      return _extends({}, this.fields[name], {
        name: name
      });
    }
  }, {
    key: 'getFieldMember',
    value: function getFieldMember(name, member) {
      return this.getField(name)[member];
    }
  }, {
    key: 'getFieldMeta',
    value: function getFieldMeta(name) {
      if (!this.fieldsMeta[name]) {
        this.fieldsMeta[name] = {};
      }
      return this.fieldsMeta[name];
    }
  }, {
    key: 'setFieldMeta',
    value: function setFieldMeta(name, meta) {
      this.fieldsMeta[name] = meta;
    }
  }, {
    key: 'clearField',
    value: function clearField(name) {
      delete this.fields[name];
      delete this.fieldsMeta[name];
    }
  }]);

  return FieldsStore;
}();

var _initialiseProps = function _initialiseProps() {
  var _this3 = this;

  this.getFieldsValue = function (names) {
    var fields = names || flatFieldNames(_this3.getValidFieldsName());
    var allValues = {};
    fields.forEach(function (f) {
      set(allValues, f, _this3.getFieldValue(f));
    });
    return allValues;
  };

  this.getFieldValue = function (name) {
    var fields = _this3.fields;

    return _this3.getValueFromFields(name, fields);
  };

  this.getFieldsError = function (names) {
    var fields = names || flatFieldNames(_this3.getValidFieldsName());
    var allErrors = {};
    fields.forEach(function (f) {
      set(allErrors, f, _this3.getFieldError(f));
    });
    return allErrors;
  };

  this.getFieldError = function (name) {
    return getErrorStrs(_this3.getFieldMember(name, 'errors'));
  };

  this.setFieldsInitialValue = function (initialValues) {
    var fieldsMeta = _this3.fieldsMeta;
    var virtualPaths = getVirtualPaths(fieldsMeta);
    Object.keys(initialValues).forEach(function (name) {
      if (fieldsMeta[name] && fieldsMeta[name].virtual) {
        for (var i = 0, len = virtualPaths[name].length; i < len; i++) {
          var path = virtualPaths[name][i];
          if (has(initialValues, path)) {
            fieldsMeta[path] = _extends({}, fieldsMeta[path], {
              initialValue: get(initialValues, path)
            });
          }
        }
      } else if (fieldsMeta[name]) {
        fieldsMeta[name] = _extends({}, fieldsMeta[name], {
          initialValue: initialValues[name]
        });
      }
    });
  };

  this.isFieldValidating = function (name) {
    return _this3.getFieldMember(name, 'validating');
  };

  this.isFieldsValidating = function (ns) {
    var names = ns || _this3.getValidFieldsName();
    return names.some(function (n) {
      return _this3.isFieldValidating(n);
    });
  };

  this.isFieldTouched = function (name) {
    return _this3.getFieldMember(name, 'touched');
  };

  this.isFieldsTouched = function (ns) {
    var names = ns || _this3.getValidFieldsName();
    return names.some(function (n) {
      return _this3.isFieldTouched(n);
    });
  };
};

export default function createFieldsStore(fields) {
  return new FieldsStore(fields);
}