import { createVNode as _createVNode } from "vue";
import { defineComponent, ref, getCurrentInstance, inject, computed, cloneVNode, onBeforeUnmount } from "vue";
import Schema from 'async-validator'; // import { get, set, cloneDeep, castArray } from 'lodash-es'

import { get, set, cloneDeep, castArray } from '../_util/_';
import { getComponent, flattenChildren, isValidElement } from '../_util/props-util';
import { FormContextKey } from "./interface";
const formItemProps = {
  prefixCls: {
    default: 'x-form-item'
  },
  layout: String,
  label: String,
  labelWidth: Number,
  prop: String,
  rules: [Object, Array],
  required: Boolean,
  first: Boolean,
  extra: String
};
export default defineComponent({
  name: 'x-form-item',
  props: formItemProps,

  setup(props, {
    expose,
    slots
  }) {
    const {
      proxy: vm
    } = getCurrentInstance();
    const formContext = inject(FormContextKey);
    const formProps = (formContext === null || formContext === void 0 ? void 0 : formContext.props) || {};
    const formRulesRef = computed(() => castArray(get(formProps.rules, props.prop) || []));
    const rulesRef = computed(() => {
      const rules = formRulesRef.value.concat(props.rules || []);
      if (props.required) rules.push({
        required: true,
        trigger: 'change',
        type: 'any'
      });
      return rules;
    });
    const valueRef = computed(() => get(formProps.model, props.prop));
    const initialValue = cloneDeep(valueRef.value);
    const labelWidthRef = computed(() => {
      var _ref, _props$labelWidth;

      return (_ref = (_props$labelWidth = props.labelWidth) !== null && _props$labelWidth !== void 0 ? _props$labelWidth : formProps.labelWidth) !== null && _ref !== void 0 ? _ref : undefined;
    });
    const layoutRef = computed(() => props.layout || formProps.layout);
    const requiredRef = computed(() => rulesRef.value.some(rule => rule.required));
    const explainsRef = ref([]);
    const labelNodeRef = computed(() => {
      const layout = layoutRef.value;
      const child = getComponent(vm, 'label');

      if (layout === 'vertical' && !child) {
        return undefined;
      }

      const {
        prefixCls
      } = props;
      const labelProps = {
        class: `${prefixCls}_label`,
        style: {
          width: labelWidthRef.value + 'px'
        }
      };
      return _createVNode("label", labelProps, [getComponent(vm, 'label')]);
    });
    const exposeProps = {
      props,
      valueRef,
      validate,
      resetField,
      clearValidate
    };

    if (formContext) {
      formContext.addItem(exposeProps);
      onBeforeUnmount(() => {
        formContext.delItem(exposeProps);
      });
    }

    function validate(options = {}) {
      const {
        prop,
        first
      } = props;
      if (prop == null) return Promise.resolve();
      if (options.first === undefined) options.first = first;
      const trigger = options.trigger;
      let rules = rulesRef.value;
      if (trigger) rules = rules.filter(rule => {
        var _rule$trigger;

        return (_rule$trigger = rule.trigger) === null || _rule$trigger === void 0 ? void 0 : _rule$trigger.includes(trigger);
      });

      if (!rules.length) {
        return Promise.resolve();
      }

      const validator = new Schema({
        [prop]: rules
      });
      return new Promise((resolve, reject) => {
        validator.validate({
          [prop]: valueRef.value
        }, options, errors => {
          if (errors !== null && errors !== void 0 && errors.length) {
            explainsRef.value = errors.map(e => e.message);
            reject({
              errors
            });
          } else {
            clearValidate();
            resolve({});
          }
        });
      });
    }

    function resetField() {
      set(formProps.model, props.prop, cloneDeep(initialValue));
      clearValidate();
    }

    function clearValidate() {
      explainsRef.value = [];
    }

    expose(exposeProps);

    function onFocus() {
      validate({
        trigger: 'focus'
      });
    }

    function onBlur() {
      validate({
        trigger: 'blur'
      });
    }

    function onChange() {
      clearValidate();
      validate({
        trigger: 'change'
      });
    }

    return () => {
      var _slots$default;

      const {
        prefixCls
      } = props;
      const cls = [prefixCls, `${prefixCls}-${layoutRef.value}`, {
        [`${prefixCls}-error`]: explainsRef.value.length,
        [`${prefixCls}-required`]: requiredRef.value
      }];
      const children = flattenChildren((_slots$default = slots.default) === null || _slots$default === void 0 ? void 0 : _slots$default.call(slots));
      let child = children[0];

      if (isValidElement(child)) {
        child = cloneVNode(child, {
          onFocus,
          onBlur,
          onChange
        });
      }

      return _createVNode("div", {
        "class": cls
      }, [labelNodeRef.value, _createVNode("div", {
        "class": `${prefixCls}_wrapper`
      }, [_createVNode("div", {
        "class": `${prefixCls}_input`
      }, [[child, children.slice(1)]]), _createVNode("div", {
        "class": `${prefixCls}_feedback`
      }, [explainsRef.value.map(msg => _createVNode("div", {
        "class": `${prefixCls}_feedback_line`
      }, [msg])), props.extra ? _createVNode("div", {
        "class": `${prefixCls}_extra`
      }, [getComponent(vm, 'extra')]) : undefined])])]);
    };
  }

});