<script lang="tsx">
  import { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
  import type { PropType, Ref, ComputedRef } from 'vue';
  import type { FormActionType, FormProps } from '../types/form';
  import type { FormSchema } from '../types/form';
  import type { ValidationRule } from 'ant-design-vue/lib/form/Form';
  import type { TableActionType } from '/@/components/Table';
  import { defineComponent, computed, unref, toRefs, ref, watchEffect } from 'vue';
  import { Form, Col, Divider } from 'ant-design-vue';
  import { componentMap } from '../componentMap';
  import { BasicHelp } from '/@/components/Basic';
  import { isBoolean, isFunction, isNull } from '/@/utils/is';
  import { getSlot } from '/@/utils/helper/tsxHelper';
  import { createPlaceholderMessage, setComponentRuleType } from '../helper';
  import { upperFirst, cloneDeep, debounce } from 'lodash-es';
  import { useItemLabelWidth } from '../hooks/useLabelWidth';
  import { useI18n } from '/@/hooks/web/useI18n';
  import { useAppInject } from '/@/hooks/web/useAppInject';
  import { usePermission } from '/@/hooks/web/usePermission';
  import Middleware from './Middleware.vue';
  import { useLocaleStoreWithOut } from '/@/store/modules/locale';

  // 提取类型定义
  interface FormItemContext {
    schema: Ref<FormSchema>;
    formProps: Ref<FormProps>;
    getValues: ComputedRef<Recordable>;
    getComponentsProps: ComputedRef<Recordable>;
    getDisable: ComputedRef<boolean>;
    getDynamicPropsValue: ComputedRef<any>;
  }

  // 提取常量
  const VALIDATOR_DEBOUNCE_TIME = 100;
  const COMPONENT_EVENTS = {
    CHANGE: 'change',
    BLUR: 'blur'
  };

  export default defineComponent({
    name: 'BasicFormItem',
    inheritAttrs: false,
    props: {
      schema: {
        type: Object as PropType<FormSchema>,
        default: () => ({}),
      },
      formProps: {
        type: Object as PropType<FormProps>,
        default: () => ({}),
      },
      allDefaultValues: {
        type: Object as PropType<Recordable>,
        default: () => ({}),
      },
      formModel: {
        type: Object as PropType<Recordable>,
        default: () => ({}),
      },
      setFormModel: {
        type: Function as PropType<(key: string, value: any) => void>,
        default: null,
      },
      validateFields: {
        type: Function as PropType<(nameList?: NamePath[] | undefined, options?: ValidateOptions) => Promise<any>>,
        default: null,
      },
      tableAction: {
        type: Object as PropType<TableActionType>,
      },
      formActionType: {
        type: Object as PropType<FormActionType>,
      },
      clearValidate: {
        type: Function,
        default: null,
      },
      formName: {
        type: String,
        default: '',
      },
    },
    setup(props, { slots }) {
      const { t } = useI18n();
      const localeStore = useLocaleStoreWithOut();
      const { schema, formProps } = toRefs(props) as {
        schema: Ref<FormSchema>;
        formProps: Ref<FormProps>;
      };

      const itemLabelWidthProp = useItemLabelWidth(schema, formProps);
      const { hasPermission } = usePermission();
      const { getIsMobile } = useAppInject();

      // 使用 ref 缓存计算结果，避免重复计算
      const vSwitchArr = ref<any[]>([]);
      const prevValidatorArr = ref<any[]>([]);

      // 计算属性优化
      const getValues = computed(() => {
        const { allDefaultValues, formModel, schema } = props;
        const { mergeDynamicData } = props.formProps;
        
        return {
          field: schema.field,
          model: formModel,
          values: {
            ...mergeDynamicData,
            ...allDefaultValues,
            ...formModel,
          } as Recordable,
          schema: schema,
        };
      });

      const getComponentsProps = computed(() => {
        const { schema, tableAction, formModel, formActionType } = props;
        let { componentProps = {} } = schema;
        
        if (isFunction(componentProps)) {
          componentProps = componentProps({ schema, tableAction, formModel, formActionType }) ?? {};
        }
        
        if (schema.component === 'Divider') {
          componentProps = Object.assign({ 
            type: 'horizontal',
            orientation: 'left', 
            plain: true 
          }, componentProps);
        }
        
        return componentProps as Recordable;
      });

      const getDisable = computed(() => {
        const { disabled: globDisabled } = props.formProps;
        
        if (globDisabled) {
          return globDisabled;
        }
        
        const { dynamicDisabled } = props.schema;
        const { disabled: itemDisabled = false } = unref(getComponentsProps);
        let disabled = !!globDisabled || itemDisabled;
        
        if (isBoolean(dynamicDisabled)) {
          disabled = dynamicDisabled;
        }
        
        if (isFunction(dynamicDisabled)) {
          disabled = dynamicDisabled(unref(getValues));
        }
        
        return disabled;
      });

      const getDynamicPropsValue = computed(() => {
        const { dynamicPropsVal, dynamicPropskey } = props.schema;
        
        if (dynamicPropskey == null) {
          return null;
        }
        
        const { [dynamicPropskey]: itemValue } = unref(getComponentsProps);
        
        if (isFunction(dynamicPropsVal)) {
          return dynamicPropsVal(unref(getValues));
        }
        
        return itemValue;
      });

      // 提取工具函数
      const getShowState = () => {
        const { show, ifShow } = props.schema;
        const { showAdvancedButton } = props.formProps;
        const itemIsAdvanced = showAdvancedButton 
          ? (isBoolean(props.schema.isAdvanced) ? props.schema.isAdvanced : true) 
          : true;

        let isShow = true;
        let isIfShow = true;

        if (isBoolean(show)) {
          isShow = show;
        }
        if (isBoolean(ifShow)) {
          isIfShow = ifShow;
        }
        if (isFunction(show)) {
          isShow = show(unref(getValues));
        }
        if (isFunction(ifShow)) {
          isIfShow = ifShow(unref(getValues));
        }
        
        isShow = isShow && itemIsAdvanced;
        return { isShow, isIfShow };
      };

      // 防抖验证器优化
      const hijackValidator = (rules: ValidationRule[]) => {
        vSwitchArr.value = [];
        prevValidatorArr.value = [];
 
        rules?.forEach((item, index) => {
          const fn = item.validator;
          vSwitchArr.value[index] = true;
          prevValidatorArr.value[index] = null;
          
          if (isFunction(fn)) {
            const debouncedValidator = debounce((rule, value, callback) => {
              const result = fn(rule, value, callback);
              prevValidatorArr.value[index] = result;
              return result;
            }, VALIDATOR_DEBOUNCE_TIME);

            item.validator = (rule, value, callback) => {
              if (vSwitchArr.value[index]) {
                vSwitchArr.value[index] = false;
                return debouncedValidator(rule, value, callback);
              } else {
                return prevValidatorArr.value[index];
              }
            };
          }
        });
      };

      // 规则处理优化
      const handleRules = (): ValidationRule[] => {
        const { 
          rules: defRules = [], 
          component, 
          rulesMessageJoinLabel, 
          label, 
          dynamicRules, 
          required, 
          auth, 
          field 
        } = props.schema;

        // 禁用状态下清除验证
        const { disabled: globDisabled } = props.formProps;
        const { disabled: itemDisabled = false } = unref(getComponentsProps);
        if (globDisabled || itemDisabled) {
          props.clearValidate?.(field);
          return [];
        }

        // 权限和显示状态检查
        const { isShow } = getShowState();
        if ((auth && !hasPermission(auth)) || !isShow) {
          return [];
        }

        // 动态规则处理
        if (isFunction(dynamicRules)) {
          const ruleArr = dynamicRules(unref(getValues)) as ValidationRule[];
          if (required) {
            ruleArr.unshift({ required: true });
          }
          hijackValidator(ruleArr);
          return ruleArr;
        }

        let rules: ValidationRule[] = cloneDeep(defRules) as ValidationRule[];
        const { rulesMessageJoinLabel: globalRulesMessageJoinLabel } = props.formProps;
        const joinLabel = Reflect.has(props.schema, 'rulesMessageJoinLabel') 
          ? rulesMessageJoinLabel 
          : globalRulesMessageJoinLabel;
        
        const defaultMsg = createPlaceholderMessage(component) + `${joinLabel ? label : ''}`;

        // 验证器函数
        const validator = (rule: any, value: any) => {
          const msg = rule.message || defaultMsg;
          
          if (value === undefined || isNull(value)) {
            return Promise.reject(msg);
          } else if (Array.isArray(value) && value.length === 0) {
            return Promise.reject(msg);
          } else if (typeof value === 'string' && value.trim() === '') {
            return Promise.reject(msg);
          } else if (
            typeof value === 'object' &&
            Reflect.has(value, 'checked') &&
            Reflect.has(value, 'halfChecked') &&
            Array.isArray(value.checked) &&
            Array.isArray(value.halfChecked) &&
            value.checked.length === 0 &&
            value.halfChecked.length === 0
          ) {
            return Promise.reject(msg);
          }
          
          return Promise.resolve();
        };

        const getRequired = isFunction(required) ? required(unref(getValues)) : required;

        if ((!rules || rules.length === 0) && getRequired) {
          rules = [{ required: getRequired, validator }];
        }

        const requiredRuleIndex = rules.findIndex(
          (rule) => Reflect.has(rule, 'required') && !Reflect.has(rule, 'validator')
        );

        if (requiredRuleIndex !== -1) {
          const rule = rules[requiredRuleIndex];
          const { isShow } = getShowState();
          
          if (!isShow) {
            rule.required = false;
          }
          
          if (component) {
            rule.message = rule.message || defaultMsg;

            if (component.includes('Input') || component.includes('Textarea')) {
              rule.whitespace = true;
            }
            
            const valueFormat = unref(getComponentsProps)?.valueFormat;
            setComponentRuleType(rule, component, valueFormat);
          }
        }

        // 最大长度规则检查
        const characterInx = rules.findIndex((val) => val.max);
        if (characterInx !== -1 && !rules[characterInx].validator) {
          rules[characterInx].message = rules[characterInx].message 
            || t('component.form.maxTip', [rules[characterInx].max] as Recordable);
        }

        // 正则表达式模式处理
        rules.forEach((item) => {
          if (typeof item.pattern === 'string') {
            try {
              const reg = new Function('item', `return ${item.pattern}`)(item);
              item.pattern = Object.prototype.toString.call(reg) === '[object RegExp]' 
                ? reg 
                : new RegExp(item.pattern);
            } catch (error) {
              item.pattern = new RegExp(item.pattern);
            }
          }
        });

        hijackValidator(rules);
        return rules;
      };

      // 组件渲染优化
      const renderComponent = () => {
        const { 
          renderComponentContent, 
          component, 
          field, 
          changeEvent = COMPONENT_EVENTS.CHANGE, 
          valueField, 
          componentProps, 
          dynamicRules, 
          rules: defRules = [] 
        } = props.schema;

        const isCheck = component && ['Switch', 'Checkbox'].includes(component);
        let isTrim = false;
        
        if (component === 'Input' && componentProps && componentProps.trim) {
          isTrim = true;
        }

        const eventKey = `on${upperFirst(changeEvent)}`;
        
        const getRules = (): ValidationRule[] => {
          const dyRules = isFunction(dynamicRules) ? dynamicRules(unref(getValues)) : [];
          return [...dyRules, ...defRules];
        };

        const on = {
          [eventKey]: (...args: Nullable<Recordable>[]) => {
            const [e] = args;
            const propsData = unref(getComponentsProps);
            
            if (propsData[eventKey]) {
              propsData[eventKey](...args);
            }
            
            const target = e ? e.target : null;
            let value;
            
            if (target) {
              value = isCheck ? target.checked : (isTrim ? target.value.trim() : target.value);
            } else {
              value = e;
            }
            
            props.setFormModel(field, value);
            
            const findItem = getRules().find((item) => item?.trigger === COMPONENT_EVENTS.BLUR);
            if (!findItem) {
              props.validateFields([field]).catch((_) => {});
            }
          },
        };

        const Comp = componentMap.get(component) as ReturnType<typeof defineComponent>;
        if (!Comp) return null;

        const { autoSetPlaceHolder, size } = props.formProps;
        const propsData: Recordable = {
          allowClear: true,
          getPopupContainer: (trigger: Element) => trigger?.parentNode,
          size,
          ...unref(getComponentsProps),
          disabled: unref(getDisable),
        };

        const dynamicPropskey = props.schema.dynamicPropskey;
        if (dynamicPropskey) {
          propsData[dynamicPropskey] = unref(getDynamicPropsValue);
        }

        const isCreatePlaceholder = !!autoSetPlaceHolder;
        if (isCreatePlaceholder && component !== 'RangePicker' && component) {
          let label = isFunction(props.schema.label) 
            ? props.schema.label() 
            : props.schema.label;
          
          if (localeStore.getLocale === 'en' && !(/^\s/.test(label))) {
            label = ' ' + label;
          }
          
          propsData.placeholder = unref(getComponentsProps)?.placeholder 
            || createPlaceholderMessage(component) + label;
        }
        
        propsData.codeField = field;
        propsData.formValues = unref(getValues);

        const bindValue: Recordable = {
          [valueField || (isCheck ? 'checked' : 'value')]: props.formModel[field],
        };

        const compAttr: Recordable = {
          ...propsData,
          ...on,
          ...bindValue,
        };

        if (!renderComponentContent) {
          return <Comp {...compAttr} />;
        }
        
        const compSlot = isFunction(renderComponentContent)
          ? { ...renderComponentContent(unref(getValues)) }
          : { default: () => renderComponentContent };
          
        return <Comp {...compAttr}>{compSlot}</Comp>;
      };

      // 标签渲染优化
      const renderLabelHelpMessage = () => {
        const { 
          label: itemLabel, 
          helpMessage, 
          helpComponentProps, 
          subLabel, 
          labelLength 
        } = props.schema;
        
        const label = isFunction(itemLabel) ? itemLabel() : itemLabel;
        let showLabel: string = label + '';
        
        if (labelLength) {
          showLabel = showLabel.substr(0, labelLength);
        }
        
        const titleObj = { title: label };
        const renderLabel = subLabel ? (
          <span>
            {label} <span class="text-secondary">{subLabel}</span>
          </span>
        ) : labelLength ? (
          <label {...titleObj}>{showLabel}</label>
        ) : (
          label
        );

        const getHelpMessage = isFunction(helpMessage) 
          ? helpMessage(unref(getValues)) 
          : helpMessage;
        
        if (!getHelpMessage || (Array.isArray(getHelpMessage) && getHelpMessage.length === 0)) {
          return renderLabel;
        }
        
        return (
          <span>
            {renderLabel}
            <BasicHelp 
              placement="top" 
              class="mx-1" 
              text={getHelpMessage} 
              {...helpComponentProps} 
            />
          </span>
        );
      };

      // 表单项渲染优化
      const renderItem = () => {
        const { itemProps, slot, render, field, suffix, component } = props.schema;
        const { labelCol, wrapperCol } = unref(itemLabelWidthProp);
        const { colon } = props.formProps;

        if (component === 'Divider') {
          return (
            <Col span={24}>
              <Divider {...unref(getComponentsProps)}>
                {renderLabelHelpMessage()}
              </Divider>
            </Col>
          );
        }

        const getContent = () => {
          return slot 
            ? getSlot(slots, slot, unref(getValues)) 
            : render 
              ? render(unref(getValues)) 
              : renderComponent();
        };

        const showSuffix = !!suffix;
        const getSuffix = isFunction(suffix) ? suffix(unref(getValues)) : suffix;

        return (
          <Form.Item
            name={field}
            colon={colon}
            class={{ 'suffix-item': showSuffix }}
            {...(itemProps as Recordable)}
            label={renderLabelHelpMessage()}
            rules={handleRules()}
            validateFirst={true}
            labelCol={labelCol}
            wrapperCol={wrapperCol}
          >
            <div style="display:flex">
              <Middleware formName={props.formName} fieldName={field}>
                {getContent()}
              </Middleware>
              {showSuffix && <span class="suffix">{getSuffix}</span>}
            </div>
          </Form.Item>
        );
      };

      // 主渲染函数
      return () => {
        const { colProps = {}, colSlot, renderColContent, component } = props.schema;
        
        if (!componentMap.has(component)) {
          return null;
        }

        const { baseColProps = {} } = props.formProps;
        let realColProps = { ...baseColProps, ...colProps };
        
        if (colProps['span'] && !unref(getIsMobile)) {
          ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'].forEach((name) => delete realColProps[name]);
        }

        const { isIfShow, isShow } = getShowState();
        const values = unref(getValues);

        const getContent = () => {
          return colSlot 
            ? getSlot(slots, colSlot, values) 
            : renderColContent 
              ? renderColContent(values) 
              : renderItem();
        };

        return (
          isIfShow && (
            <Col {...realColProps} v-show={isShow}>
              {getContent()}
            </Col>
          )
        );
      };
    },
  });
</script>