import PropTypes from '../_util/vue-types'
import classNames from 'classnames'
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, getSlots } from '../_util/props-util'
import getTransitionProps from '../_util/getTransitionProps'
import BaseMixin from '../_util/BaseMixin'
export const 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,
}

export default {
  name: 'AFormItem',
  __ANT_FORM_ITEM: true,
  mixins: [BaseMixin],
  props: initDefaultProps(FormItemProps, {
    hasFeedback: false,
    prefixCls: 'ant-form',
    colon: true,
  }),
  inject: {
    FormProps: { default: {}},
  },
  data () {
    return { helpShow: false }
  },
  mounted () {
    warning(
      this.getControls(this.$slots.default, true).length <= 1,
      '`Form.Item` cannot generate `validateStatus` and `help` automatically, ' +
      'while there are more than one `getFieldDecorator` in it.',
    )
  },

  // shouldComponentUpdate(...args: any[]) {
  //   return PureRenderMixin.shouldComponentUpdate.apply(this, args);
  // }
  methods: {
    getHelpMsg () {
      const help = getComponentFromProp(this, 'help')
      const onlyControl = this.getOnlyControl()
      if (help === undefined && onlyControl) {
        const errors = this.getField().errors
        return errors ? errors.map((e) => e.message).join(', ') : ''
      }

      return help
    },

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

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

        if (getSlotOptions(child).__ANT_FORM_ITEM) {
          continue
        }
        const attrs = child.data && child.data.attrs
        if (!attrs) {
          continue
        }
        const slots = getSlots(child)
        if (FIELD_META_PROP in attrs) { // And means FIELD_DATA_PROP in chidl.props, too.
          controls.push(child)
        } else if (slots.default) {
          controls = controls.concat(this.getControls(slots.default, recursively))
        }
      }
      return controls
    },

    getOnlyControl () {
      const child = this.getControls(this.$slots.default, false)[0]
      return child !== undefined ? child : null
    },

    getChildAttr (prop) {
      const child = this.getOnlyControl()
      let 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 () {
      return this.getChildAttr('id')
    },

    getMeta () {
      return this.getChildAttr(FIELD_META_PROP)
    },

    getField () {
      return this.getChildAttr(FIELD_DATA_PROP)
    },

    onHelpAnimEnd  () {
      this.setState({ helpShow: false })
    },

    renderHelp () {
      const prefixCls = this.prefixCls
      const help = this.getHelpMsg()
      const children = help ? (
        <div class={`${prefixCls}-explain`} key='help'>
          {help}
        </div>
      ) : null
      const transitionProps = getTransitionProps('show-help', {
        afterLeave: this.onHelpAnimEnd,
      })
      if (children) {
        this.setState({ helpShow: true })
      }
      return (
        <transition
          {...transitionProps}
          key='help'
        >
          {children}
        </transition>
      )
    },

    renderExtra () {
      const { prefixCls } = this
      const extra = getComponentFromProp(this, 'extra')
      return extra ? (
        <div class={`${prefixCls}-extra`}>{extra}</div>
      ) : null
    },

    getValidateStatus () {
      const onlyControl = this.getOnlyControl()
      if (!onlyControl) {
        return ''
      }
      const field = this.getField()
      if (field.validating) {
        return 'validating'
      }
      if (field.errors) {
        return 'error'
      }
      const fieldValue = 'value' in field ? field.value : this.getMeta().initialValue
      if (fieldValue !== undefined && fieldValue !== null && fieldValue !== '') {
        return 'success'
      }
      return ''
    },

    renderValidateWrapper (c1, c2, c3) {
      const props = this.$props
      const onlyControl = this.getOnlyControl
      const validateStatus = (props.validateStatus === undefined && onlyControl)
        ? this.getValidateStatus()
        : props.validateStatus

      let 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',
        })
      }
      return (
        <div class={classes}>
          <span class={`${props.prefixCls}-item-children`}>{c1}</span>
          {c2}{c3}
        </div>
      )
    },

    renderWrapper (children) {
      const { prefixCls, wrapperCol = {}} = this
      const { class: cls, style, id, on, ...restProps } = wrapperCol
      const className = classNames(
        `${prefixCls}-item-control-wrapper`,
        cls,
      )
      const colProps = {
        props: restProps,
        class: className,
        key: 'wrapper',
        style,
        id,
        on,
      }
      return (
        <Col {...colProps}>
          {children}
        </Col>
      )
    },

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

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

    // Resolve duplicated ids bug between different forms
    // https://github.com/ant-design/ant-design/issues/7351
    onLabelClick (e) {
      const label = getComponentFromProp(this, 'label')
      const id = this.id || this.getId()
      if (!id) {
        return
      }
      const 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()
        }
        const control = this.$el.querySelector(`[id="${id}"]`)
        if (control && control.focus) {
          control.focus()
        }
      }
    },

    renderLabel () {
      const { prefixCls, labelCol = {}, colon, id } = this
      const label = getComponentFromProp(this, 'label')
      const required = this.isRequired()
      const { class: labelColClass, style: labelColStyle, id: labelColId, on, ...restProps } = labelCol
      const labelColClassName = classNames(
        `${prefixCls}-item-label`,
        labelColClass,
      )
      const labelClassName = classNames({
        [`${prefixCls}-item-required`]: required,
      })

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

      return label ? (
        <Col {...colProps} >
          <label
            for={id || this.getId()}
            class={labelClassName}
            title={typeof label === 'string' ? label : ''}
            onClick={this.onLabelClick}
          >
            {labelChildren}
          </label>
        </Col>
      ) : null
    },
    renderChildren () {
      const { $slots } = this
      return [
        this.renderLabel(),
        this.renderWrapper(
          this.renderValidateWrapper(
            filterEmpty($slots.default || []),
            this.renderHelp(),
            this.renderExtra(),
          ),
        ),
      ]
    },
    renderFormItem (children) {
      const props = this.$props
      const prefixCls = props.prefixCls
      const itemClassName = {
        [`${prefixCls}-item`]: true,
        [`${prefixCls}-item-with-help`]: !!this.getHelpMsg() || this.helpShow,
        [`${prefixCls}-item-no-colon`]: !props.colon,
      }

      return (
        <Row class={classNames(itemClassName)}>
          {children}
        </Row>
      )
    },
  },

  render () {
    const children = this.renderChildren()
    return this.renderFormItem(children)
  },
}