<!-- @format -->

<template>
  <a-col
    :span="span"
    class="ns-form-item"
    :class="getFormItemClass"
    v-if="getIfShow"
    v-show="getShow">
    <a-form-item v-if="getFormItemDisplay" v-bind="formItemProps">
      <template #[item]="data" v-for="item in Object.keys(getSlots)">
        <slot :name="schema.field + '_' + item" v-bind="data || {}"></slot>
      </template>
      <component :is="schema.component" v-bind="formProps">
        <template #[item]="data" v-for="item in Object.keys($slots)">
          <slot :name="item" v-bind="data || {}" :field="schema.field"></slot>
        </template>
      </component>
      <!--      <div class="ns-tips">-->
      <!--        <slot :name="`${schema.field}_tips`"></slot>-->
      <!--      </div>-->
    </a-form-item>
    <template v-else>
      <component :is="schema.component" v-bind="formProps">
        <template #[item]="data" v-for="item in Object.keys($slots)">
          <slot :name="item" v-bind="data || {}"></slot>
        </template>
      </component>
      <!--      <div class="ns-tips">-->
      <!--        <slot :name="`${schema.field}_tips`"></slot>-->
      <!--      </div>-->
    </template>
  </a-col>
</template>

<script lang="ts">
  import type { PropType } from 'vue';
  import { computed, defineComponent, inject, nextTick, provide, ref, unref } from 'vue';
  import {
    forEach,
    isArray,
    isBoolean,
    isFunction,
    isObject,
    isString,
    isUndefined,
    mapKeys,
    upperFirst,
    get,
    cloneDeep,
  } from 'lodash-es';
  import { isInputType } from '/nerv-lib/component/form/form-util';
  import { useParams } from '/nerv-lib/use/use-params';

  enum prefix {
    '请选择',
    '请输入',
  }

  export default defineComponent({
    name: 'NsFormItem',
    components: {},
    props: {
      span: Number,
      schema: {
        type: Object as PropType<FormSchema>,
        default: () => ({}),
      },
      show: {
        type: Boolean,
        default: true,
      },
      formModel: {
        type: Object as PropType<Recordable>,
        default: () => ({}),
      },
      index: {
        type: Number,
        default: 0,
      },
    },
    setup(props, { slots }) {
      const components = inject('components', () => ({}))();
      const setFormModel = inject('setFormModel') as Function;
      const getFormModel = inject('getFormModel') as Function;
      const submit = inject('submit') as Function;
      const validateRef = ref({});
      const formLayout = inject('formLayout');

      const { getParams } = useParams();

      const getSlots = computed(() => {
        const { schema } = props;
        return mapKeys(slots, (_val, key) => {
          return schema.field ? key.replace(schema.field + '_', '') : key;
        });
      });

      provide(
        'rules',
        computed(() => {
          return props.schema?.rules || [];
        }),
      );

      const formItemProps = computed(() => {
        const {
          schema: { field, rules, label, component, autoLink, formItemProps, extra, style },
        } = props;
        const tableComponent = ['nsTable'];
        let nsClass = '';
        component && tableComponent.includes(component) && (nsClass = 'ns-form-item-validate-self');
        return {
          class: nsClass,
          name: field,
          rules: rules || [],
          label: label,
          autoLink: autoLink || true,
          extra: extra,
          style,
          ...formItemProps,
          ...validateRef.value,
        };
      });

      const getFormItemDisplay = computed(() => {
        const { schema } = props;
        return schema.displayFormItem !== false;
      });

      const getFormItemClass = computed(() => {
        const { schema } = props;
        return schema.class;
      });

      const getDisabled = computed(() => {
        const { dynamicDisabled } = props.schema;
        if (dynamicDisabled) {
          if (isBoolean(dynamicDisabled)) {
            return { disabled: dynamicDisabled };
          }
          if (isFunction(dynamicDisabled)) {
            return { disabled: dynamicDisabled(props.formModel) };
          }
          return {};
        }
        return {};
      });

      const getIfShow = computed(() => {
        const { ifShow } = props.schema;
        let isIfShow = true;
        if (isBoolean(ifShow)) {
          isIfShow = ifShow;
        }
        if (isFunction(ifShow)) {
          isIfShow = ifShow(props.formModel);
        }
        // 不显示时候删除属性
        if (!isIfShow) {
          setFormModel(props.schema.field, null);
        }

        return isIfShow && props.show;
      });

      const getShow = computed(() => {
        const { show } = props.schema;
        let isShow = true;
        if (isBoolean(show)) {
          isShow = show;
        }
        if (isFunction(show)) {
          isShow = show(props.formModel);
        }
        return isShow;
      });
      // console.log(props.schema?.field, getShow.value, props.schema);

      const getDynamicParams = computed(() => {
        const { dynamicParams, defaultParams, componentProps = {} } = props.schema;
        const { params = {} } = componentProps;
        if (dynamicParams) {
          const data = getParams(props.formModel, dynamicParams, { ...params, ...defaultParams });
          // console.log('getParams', data);
          return data;
        } else {
          return { ...params, ...defaultParams };
        }
      });

      /**
       * 检测requiredParams是否全部获得数据
       * @param params
       */
      function checkRequiredParams(params: Recordable) {
        const { dynamicParams } = props.schema;
        let { requiredParams } = props.schema;
        if (requiredParams) {
          if (requiredParams === true) requiredParams = dynamicParams as any;
          if (isFunction(requiredParams)) {
            console.error(
              'Property dynamicParams of props cannot set to Function when using requiredParams',
            );
            return false;
          } else {
            if (isString(requiredParams)) {
              if (isUndefined(params[requiredParams])) return false;
            } else if (isArray(requiredParams)) {
              for (let i = 0, l = requiredParams.length; i < l; i++) {
                if (isUndefined(params[requiredParams[i]])) return false;
              }
            } else if (isObject(requiredParams)) {
              const keys = Object.keys(requiredParams);
              for (let i = 0, l = keys.length; i < l; i++) {
                if (isUndefined(params[keys[i]])) return false;
              }
            }
          }
          return true;
        }
        return true;
      }

      const formProps = computed(() => {
        const { formModel } = props;
        const {
          component,
          field,
          label,
          dynamicParams,
          changeEvent = 'change',
          valueField,
          addModel = [],
          autoAddLink = false,
          autoSubmit = false,
          componentProps,
        } = props.schema;
        const isCheck =
          component && ['NsSwitch', 'NsCheckbox', 'Switch', 'Checkbox'].includes(component);
        const eventKey = `on${upperFirst(changeEvent)}`;
        const attr: Recordable = {};
        const isInput = isInputType(component);
        if (isInput) {
          attr.allowClear = true;
        }

        // const { placeholder } = componentProps;
        // // 赋予初始提示符
        // componentProps['placeholder'] = placeholder || `${prefix[Number(isInput)]}${label}`;

        const propsData: Recordable = {
          field,
          dynamicParams,
          ...attr,
          size: 'default',
          ...unref(getComponentsProps),
          ...unref(getDisabled),
        };

        // 如果有值
        if (getDynamicParams.value) {
          if (props.schema?.requiredParams) {
            if (checkRequiredParams(getDynamicParams.value)) {
              propsData.params = getDynamicParams.value;
              propsData.checkRequiredParams = true;
            } else {
              propsData.checkRequiredParams = false;
            }
          } else {
            propsData.params = getDynamicParams.value;
          }
        }

        const bindValue: Recordable = {
          [valueField || (isCheck ? 'checked' : 'value')]: props.formModel[field],
        };
        const on = {
          [eventKey]: (...args: Nullable<Recordable>[]) => {
            const [e] = args;
            if (propsData[eventKey]) {
              (propsData[eventKey] as Function)(...args);
            }
            const target = e ? e.target : null;
            const value = target ? (isCheck ? target.checked : target.value) : e;

            setFormModel(field, value);

            if (isArray(addModel)) {
              addModel.forEach((item) => {
                setFormModel(item, args[1] && args[1][item]);
              });
            } else {
              forEach(addModel as any, (value, key) => {
                setFormModel(key, args[1] && get(args[1], value));
              });
            }

            if (autoAddLink) {
              const fieldLink = getFormModel('fieldLink') || {};
              fieldLink[field] = args[1];
              setFormModel('fieldLink', fieldLink);
            }

            autoSubmit && nextTick(submit as any);
          },
          onValidateChange: (text: Object | undefined) => {
            if (isUndefined(text)) text = {};
            validateRef.value = text;
          },
        };

        return {
          ...propsData,
          ...on,
          ...bindValue,
          formModel,
        };
      });

      const getComponentsProps = computed(() => {
        const { componentProps = {} } = unref(props).schema;
        return componentProps;
      });

      return {
        formItemProps,
        formProps,
        getShow,
        getIfShow,
        components,
        getFormItemDisplay,
        getFormItemClass,
        getSlots,
        formLayout,
        cloneDeep,
      };
    },
    beforeCreate() {
      this.$options.components = this.components;
    },
  });
</script>
<style lang="less" scoped>
  .ns-tips {
    height: auto;
    line-height: 32px;
    width: auto;
    display: inline-block;
  }
</style>
