<template>
  <div>
    <Form
      ref="formRef"
      :model="formModel"
      :layout="getProps?.layout"
      :label-col="getProps?.labelCol"
      :wrapper-col="getProps?.wrapperCol"
      :labelAlign="getProps?.labelAlign"
      @keypress.enter="handleEnterPress"
    >
      <Row v-bind="getRow" class="questionnaire-form">
        <template v-for="schema in getSchemas" :key="schema.field">
          <Col :span="schema.colProps?.span">
            <FormItem
              :key="schema.key"
              :name="schema.field"
              :rules="rules(schema)"
              :validateTrigger="['blur', 'change']"
            >
              <template #label>
                <div class="label-box" v-if="schema.type !== 'title'">
                  <span v-show="(schema.componentProps as any)?.required" style="color: #ff4d4f">
                    *
                  </span>
                  <span class="mr-1.5">{{ `${getIndex(schema.key)}.` }} </span>
                  <a-tag v-if="getLabel(schema.type)" :color="getLabel(schema.type)?.color">{{
                    getLabel(schema.type)?.text
                  }}</a-tag>
                  <span>{{ schema.label }}</span>
                </div>
              </template>
              <component
                :is="defaultComponent(schema)"
                :disabled="getDisable(schema)"
                :isView="getIsView(schema)"
                :componentKey="schema.key"
                v-bind="getComponentsProps(schema)"
                v-model:value="formModel![schema.field!]"
              />
            </FormItem>
          </Col>
        </template>
      </Row>

      <div :style="{ textAlign: getProps.buttonLocation }">
        <slot name="buttonBefore"></slot>

        <a-button type="primary" v-if="getProps.showSubmitButton" @click="handleSubmit" class="btn">
          {{ t('提交') }}
        </a-button>
        <a-button class="btn reset-btn" v-if="getProps.showResetButton" @click="handleReset">
          {{ t('重置') }}
        </a-button>
        <slot name="buttonAfter"></slot>
      </div>
    </Form>
  </div>
</template>
<script lang="ts" setup>
  import { Form, FormInstance, Row, Col } from 'ant-design-vue';
  import { isBoolean, isFunction, isNil, cloneDeep } from 'lodash-es';
  import { computed, reactive, ref, provide, unref, nextTick, toRaw } from 'vue';
  import { FormProps, FormSchema } from '/@/components/Form/src/types/form';
  import RadioGroup from './components/RadioGroup.vue';
  import CheckboxGroup from './components/CheckboxGroup.vue';

  import { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
  import {
    defaultValueComponents,
    staticDataComponents,
    noDefaultValueComponents,
  } from '/@/components/Form/src/helper';
  import { deepMerge } from '/@/utils';
  import { componentMap } from '/@/components/Form/src/componentMap';

  import { useI18n } from '/@/hooks/web/useI18n';
  import { needDicDefaultValue } from '/@/components/Designer/src/types';
  const FormItem = Form.Item;

  const { t } = useI18n();
  const formRef = ref<FormInstance>();

  const propsRef = ref<Partial<FormProps>>({});

  const schemaRef = ref<Nullable<FormSchema[]>>(null);

  const emit = defineEmits(['submit']);

  const props = defineProps({
    // 表单配置规则
    formProps: {
      type: Object as PropType<FormProps>,
    },
    //表单数据
    formModel: {
      type: Object as PropType<Recordable>,
      default: () => {},
    },
  });

  const formModel = reactive<Recordable>(props.formModel);

  const getSchemas = computed<FormSchema[]>(() => {
    return (unref(getProps).schemas as any) || unref(schemaRef);
  });

  // Get the basic configuration of the form
  const getProps = computed((): FormProps => {
    return { ...(props.formProps as FormProps), ...unref(propsRef) } as FormProps;
  });

  // Get uniform row style and Row configuration for the entire form
  const getRow = computed((): Recordable => {
    const { baseRowStyle = {}, rowProps } = props.formProps!;

    return {
      style: baseRowStyle,
      ...rowProps,
    };
  });

  const getComponentsProps = (schema) => {
    let { componentProps = {} } = schema;

    if (isFunction(componentProps)) {
      componentProps = componentProps({ schema: schema, formModel, formActionType: formApi }) ?? {};
    }
    if (isBoolean(schema.dynamicDisabled)) {
      componentProps['disabled'] = schema.dynamicDisabled;
    }
    if (isBoolean(schema.required)) {
      componentProps['required'] = schema.required;
    }
    return componentProps as Recordable;
  };

  const rules = (schema) => {
    const requiredRule = {
      required: getComponentsProps(schema).required || false,
      message: `${schema.label}是必填项`,
    };
    const rulesList = cloneDeep(getComponentsProps(schema).rules);
    if (!rulesList) return [requiredRule];
    rulesList?.map((item) => (item.pattern = eval(item.pattern)));
    return [...rulesList, requiredRule];
  };

  const getDisable = (schema) => {
    const { disabled: globDisabled } = unref(getProps);
    const { dynamicDisabled } = schema;
    const { disabled: itemDisabled = false } = getComponentsProps(schema);
    let disabled = !!globDisabled || itemDisabled;
    if (isBoolean(dynamicDisabled)) {
      disabled = dynamicDisabled;
    }
    if (isFunction(dynamicDisabled)) {
      disabled = dynamicDisabled({
        values: formModel![schema.field],
        model: formModel!,
        schema: schema,
        field: schema.field,
      });
    }
    return disabled;
  };
  const getIsView = (schema) => {
    const { disabled: globDisabled } = unref(getProps);
    const { dynamicDisabled } = schema;

    let disabled = !!globDisabled;
    if (isBoolean(dynamicDisabled)) {
      disabled = dynamicDisabled;
    }
    if (isFunction(dynamicDisabled)) {
      disabled = dynamicDisabled({
        values: formModel![schema.field],
        model: formModel!,
        schema: unref(schema),
        field: unref(schema).field,
      });
    }
    return disabled;
  };

  const getLabel = (type) => {
    switch (type) {
      case 'radio':
        return { color: 'blue', text: '单选题' };
      case 'checkbox':
        return { color: 'cyan', text: '多选题' };
    }
    return false;
  };

  const getIndex = (key) => {
    const schemas = getSchemas.value.filter((x) => x.type !== 'title');
    const index = schemas.findIndex((x) => x.key === key);
    return index + 1;
  };

  const defaultComponent = (schema) => {
    switch (schema.type) {
      case 'radio':
        return RadioGroup;
      case 'checkbox':
        return CheckboxGroup;
      default:
        return componentMap.get(schema.component);
    }
  };

  provide('formModel', formModel);
  provide('formProps', getProps);

  const handleSubmit = async () => {
    try {
      const { submitFunc } = unref(getProps);
      if (submitFunc && isFunction(submitFunc)) {
        await submitFunc();
        return;
      }
      const res = await formRef.value!.validate();
      emit('submit', res);
    } catch (error) {}
  };

  const handleReset = () => {
    resetFields();
  };

  getComponent(getSchemas.value);

  function getComponent(component) {
    component?.map((info) => {
      setComponentDefault(info);
    });
  }

  function setComponentDefault(item) {
    if (
      (staticDataComponents.includes(item.component) &&
        (item.componentProps as any)?.datasourceType === 'staticData') ||
      (needDicDefaultValue.includes(item.type) &&
        (item.componentProps as any)?.datasourceType === 'dic')
    ) {
      let { defaultSelect } = item.componentProps as any;
      formModel[item.field] = defaultSelect;
      return;
    }
    let { defaultValue } = item;
    if (!!item.field && !noDefaultValueComponents.includes(item.component)) {
      formModel[item.field] = defaultValue;
      return;
    }
  }

  /**
   * 回车提交表单
   */
  function handleEnterPress(e: KeyboardEvent) {
    const { autoSubmitOnEnter } = unref(getProps);
    if (!autoSubmitOnEnter) return;
    if (e.key === 'Enter' && e.target && e.target instanceof HTMLElement) {
      const target: HTMLElement = e.target as HTMLElement;
      if (target && target.tagName && target.tagName.toUpperCase() == 'INPUT') {
        handleSubmit();
      }
    }
  }

  //调用表单验证
  const validate = async (nameList?: NamePath[]): Promise<any> => formRef.value?.validate(nameList);

  //清除表单验证
  const clearValidate = async (name?: string | string[]): Promise<any> =>
    formRef.value?.clearValidate(name);

  //跳到某个字段
  const scrollToField = async (name: NamePath, options?: ScrollOptions): Promise<any> =>
    formRef.value?.scrollToField(name, options);

  //验证某个字段
  const validateFields = async (
    nameList?: NamePath[] | string,
    options?: ValidateOptions,
  ): Promise<any> => formRef.value?.validateFields(nameList, options);

  const findSchema = (schemaArr, key) => {
    let schema;
    schemaArr?.some((info) => {
      schema = info.field === key ? info : null;
      return !!schema;
    });
    return schema;
  };

  const setDefaultValue = async (): Promise<void> => {
    getComponent(getSchemas.value);
  };

  // 重置表单所有数据
  const resetFields = async (): Promise<void> => {
    Object.keys(formModel).forEach((key) => {
      //没有绑定字段的组件不处理
      if (!key) return;
      const schema = findSchema(unref(getSchemas), key);

      const isInput = schema?.component && defaultValueComponents.includes(schema.component);
      const isStatic =
        schema?.component &&
        staticDataComponents.includes(schema.component) &&
        schema?.componentProps.datasourceType === 'staticData';
      const isDic =
        schema?.type &&
        needDicDefaultValue.includes(schema.type) &&
        schema?.componentProps.datasourceType === 'dic';

      if (isInput) {
        formModel[key] = schema?.defaultValue || '';
      } else if (isStatic || isDic) {
        formModel[key] = schema?.componentProps.defaultSelect;
      } else {
        formModel[key] =
          schema?.defaultValue || schema?.defaultValue === 0 ? schema?.defaultValue : '';
      }
    });
    nextTick(() => clearValidate());
  };

  // 更改formProps
  const setProps = async (formProps: Partial<FormProps>): Promise<void> => {
    propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
  };

  //设定某个字段值  慎用  建议直接页面直接操作formModel数据
  const setFieldsValue = async (values: Recordable): Promise<void> => {
    Object.keys(values).forEach((key) => {
      if (!isNil(key)) {
        formModel[key] = values[key];
      }
    });
  };

  //获取表单值  慎用  建议直接页面直接操作formModel数据
  const getFieldsValue = (): Recordable => {
    return toRaw(unref(formModel));
  };

  // 提交
  const submit = async (e?: Event): Promise<void> => {
    e && e.preventDefault();
    const { submitFunc } = unref(getProps);
    if (submitFunc && isFunction(submitFunc)) {
      await submitFunc();
      return;
    }
    const formEl = unref(formRef);
    if (!formEl) return;
    try {
      const values = await validate();
      emit('submit', values);
    } catch (error: any) {
      throw new Error(error);
    }
  };

  const formApi = {
    submit,
    validate,
    clearValidate,
    scrollToField,
    validateFields,
    resetFields,
    setProps,
    setFieldsValue,
    getFieldsValue,
    setDefaultValue,
  };

  //将表单方法  导出 给父组件使用。
  defineExpose(formApi);
</script>
<style lang="less" scoped>
  .ant-form {
    margin: 15px !important;

    :deep(.ant-form-item-label) {
      max-width: 100%;

      > label {
        width: 100%;

        &::before {
          display: none;
        }
      }
    }
  }

  .label-box {
    margin-bottom: 20px;
    line-height: 50px;
    background: #f8faff;
    font-weight: bold;
    width: 100%;
  }

  .ant-tag-blue {
    border-color: #e6f7ff;
  }

  .ant-tag-cyan {
    border-color: #e6fffb;
  }

  .reset-btn {
    border-color: #f0f0f0;
    background-color: #f0f0f0;
    margin-left: 60px;
    @media (max-width: @screen-sm) {
      margin-top: 10px;
      margin-left: 0;
    }
  }

  .btn {
    width: 200px;
    @media (max-width: @screen-sm) {
      width: 100%;
    }
  }
</style>
