import _typeof from 'babel-runtime/helpers/typeof';
import _defineProperty from 'babel-runtime/helpers/defineProperty';
import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties';
import _mergeJSXProps from 'babel-helper-vue-jsx-merge-props';
import _toConsumableArray from 'babel-runtime/helpers/toConsumableArray';
import intersperse from 'intersperse';
import PropTypes from '../_util/vue-types';
import classNames from 'classnames';
import find from 'lodash/find';
import Row from '../grid/Row';
import Col, { ColProps } from '../grid/Col';
import warning from '../_util/warning';
import { FIELD_META_PROP, FIELD_DATA_PROP } from './constants';
import { initDefaultProps, getComponentFromProp, filterEmpty, getSlotOptions, isValidElement, getAllChildren } from '../_util/props-util';
import getTransitionProps from '../_util/getTransitionProps';
import BaseMixin from '../_util/BaseMixin';
import { cloneElement, cloneVNodes } from '../_util/vnode';
import Icon from '../icon';

function noop() {}
export var FormItemProps = {
  id: PropTypes.string,
  prefixCls: PropTypes.string,
  label: PropTypes.any,
  labelCol: PropTypes.shape(ColProps).loose,
  wrapperCol: PropTypes.shape(ColProps).loose,
  help: PropTypes.any,
  extra: PropTypes.any,
  validateStatus: PropTypes.oneOf(['', 'success', 'warning', 'error', 'validating']),
  hasFeedback: PropTypes.bool,
  required: PropTypes.bool,
  colon: PropTypes.bool,
  fieldDecoratorId: PropTypes.string,
  fieldDecoratorOptions: PropTypes.object
};
function comeFromSlot() {
  var vnodes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  var itemVnode = arguments[1];

  var isSlot = false;
  for (var i = 0, len = vnodes.length; i < len; i++) {
    var vnode = vnodes[i];
    if (vnode && (vnode === itemVnode || vnode.$vnode === itemVnode)) {
      isSlot = true;
    } else {
      var children = vnode.componentOptions ? vnode.componentOptions.children : vnode.children;
      isSlot = comeFromSlot(children, itemVnode);
    }
    if (isSlot) {
      break;
    }
  }
  return isSlot;
}

export default {
  name: 'AFormItem',
  __ANT_FORM_ITEM: true,
  mixins: [BaseMixin],
  props: initDefaultProps(FormItemProps, {
    hasFeedback: false,
    prefixCls: 'ant-form',
    colon: true
  }),
  inject: {
    FormProps: { 'default': function _default() {
        return {};
      } },
    decoratorFormProps: { 'default': function _default() {
        return {};
      } },
    collectFormItemContext: { 'default': function _default() {
        return noop;
      } }
  },
  data: function data() {
    return { helpShow: false };
  },
  created: function created() {
    this.collectContext();
  },
  beforeUpdate: function beforeUpdate() {
    if (process.env.NODE_ENV !== 'production') {
      this.collectContext();
    }
  },
  beforeDestroy: function beforeDestroy() {
    this.collectFormItemContext(this.$vnode.context, 'delete');
  },
  mounted: function mounted() {
    warning(this.getControls(this.slotDefault, true).length <= 1, '`Form.Item` cannot generate `validateStatus` and `help` automatically, ' + 'while there are more than one `getFieldDecorator` in it.');
    warning(!this.fieldDecoratorId, '`fieldDecoratorId` is deprecated. please use `v-decorator={id, options}` instead.');
  },

  methods: {
    collectContext: function collectContext() {
      if (this.FormProps.form && this.FormProps.form.templateContext) {
        var templateContext = this.FormProps.form.templateContext;

        var vnodes = Object.values(templateContext.$slots || {}).reduce(function (a, b) {
          return [].concat(_toConsumableArray(a), _toConsumableArray(b));
        }, []);
        var isSlot = comeFromSlot(vnodes, this.$vnode);
        warning(!isSlot, 'You can not set FormItem from slot, please use slot-scope instead slot');
        var isSlotScope = false;
        // 进一步判断是否是通过slot-scope传递
        if (!isSlot && this.$vnode.context !== templateContext) {
          isSlotScope = comeFromSlot(this.$vnode.context.$children, templateContext.$vnode);
        }
        if (!isSlotScope && !isSlot) {
          this.collectFormItemContext(this.$vnode.context);
        }
      }
    },
    getHelpMessage: function getHelpMessage() {
      var help = getComponentFromProp(this, 'help');
      var onlyControl = this.getOnlyControl();
      if (help === undefined && onlyControl) {
        var errors = this.getField().errors;
        if (errors) {
          return intersperse(errors.map(function (e, index) {
            return isValidElement(e.message) ? cloneElement(e.message, { key: index }) : e.message;
          }), ' ');
        } else {
          return '';
        }
      }

      return help;
    },
    getControls: function getControls() {
      var childrenArray = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
      var recursively = arguments[1];

      var controls = [];
      for (var i = 0; i < childrenArray.length; i++) {
        if (!recursively && controls.length > 0) {
          break;
        }

        var child = childrenArray[i];
        if (!child.tag && child.text.trim() === '') {
          continue;
        }

        if (getSlotOptions(child).__ANT_FORM_ITEM) {
          continue;
        }
        var children = getAllChildren(child);
        var attrs = child.data && child.data.attrs || {};
        if (FIELD_META_PROP in attrs) {
          // And means FIELD_DATA_PROP in child.props, too.
          controls.push(child);
        } else if (children) {
          controls = controls.concat(this.getControls(children, recursively));
        }
      }
      return controls;
    },
    getOnlyControl: function getOnlyControl() {
      var child = this.getControls(this.slotDefault, false)[0];
      return child !== undefined ? child : null;
    },
    getChildAttr: function getChildAttr(prop) {
      var child = this.getOnlyControl();
      var data = {};
      if (!child) {
        return undefined;
      }
      if (child.data) {
        data = child.data;
      } else if (child.$vnode && child.$vnode.data) {
        data = child.$vnode.data;
      }
      return data[prop] || data.attrs[prop];
    },
    getId: function getId() {
      return this.getChildAttr('id');
    },
    getMeta: function getMeta() {
      return this.getChildAttr(FIELD_META_PROP);
    },
    getField: function getField() {
      return this.getChildAttr(FIELD_DATA_PROP);
    },
    onHelpAnimEnd: function onHelpAnimEnd(_key, helpShow) {
      this.helpShow = helpShow;
      if (!helpShow) {
        this.$forceUpdate();
      }
    },
    renderHelp: function renderHelp() {
      var _this = this;

      var h = this.$createElement;

      var prefixCls = this.prefixCls;
      var help = this.getHelpMessage();
      var children = help ? h(
        'div',
        { 'class': prefixCls + '-explain', key: 'help' },
        [help]
      ) : null;
      if (children) {
        this.helpShow = !!children;
      }
      var transitionProps = getTransitionProps('show-help', {
        afterEnter: function afterEnter() {
          return _this.onHelpAnimEnd('help', true);
        },
        afterLeave: function afterLeave() {
          return _this.onHelpAnimEnd('help', false);
        }
      });
      return h(
        'transition',
        _mergeJSXProps([transitionProps, { key: 'help' }]),
        [children]
      );
    },
    renderExtra: function renderExtra() {
      var h = this.$createElement;
      var prefixCls = this.prefixCls;

      var extra = getComponentFromProp(this, 'extra');
      return extra ? h(
        'div',
        { 'class': prefixCls + '-extra' },
        [extra]
      ) : null;
    },
    getValidateStatus: function getValidateStatus() {
      var onlyControl = this.getOnlyControl();
      if (!onlyControl) {
        return '';
      }
      var field = this.getField();
      if (field.validating) {
        return 'validating';
      }
      if (field.errors) {
        return 'error';
      }
      var fieldValue = 'value' in field ? field.value : this.getMeta().initialValue;
      if (fieldValue !== undefined && fieldValue !== null && fieldValue !== '') {
        return 'success';
      }
      return '';
    },
    renderValidateWrapper: function renderValidateWrapper(c1, c2, c3) {
      var h = this.$createElement;

      var props = this.$props;
      var onlyControl = this.getOnlyControl;
      var validateStatus = props.validateStatus === undefined && onlyControl ? this.getValidateStatus() : props.validateStatus;

      var classes = props.prefixCls + '-item-control';
      if (validateStatus) {
        classes = classNames(props.prefixCls + '-item-control', {
          'has-feedback': props.hasFeedback || validateStatus === 'validating',
          'has-success': validateStatus === 'success',
          'has-warning': validateStatus === 'warning',
          'has-error': validateStatus === 'error',
          'is-validating': validateStatus === 'validating'
        });
      }
      var iconType = '';
      switch (validateStatus) {
        case 'success':
          iconType = 'check-circle';
          break;
        case 'warning':
          iconType = 'exclamation-circle';
          break;
        case 'error':
          iconType = 'close-circle';
          break;
        case 'validating':
          iconType = 'loading';
          break;
        default:
          iconType = '';
          break;
      }
      var icon = props.hasFeedback && iconType ? h(
        'span',
        { 'class': props.prefixCls + '-item-children-icon' },
        [h(Icon, {
          attrs: { type: iconType, theme: iconType === 'loading' ? 'outlined' : 'filled' }
        })]
      ) : null;
      return h(
        'div',
        { 'class': classes },
        [h(
          'span',
          { 'class': props.prefixCls + '-item-children' },
          [c1, icon]
        ), c2, c3]
      );
    },
    renderWrapper: function renderWrapper(children) {
      var h = this.$createElement;
      var prefixCls = this.prefixCls,
          _wrapperCol = this.wrapperCol,
          wrapperCol = _wrapperCol === undefined ? {} : _wrapperCol;

      var cls = wrapperCol['class'],
          style = wrapperCol.style,
          id = wrapperCol.id,
          on = wrapperCol.on,
          restProps = _objectWithoutProperties(wrapperCol, ['class', 'style', 'id', 'on']);

      var className = classNames(prefixCls + '-item-control-wrapper', cls);
      var colProps = {
        props: restProps,
        'class': className,
        key: 'wrapper',
        style: style,
        id: id,
        on: on
      };
      return h(
        Col,
        colProps,
        [children]
      );
    },
    isRequired: function isRequired() {
      var required = this.required;

      if (required !== undefined) {
        return required;
      }
      if (this.getOnlyControl()) {
        var meta = this.getMeta() || {};
        var validate = meta.validate || [];

        return validate.filter(function (item) {
          return !!item.rules;
        }).some(function (item) {
          return item.rules.some(function (rule) {
            return rule.required;
          });
        });
      }
      return false;
    },


    // Resolve duplicated ids bug between different forms
    // https://github.com/ant-design/ant-design/issues/7351
    onLabelClick: function onLabelClick(e) {
      var label = getComponentFromProp(this, 'label');
      var id = this.id || this.getId();
      if (!id) {
        return;
      }
      var controls = document.querySelectorAll('[id="' + id + '"]');
      if (controls.length !== 1) {
        // Only prevent in default situation
        // Avoid preventing event in `label={<a href="xx">link</a>}``
        if (typeof label === 'string') {
          e.preventDefault();
        }
        var control = this.$el.querySelector('[id="' + id + '"]');
        if (control && control.focus) {
          control.focus();
        }
      }
    },
    renderLabel: function renderLabel() {
      var h = this.$createElement;
      var prefixCls = this.prefixCls,
          _labelCol = this.labelCol,
          labelCol = _labelCol === undefined ? {} : _labelCol,
          colon = this.colon,
          id = this.id;

      var label = getComponentFromProp(this, 'label');
      var required = this.isRequired();

      var labelColClass = labelCol['class'],
          labelColStyle = labelCol.style,
          labelColId = labelCol.id,
          on = labelCol.on,
          restProps = _objectWithoutProperties(labelCol, ['class', 'style', 'id', 'on']);

      var labelColClassName = classNames(prefixCls + '-item-label', labelColClass);
      var labelClassName = classNames(_defineProperty({}, prefixCls + '-item-required', required));

      var labelChildren = label;
      // Keep label is original where there should have no colon
      var haveColon = colon && this.FormProps.layout !== 'vertical';
      // Remove duplicated user input colon
      if (haveColon && typeof label === 'string' && label.trim() !== '') {
        labelChildren = label.replace(/[:|:]\s*$/, '');
      }
      var colProps = {
        props: restProps,
        'class': labelColClassName,
        key: 'label',
        style: labelColStyle,
        id: labelColId,
        on: on
      };

      return label ? h(
        Col,
        colProps,
        [h(
          'label',
          {
            attrs: {
              'for': id || this.getId(),

              title: typeof label === 'string' ? label : ''
            },
            'class': labelClassName, on: {
              'click': this.onLabelClick
            }
          },
          [labelChildren]
        )]
      ) : null;
    },
    renderChildren: function renderChildren() {
      return [this.renderLabel(), this.renderWrapper(this.renderValidateWrapper(this.slotDefault, this.renderHelp(), this.renderExtra()))];
    },
    renderFormItem: function renderFormItem(children) {
      var _itemClassName;

      var h = this.$createElement;

      var props = this.$props;
      var prefixCls = props.prefixCls;
      var itemClassName = (_itemClassName = {}, _defineProperty(_itemClassName, prefixCls + '-item', true), _defineProperty(_itemClassName, prefixCls + '-item-with-help', this.helpShow), _defineProperty(_itemClassName, prefixCls + '-item-no-colon', !props.colon), _itemClassName);

      return h(
        Row,
        { 'class': classNames(itemClassName) },
        [children]
      );
    },
    decoratorOption: function decoratorOption(vnode) {
      if (vnode.data && vnode.data.directives) {
        var directive = find(vnode.data.directives, ['name', 'decorator']);
        warning(!directive || directive && Array.isArray(directive.value), 'Invalid directive: type check failed for directive "decorator". Expected Array, got ' + _typeof(directive.value) + '. At ' + vnode.tag + '.');
        return directive ? directive.value : null;
      } else {
        return null;
      }
    },
    decoratorChildren: function decoratorChildren(vnodes) {
      var FormProps = this.FormProps;

      var getFieldDecorator = FormProps.form.getFieldDecorator;
      for (var i = 0, len = vnodes.length; i < len; i++) {
        var vnode = vnodes[i];
        if (getSlotOptions(vnode).__ANT_FORM_ITEM) {
          break;
        }
        if (vnode.children) {
          vnode.children = this.decoratorChildren(cloneVNodes(vnode.children));
        } else if (vnode.componentOptions && vnode.componentOptions.children) {
          vnode.componentOptions.children = this.decoratorChildren(cloneVNodes(vnode.componentOptions.children));
        }
        var option = this.decoratorOption(vnode);
        if (option && option[0]) {
          vnodes[i] = getFieldDecorator(option[0], option[1])(vnode);
        }
      }
      return vnodes;
    }
  },

  render: function render() {
    var $slots = this.$slots,
        decoratorFormProps = this.decoratorFormProps,
        fieldDecoratorId = this.fieldDecoratorId,
        _fieldDecoratorOption = this.fieldDecoratorOptions,
        fieldDecoratorOptions = _fieldDecoratorOption === undefined ? {} : _fieldDecoratorOption,
        FormProps = this.FormProps;

    var child = filterEmpty($slots['default'] || []);
    if (decoratorFormProps.form && fieldDecoratorId && child.length) {
      var getFieldDecorator = decoratorFormProps.form.getFieldDecorator;
      child[0] = getFieldDecorator(fieldDecoratorId, fieldDecoratorOptions)(child[0]);
      warning(!(child.length > 1), '`autoFormCreate` just `decorator` then first children. but you can use JSX to support multiple children');
      this.slotDefault = child;
    } else if (FormProps.form) {
      child = cloneVNodes(child);
      this.slotDefault = this.decoratorChildren(child);
    } else {
      this.slotDefault = child;
    }

    var children = this.renderChildren();
    return this.renderFormItem(children);
  }
};