<template>
  <a-col v-bind="schemaItem.colProps">
    <a-form-item
      v-bind="{ ...schemaItem.formItemProps }"
      :label="renderLabelHelpMessage"
      :name="schemaItem.field"
      :labelCol="itemLabelWidthProp.labelCol"
      :wrapperCol="itemLabelWidthProp.wrapperCol"
      :rules="getRules"
    >
      <template v-if="vMode">
        {{ getVModeView(schemaItem) }}
        <slot v-if="schemaItem.slot" :name="schemaItem.slot" v-bind="getValues">
        </slot>
      </template>
      <template v-else>
        <slot v-if="schemaItem.slot" :name="schemaItem.slot" v-bind="getValues">
        </slot>
        <component
          v-else
          :is="getComponent"
          :ref="setItemRef"
          :key="schemaItem.field"
          v-bind="getComponentProps"
          v-on="componentEvents"
          :disabled="getDisable"
          v-model:[modelValueType]="modelValue[schemaItem.field]"
        />
        <span v-if="schemaItem.backLabel" class="schema-item-back-label">{{
          schemaItem.backLabel
        }}</span>
      </template>
    </a-form-item>
  </a-col>
</template>

<script setup lang="tsx">
import { PropType, Ref, toRef } from "vue";
import { computed, unref, toRefs, onMounted } from "vue";
import type { ValidationRule } from "ant-design-vue/lib/form/Form";
import { componentMap, ComponentMapType } from "./componentMap";
import { FormItemSchema, FormSchema } from "./types/form";
import { isBoolean, isFunction, isNull, isString } from "./utils/is";
import { useVModel } from "@vueuse/core";
import { useItemLabelWidth } from "./hooks/useLabelWidth";
import { cloneDeep } from "lodash";
import { createPlaceholderMessage } from "./support";
import BasicHelp from "./components/BasicHelp.vue";
import { AllComponentProps } from "./types";
import { DEFAULT_RANGES } from "./components/TimeRanges";
// import dayjs from "dayjs";
const props = defineProps({
  formModel: {
    type: Object as PropType<Record<string, any>>,
    default: () => ({}),
  },
  schemaItem: {
    type: Object as PropType<FormItemSchema>,
    default: () => ({}),
  },
  schema: {
    type: Object as PropType<FormSchema>,
    default: () => ({}),
  },
  setFormModel: {
    type: Function as PropType<(key: string, value: any) => void>,
    default: null,
  },
  /** 将表单组件实例保存起来 */
  setItemRef: {
    type: Function,
    default: () => {},
  },
  viewMode: {
    type: Boolean,
    default: false,
  },
});

const emit = defineEmits(["update:formModel"]);

// const currentInstance = getCurrentInstance();

const modelValue = useVModel(props, "formModel", emit);
const vMode = toRef(props, "viewMode");
const { schemaItem, schema } = toRefs(props) as {
  schemaItem: Ref<FormItemSchema>;
  schema: Ref<FormSchema>;
};

const itemLabelWidthProp = useItemLabelWidth(schemaItem, schema);
// eslint-disable-next-line
const valuesRef = computed(() => {
  const { formModel, schemaItem } = props;
  return { formModel, schemaItem, field: schemaItem.field };
});

const modelValueType = computed(() => {
  const { component, componentProps } = schemaItem.value;
  if (typeof componentProps !== "function" && componentProps?.vModelKey) {
    return componentProps.vModelKey;
  }
  const isCheck =
    isString(component) && ["Switch", "Checkbox"].includes(component);
  return isCheck ? "checked" : "value";
});

const getValues = computed(() => {
  const { formModel, schemaItem, viewMode } = props;
  const { mergeDynamicData } = props.schema;

  return {
    field: schemaItem.field,
    formModel: formModel,
    values: {
      ...mergeDynamicData,
      ...formModel,
    } as Recordable,
    schemaItem: schemaItem,
    viewMode,
  };
});

/**
 * @description 当前表单项组件
 */
const getComponent = computed(() => {
  const component = props.schemaItem.component;
  return isString(component) ? componentMap[component] : component;
});

/**
 * @description 表单组件props
 */
const getComponentProps = computed(() => {
  const { formModel, schemaItem } = props;
  const { componentProps = {}, component, label = "" } = schemaItem;

  if (!componentProps) {
    return {};
  }
  if (componentProps.hasOwnProperty("mode")) {
    if (!componentProps["mode"]) {
      componentProps["mode"] = "";
    }
  }
  if (isFunction(componentProps)) {
    const compProps = componentProps({
      formModel,
      schemaItem,
    }) as AllComponentProps;

    compProps.placeholder ??= isString(component)
      ? createPlaceholderMessage(component, label)
      : undefined;
    return compProps;
  }
  if (component !== "RangePicker" && isString(component)) {
    (componentProps as AllComponentProps).placeholder ??=
      createPlaceholderMessage(component, label);
  }
  //增加默认的range
  if (component == "RangePicker" && isString(component)) {
    let ranges = componentProps.ranges;
    if (!ranges) {
      componentProps["ranges"] = DEFAULT_RANGES;
    }
  }

  return componentProps as AllComponentProps;
});

/**
 * @description 表单组件事件
 */
const componentEvents = computed(() => {
  const componentProps = props.schemaItem?.componentProps || {};
  return Object.keys(componentProps).reduce((prev, key) => {
    if (/on([A-Z])/.test(key)) {
      // eg: onChange => change
      const eventKey = key.replace(/on([A-Z])/, "$1").toLocaleLowerCase();
      prev[eventKey] = componentProps[key];
    }
    return prev;
  }, {});
});

const getDisable = computed(() => {
  // const { disabled: globDisabled } = unref(formPropsRef);
  const { dynamicDisabled } = props.schemaItem;
  const { disabled: itemDisabled = false } = unref(getComponentProps);
  let disabled = itemDisabled;
  if (isBoolean(dynamicDisabled)) {
    disabled = dynamicDisabled;
  }
  if (isFunction(dynamicDisabled)) {
    disabled = dynamicDisabled(unref(getValues));
  }
  return disabled;
});

const renderLabelHelpMessage = computed(() => {
  const { label, helpMessage, helpComponentProps, subLabel } = props.schemaItem;
  const renderLabel = subLabel ? (
    <span>
      {label} <span class="text-secondary">{subLabel}</span>
    </span>
  ) : (
    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 getComponentsProps = computed(() => {
  const { schemaItem, formModel } = props;
  let { componentProps = {} } = schemaItem;
  if (isFunction(componentProps)) {
    componentProps = componentProps({ schemaItem, formModel }) ?? {};
  }
  return componentProps as Recordable;
});

function setComponentRuleType(
  rule: ValidationRule,
  component: ComponentMapType,
  valueFormat: string
) {
  if (
    ["DatePicker", "MonthPicker", "WeekPicker", "TimePicker"].includes(
      component
    )
  ) {
    rule.type = valueFormat ? "string" : "object";
  } else if (
    ["RangePicker", "Upload", "CheckboxGroup", "TimePicker"].includes(component)
  ) {
    rule.type = "array";
  } else if (["InputNumber"].includes(component)) {
    rule.type = "number";
  }
}

const getRules = computed(() => {
  const {
    rules: defRules = [],
    component,
    rulesMessageJoinLabel,
    label,
    dynamicRules,
    required,
    field,
  } = props.schemaItem;

  if (field == "field5") {
    console.log("rules", props.schemaItem);
  }

  if (isFunction(dynamicRules)) {
    return dynamicRules(unref(getValues)) as ValidationRule[];
  }

  let rules: ValidationRule[] = cloneDeep(defRules) as ValidationRule[];
  const { rulesMessageJoinLabel: globalRulesMessageJoinLabel } = props.schema;

  const joinLabel = Reflect.has(props.schema, "rulesMessageJoinLabel")
    ? rulesMessageJoinLabel
    : globalRulesMessageJoinLabel;
  const defaultMsg = isString(component)
    ? `${createPlaceholderMessage(component, label)}${joinLabel ? label : ""}`
    : undefined;

  function 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
    ) {
      // 非关联选择的tree组件
      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: number = rules.findIndex(
    (rule) => Reflect.has(rule, "required") && !Reflect.has(rule, "validator")
  );

  if (requiredRuleIndex !== -1) {
    const rule = rules[requiredRuleIndex];

    if (component && isString(component)) {
      if (!Reflect.has(rule, "type")) {
        rule.type = component === "InputNumber" ? "number" : "string";
      }

      rule.message = rule.message || defaultMsg;

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

  // Maximum input length rule check
  const characterInx = rules.findIndex((val) => val.max);
  if (characterInx !== -1 && !rules[characterInx].validator) {
    rules[characterInx].message = rules[characterInx].message;
  }

  return rules;
});

//获取查看视图下的字段值
const getVModeView = (schemaItem: FormItemSchema) => {
  const compProps = getComponentProps.value;
  const compName = schemaItem.component;
  if (["Select"].some((n) => n === compName)) {
    return compProps.options?.find(
      (o) => o.value == props.formModel[schemaItem.field]
    )?.label;
  } else {
    return props.formModel[schemaItem.field];
  }
};
// watch(
//   () => props.formModel[props.schemaItem.field],
//   (n, o) => {
//     const { component, componentProps, field } = props.schemaItem;
//     if (component == "RangePicker" && (componentProps as any).valueFormat) {
//       const format = (componentProps as any).valueFormat;
//       if (n) {
//         modelValue.value[field] = [
//           dayjs(n[0]).format(format),
//           dayjs(n[1]).format(format),
//         ];
//       }
//     }
//   }
// );
onMounted(async () => {
  if (getComponentProps.value?.request) {
    const compProps = getComponentProps.value;
    const compName = props.schemaItem.component;
    if (["Select", "RadioGroup", "CheckboxGroup"].some((n) => n === compName)) {
      compProps.options = await getComponentProps.value?.request();
      console.log("compProps.options", compProps.options);
    } else if (["TreeSelect", "Tree"].some((n) => n === compName)) {
      compProps.treeData = await getComponentProps.value?.request();
    }
  }
});
</script>

<style lang="less" scoped>
:deep(.ant-form-item-control-input-content) {
  display: flex;
  align-items: center;
}
.schema-item-back-label {
  margin-left: 10px;
}
</style>
